From c7c340b90f927cbd0499173bafe4678b02093843 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 22 May 2018 11:36:45 -0400
Subject: [PATCH 07/24] Add optinfo, remarks and optimization records

This patch adds a way to create "optinfo" instances, populate
them with messages and data, and to send them to up to three
sinks:
* as diagnostic "remarks"
* via -fopt-info
* as JSON files via -fsave-optimization-record

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
	optinfo-emit-fopt-info.o, optinfo-emit-json.o.
	(GTFILES): Add $(srcdir)/optinfo.h.
	* common.opt (fremarks): New option.
	(fsave-optimization-record): New option.
	* coretypes.h (struct kv_pair): Move here from dumpfile.c.
	* diagnostic-color.c (color_dict): Add "remark", as bold green.
	* diagnostic-core.h (remark): New decl.
	* diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
	(remark): New function.
	* diagnostic.def (DK_REMARK): New diagnostic kind.
	* doc/invoke.texi (Remarks): New section.
	(-fsave-optimization-record): New option.
	(-fremarks): New option.
	* dumpfile.c (struct kv_pair): Move from here to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_loc): Make static; add overload taking no FILE *.
	* dumpfile.h (dump_loc): New decl.
	(optgroup_options): New decl.

gcc/fortran/ChangeLog:
	* gfc-diagnostic.def (DK_REMARK): New diagnostic kind.

gcc/ChangeLog:
	* gengtype.c (open_base_files): Add "optinfo.h".
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo-emit-fopt-info.cc: New file.
	* optinfo-emit-fopt-info.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* opts.c (print_specific_help): Handle CL_REMARK.
	(common_handle_option): Likewise.
	* opts.h (CL_REMARK): New macro.
	(CL_MAX_OPTION_CLASS): Update for CL_REMARK.
	(CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD,
	CL_PCH_IGNORE): Likewise.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
	remarks_plugin.c.
	* gcc.dg/plugin/remarks-1.c: New test.
	* gcc.dg/plugin/remarks_plugin.c: New test plugin.
	* lib/gcc-dg.exp (dg-remark): New function.

gcc/ChangeLog:
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_start and
	optimization_records_finish.
	* 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                              |   5 +
 gcc/common.opt                               |   9 +
 gcc/coretypes.h                              |   8 +
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 ++
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  34 ++-
 gcc/dumpfile.c                               |  24 +-
 gcc/dumpfile.h                               |   3 +
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gengtype.c                               |   3 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 141 +++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 ++
 gcc/optinfo-emit-fopt-info.cc                | 100 +++++++
 gcc/optinfo-emit-fopt-info.h                 |  26 ++
 gcc/optinfo-emit-json.cc                     | 408 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  37 +++
 gcc/optinfo-internal.h                       | 139 +++++++++
 gcc/optinfo.cc                               | 272 ++++++++++++++++++
 gcc/optinfo.h                                | 389 +++++++++++++++++++++++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/profile-count.c                          |  28 ++
 gcc/profile-count.h                          |   5 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  32 +++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 158 +++++++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-ssa-live.c                          |   4 +-
 32 files changed, 1889 insertions(+), 19 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-fopt-info.cc
 create mode 100644 gcc/optinfo-emit-fopt-info.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index b3c7d5d..5ae5713 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1422,6 +1422,10 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
+	optinfo-emit-diagnostics.o \
+	optinfo-emit-fopt-info.o \
+	optinfo-emit-json.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
@@ -2588,6 +2592,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/internal-fn.h \
   $(srcdir)/hsa-common.c \
   $(srcdir)/calls.c \
+  $(srcdir)/optinfo.h \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/common.opt b/gcc/common.opt
index d6ef859..42570aa 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -505,6 +505,11 @@ Driver Negative(Qn)
 R
 Driver Joined Separate
 
+fremarks
+Common Remark Var(flag_remarks)
+Emit diagnostic remarks about optimizations
+FIXME: should this be -fdiagnostics-foo or somesuch?
+
 S
 Driver
 
@@ -1941,6 +1946,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 283b4eb..6fe56b5 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -325,6 +325,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/diagnostic-color.c b/gcc/diagnostic-color.c
index 3ee21bc..bcc3767 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
+  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
+	       6, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index aa5807e..63166b8 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool remark (location_t, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index e22c17b..97ed88b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
     {
     case DK_DEBUG:
     case DK_NOTE:
+    case DK_REMARK:
     case DK_ANACHRONISM:
     case DK_WARNING:
       break;
@@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   return ret;
 }
 
+/* Emit an optimization remark at LOCATION.  This is used by the optinfo
+   framework.
+   Return true if the remark was printed, false if it was inhibited.  */
+// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
+
+bool
+remark (location_t location, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, location);
+  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
index ce3dc56..b58095d 100644
--- a/gcc/diagnostic.def
+++ b/gcc/diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9c7fe18..e6f58f1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
 * Debugging Options::   Producing debuggable code.
 * Optimize Options::    How much optimization?
 * Instrumentation Options:: Enabling profiling and extra run-time error checking.
+* Remarks::             Details on how your code is being optimized.
 * Preprocessor Options:: Controlling header files and macro definitions.
                          Also, getting dependency information for Make.
 * Assembler Options::   Passing options to the assembler.
@@ -416,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
@@ -479,6 +481,10 @@ Objective-C and Objective-C++ Dialects}.
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
 
+@item Remarks
+@xref{Remarks,,Options to Control Remarks from the Compiler}.
+@gccoptlist{-fremarks}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -9854,6 +9860,15 @@ 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.
+FIXME: The precise format is not yet set in stone, but it ought
+to be stabilized and then documented somewhere.
+FIXME: should this be described here within the optimization options,
+or within the developer options?
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
@@ -12126,6 +12141,23 @@ The NOP instructions are inserted at---and maybe before, depending on
 @end table
 
 
+@node Remarks
+@section Options to Control Remarks from the Compiler
+@cindex remarks
+@cindex options, remarks
+
+These options are aimed at advanced users who may be interested
+in seeing additional diagnostics from the compiler, giving information
+on the decisions it is making on the code.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+FIXME: better description needed here.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 6af1445..038cdb5 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -73,14 +73,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[] =
@@ -131,7 +123,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},
@@ -344,7 +336,7 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
 
 /* Print source location on DFILE if enabled.  */
 
-void
+static void
 dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
 {
   if (dump_kind)
@@ -360,6 +352,18 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Print source location on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_loc (dump_flags_t dump_kind, source_location loc)
+{
+  if (dump_file && (dump_kind & pflags))
+    dump_loc (dump_kind, dump_file, loc);
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    dump_loc (dump_kind, alt_dump_file, loc);
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index b5582f7..3c4d5a6 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -277,6 +277,7 @@ extern const char *dump_flag_name (int);
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
 extern void dump_printf_loc (dump_flags_t, source_location,
                              const char *, ...) ATTRIBUTE_PRINTF_3;
+extern void dump_loc (dump_flags_t dump_kind, source_location loc);
 extern void dump_function (int phase, tree fn);
 extern void dump_basic_block (int, basic_block, int);
 extern void dump_generic_expr_loc (int, source_location, int, tree);
@@ -311,6 +312,8 @@ dump_enabled_p (void)
   return (dump_file || alt_dump_file);
 }
 
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
+
 namespace gcc {
 
 class dump_manager
diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
index 565fa83..a10b6aa 100644
--- a/gcc/fortran/gfc-diagnostic.def
+++ b/gcc/fortran/gfc-diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 0db5528..68455f0 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1724,7 +1724,8 @@ open_base_files (void)
       "tree-dfa.h", "tree-ssa.h", "reload.h", "cpp-id-data.h", "tree-chrec.h",
       "except.h", "output.h",  "cfgloop.h", "target.h", "lto-streamer.h",
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
-      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h", NULL
+      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h",
+      "optinfo.h", NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 819a962..abd363c 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,6 +105,7 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
+	  test_flag("Remark", flags,  " | CL_REMARK") \
 	  test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
 	sub( "^0 \\| ", "", result )
 	return result
diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
new file mode 100644
index 0000000..2f3ebf8
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,141 @@
+/* Emit optimization information as "remark" diagnostics.
+   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 "tree-pass.h"
+#include "gimple.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-internal.h"
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_translate_identifiers (&pp) = false;
+
+  bool show_color = pp_show_color (global_dc->printer);
+
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_optinfo_scope_depth (); i > 0; i--)
+    pp_space (&pp);
+
+  /* Print the items into PP.  */
+  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:
+	  {
+	    const optinfo_item_text *as_text
+	      = (const optinfo_item_text *)item;
+	    pp_string (&pp, as_text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree
+	      = (const optinfo_item_tree *)item;
+	    pp_begin_quote (&pp, show_color);
+	    dump_generic_node (&pp, as_tree->get_node (), 0, TDF_DETAILS,
+			       false);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_string (&pp, node->dump_name ());
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	}
+    }
+
+  /* Add metadata: which pass?  */
+  if (current_pass)
+    {
+      pp_string (&pp, " [");
+      pp_string (&pp, colorize_start (show_color,
+				      diagnostic_get_color_for_kind (DK_REMARK)));
+      pp_string (&pp, "pass=");
+      pp_string (&pp, current_pass->name);
+      pp_string (&pp, colorize_stop (show_color));
+      pp_string (&pp, "]");
+    }
+
+  /* Add metadata: hotness.  */
+  gimple *stmt = optinfo->get_location ().m_stmt;
+  if (stmt)
+    if (stmt->bb)
+      if (stmt->bb->count.initialized_p ())
+	{
+	  profile_count count = stmt->bb->count;
+	  pp_string (&pp, " [");
+	  pp_string (&pp, colorize_start (show_color,
+					  diagnostic_get_color_for_kind (DK_NOTE)));
+	  pp_string (&pp, "count(");
+	  pp_string (&pp, profile_quality_as_string (count.quality ()));
+	  pp_string (&pp, ")=");
+	  pp_scalar (&pp, "%li", count.to_gcov_type ());
+	  pp_string (&pp, colorize_stop (show_color));
+	  pp_string (&pp, "]");
+	}
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
new file mode 100644
index 0000000..820cefd
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.h
@@ -0,0 +1,26 @@
+/* Emit optimization information as "remark" diagnostics.
+   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_DIAGNOSTICS_H
+#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
+
+extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
diff --git a/gcc/optinfo-emit-fopt-info.cc b/gcc/optinfo-emit-fopt-info.cc
new file mode 100644
index 0000000..b01a301
--- /dev/null
+++ b/gcc/optinfo-emit-fopt-info.cc
@@ -0,0 +1,100 @@
+/* Emit optimization information via -fopt-info.
+   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 "tree.h"
+#include "tree-pass.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-fopt-info.h"
+#include "optinfo-internal.h"
+
+/* Emit OPTINFO via -fopt-info.  */
+
+void
+emit_optinfo_via_fopt_info (const optinfo *optinfo)
+{
+  dump_flags_t flags = TDF_NONE;
+  switch (optinfo->get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      flags = MSG_OPTIMIZED_LOCATIONS;
+      break;
+    case OPTINFO_KIND_FAILURE:
+      flags = MSG_MISSED_OPTIMIZATION;
+      break;
+    case OPTINFO_KIND_NOTE:
+    case OPTINFO_KIND_SCOPE:
+      flags = MSG_NOTE;
+      break;
+    }
+  if (optinfo->details_p ())
+    flags |= TDF_DETAILS;
+
+  dump_loc (flags, optinfo->get_location_t ());
+  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:
+	  {
+	    const optinfo_item_text *text = (const optinfo_item_text *)item;
+	    dump_printf (flags, "%s", text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *tree = (const optinfo_item_tree *)item;
+	    dump_generic_expr (flags, TDF_DETAILS, tree->get_node ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    dump_gimple_stmt (flags, TDF_SLIM, stmt, 0);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    dump_printf (flags, "%s", node->dump_name ());
+	  }
+	  break;
+	}
+    }
+  dump_printf (flags, "\n");
+}
diff --git a/gcc/optinfo-emit-fopt-info.h b/gcc/optinfo-emit-fopt-info.h
new file mode 100644
index 0000000..35868ab
--- /dev/null
+++ b/gcc/optinfo-emit-fopt-info.h
@@ -0,0 +1,26 @@
+/* Emit optimization information via -fopt-info.
+   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_FOPT_INFO_H
+#define GCC_OPTINFO_EMIT_FOPT_INFO_H
+
+extern void emit_optinfo_via_fopt_info (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_FOPT_INFO_H */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..0cda963
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,408 @@
+/* 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 "optinfo-internal.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+impl_location_to_json (optinfo_impl_location 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));
+  obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+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.  */
+
+static json::object *
+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;
+}
+
+/* Create a JSON object representing PASS.  */
+
+static json::object *
+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 ("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.  */
+
+static json::value *
+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 ();
+	  obj->set ("fndecl",
+		    new json::string (lang_hooks.decl_printable_name (fndecl, 2)));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* The array of optimization records.
+   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.  */
+
+static json::array *records;
+
+/* The currently open scopes, for expressing nested optimization records.  */
+
+static vec<json::array *> scopes;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  records = new json::array ();
+
+  scopes.safe_push (records);
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      records->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+
+  delete records;
+  records = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return records != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to then queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  gimple *stmt = optinfo->get_location ().m_stmt;//get_best_stmt ();
+
+  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:
+	  {
+	    const optinfo_item_text *as_text = (const optinfo_item_text *)item;
+	    message->append (new json::string (as_text->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree = (const optinfo_item_tree *)item;
+	    tree node = as_tree->get_node ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+	         dump_generic_expr (MSG_NOTE, flags, node);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+	         print_generic_expr (alt_dump_file, node, dump_flags | extra_dump_flags);
+	       which is:
+		 maybe_init_pretty_print (file);
+		 dump_generic_node (tree_pp, node, 0, flags, false);
+		 pp_flush (tree_pp);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_translate_identifiers (&pp) = false;
+
+	    dump_generic_node (&pp, node, 0, as_tree->get_flags (), false);
+
+	    item->set ("expr", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the node.  */
+	    if (EXPR_HAS_LOCATION (node))
+	      item->set ("location", location_to_json (EXPR_LOCATION (node)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+		 dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+		 print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
+	       which is:
+		 pp_needs_newline (&buffer) = true;
+		 buffer.buffer->stream = file;
+		 pp_gimple_stmt_1 (&buffer, g, spc, flags);
+		 pp_newline_and_flush (&buffer);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    item->set ("stmt", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the stmt.  */
+	    if (gimple_location (stmt))
+	      item->set ("location", location_to_json (gimple_location (stmt)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    json::object *item = new json::object ();
+	    item->set ("name", new json::string (node->name ()));
+	    item->set ("order", new json::number (node->order));
+	    if (DECL_SOURCE_LOCATION (node->decl) != UNKNOWN_LOCATION)
+	      item->set ("location",
+			 location_to_json (DECL_SOURCE_LOCATION (node->decl)));
+	    message->append (item);
+	  }
+	  break;
+	}
+   }
+
+  if (current_pass)
+    obj->set ("pass", pass_to_json (current_pass));
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (stmt)
+    {
+      loc = gimple_location (stmt);
+      if (stmt->bb)
+	if (stmt->bb->count.initialized_p ())
+	  obj->set ("count", profile_count_to_json (stmt->bb->count));
+    }
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  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));
+
+  /* Add to innermost scope.  */
+  gcc_assert (scopes.length () > 0);
+  scopes[scopes.length () - 1]->append (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      scopes.safe_push (children);
+    }
+}
+
+/* Handling for the end of an optinfo scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_optinfo_scope ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  scopes.pop ();
+}
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..f228c67
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,37 @@
+/* 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 ();
+
+// FIXME: maybe make this inline?
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_optinfo_scope ();
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..b2c1b26
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,139 @@
+/* Implementation details of optinfo.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_OPTINFO_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (optinfo_tree node_with_flags)
+    : m_node_with_flags (node_with_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node_with_flags.m_node; }
+  dump_flags_t get_flags () const { return m_node_with_flags.m_flags; }
+
+ private:
+  optinfo_tree m_node_with_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt) : m_stmt (stmt) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+
+ private:
+  gimple *m_stmt;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..6e227ce
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,272 @@
+/* Optimization information.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cfgloop.h" // for struct loop and loop_p, etc
+#include "tree-scalar-evolution.h" // for get_loop_exit_condition
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-emit-fopt-info.h"
+#include "optinfo-internal.h"
+
+/* Constructing an optinfo_location from a basic block.  */
+
+optinfo_location::optinfo_location (basic_block bb)
+  : m_stmt (NULL)
+{
+  // FIXME:
+  // TODO: deal with both RTL and gimple
+  if (bb->flags & BB_RTL)
+    ;
+  else
+    {
+       /* Use the first stmt in the bb that has a location.  */
+       gimple *stmt = NULL;
+       for (gimple_stmt_iterator si = gsi_start_bb (bb);
+	    !gsi_end_p (si); gsi_next (&si))
+	{
+	  stmt = gsi_stmt (si);
+	  if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+	    {
+		m_stmt = stmt;
+		return;
+	    }
+	}
+	/* Otherwise, just use any stmt for now.  */
+        m_stmt = stmt;
+    }
+}
+
+/* Constructing an optinfo_location from a loop.  */
+
+optinfo_location::optinfo_location (struct loop *loop)
+  : m_stmt (NULL)
+{
+  if (loop)
+    {
+      m_stmt = get_loop_exit_condition (loop);
+      if (m_stmt)
+	return;
+
+      gimple_stmt_iterator si;
+      for (si = gsi_start_bb (loop->header); !gsi_end_p (si); gsi_next (&si))
+	{
+	  gimple *stmt = gsi_stmt (si);
+	  if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+	    {
+	      m_stmt = stmt;
+	      return;
+	    }
+	}
+    }
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Flush the pending information.  */
+  emit ();
+
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Get a location_t from an optinfo.  */
+
+location_t
+optinfo::get_location_t () const
+{
+  if (m_loc.m_stmt)
+    return gimple_location (m_loc.m_stmt);
+  else
+    return UNKNOWN_LOCATION;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit () const
+{
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
+
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
+
+  /* -fopt-info.  */
+  emit_optinfo_via_fopt_info (this);
+}
+
+/* pending_optinfo's ctor.  */
+
+pending_optinfo::pending_optinfo (const optinfo_impl_location &impl_location,
+				  enum optinfo_kind kind,
+				  optinfo_location loc,
+				  bool details)
+  : m_optinfo (new optinfo (impl_location, kind, loc, details))
+{
+}
+
+/* Append a string literal to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const char *msg)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (msg), false);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* Append a tree node with flags to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (optinfo_tree node_with_flags)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_tree (node_with_flags));
+  return *this;
+}
+
+/* Append a gimple statement to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (gimple *stmt)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_gimple (stmt));
+  return *this;
+}
+
+/* Append a symbol table node to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (symtab_node *node)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_symtab_node (node));
+  return *this;
+}
+
+/* Append printf-formatted text to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const optinfo_printf &p)
+{
+  // FIXME: there are some double-copies going on here
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (p.get_formatted_text ()), true);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const optinfo_print_dec &arg)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (arg.m_wi, buf, arg.m_sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* The current nesting depth of optinfo scopes, for use
+   by remarks (for showing nesting via indentation).  */
+
+static unsigned int scope_depth;
+
+/* Push a nested optinfo scope.  */
+
+void
+push_optinfo_scope ()
+{
+  scope_depth++;
+}
+
+/* Pop a nested optinfo scope.  */
+
+void
+pop_optinfo_scope ()
+{
+  scope_depth--;
+  optimization_records_maybe_pop_optinfo_scope ();
+}
+
+/* Get the current optinfo scope-nesting depth.
+   For use by remarks (for showing nesting via indentation).  */
+
+unsigned int
+get_optinfo_scope_depth ()
+{
+  return scope_depth;
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  return dump_enabled_p () || optimization_records_enabled_p () || flag_remarks;
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return optimization_records_enabled_p ();
+}
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..0870e8a
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,389 @@
+/* Optimization information.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_OPTINFO_H
+#define GCC_OPTINFO_H
+
+#include "unique-ptr.h"
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations:
+
+   * the "-fopt-info" destination(s)
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file via "-fsave-optimization-record"
+
+   Building an optinfo instance is non-trivial, so all usage should be
+   guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.
+
+   They're intended to be be short-lived; in particular, there's no
+   interaction with GTY: it's assumed that no GC happens during the
+   lifetime of an optinfo.  */
+
+/* Forward decls.  */
+class pending_optinfo;
+
+/* optinfo-internal.h.  */
+class optinfo_item;
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* A class for describing a source-code location for an optinfo,
+   with various constructors for convenience.
+   In particular, this lets us associate optinfo instances
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.
+   Currently this is GTY-marked, so that vect_optinfo_location can be a
+   GC root.  */
+
+class GTY(()) optinfo_location
+{
+ public:
+  optinfo_location () : m_stmt (NULL) {}
+  optinfo_location (gimple *stmt) : m_stmt (stmt) {}
+  optinfo_location (basic_block bb);
+  optinfo_location (struct loop *loop);
+
+  gimple *m_stmt;
+  // Or maybe a basic_block and a location_t, so that we can support RTL.
+};
+
+/* A way to identify where in the compiler source that optimization information
+   is being emitted from.  */
+/* FIXME: taken from selftest::location; should this be refactored?  */
+
+struct optinfo_impl_location
+{
+  optinfo_impl_location (const char *file, int line, const char *function)
+    : m_file (file), m_line (line), m_function (function) {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* The current source location, expressed as an optinfo_impl_location.  */
+
+#define OPTINFO_IMPL_LOCATION \
+  (optinfo_impl_location (__FILE__, __LINE__, __FUNCTION__))
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.
+
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo
+{
+  friend class pending_optinfo;
+ public:
+  optinfo (const optinfo_impl_location &impl_location,
+	   enum optinfo_kind kind,
+	   optinfo_location loc,
+	   bool details)
+  : m_impl_location (impl_location), m_kind (kind), m_loc (loc), m_items (),
+    m_details (details)
+  {}
+  ~optinfo ();
+
+  optinfo_impl_location get_impl_location () const { return m_impl_location; }
+  enum optinfo_kind get_kind () const { return m_kind; }
+  optinfo_location get_location () const { return m_loc; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+  bool details_p () const { return m_details; }
+
+  location_t get_location_t () const;
+
+ private:
+   void emit () const;
+
+ private:
+  optinfo_impl_location m_impl_location;
+  enum optinfo_kind m_kind;
+  optinfo_location m_loc;
+  auto_vec <optinfo_item *> m_items;
+  /* If true, gate this with TDF_DETAILS when emitting to dump/-fopt-info.  */
+  bool m_details;
+};
+
+/* Various helper classes and functions for adding messages and data
+   to an optinfo.
+   All of these are used via "<<" on a pending optinfo, e.g.
+      OPTINFO_NOTE (loop)
+         << optinfo_printf ("something about loop %d: ", loop->num)
+	 << slim (expr);  */
+
+/* Packaging up a tree with dump flags.  */
+// FIXME: should this be GTY-marked?
+
+struct optinfo_tree
+{
+  optinfo_tree (tree node, dump_flags_t flags)
+    : m_node (node), m_flags (flags)
+  {
+    /* Only the TDF_* flags are allowed.  */
+    gcc_assert ((m_flags & MSG_ALL) == 0);
+    // FIXME: what happened to TDF_KIND_MASK, mentioned in dumpfile.h ?
+  }
+
+  tree m_node;
+  dump_flags_t m_flags;
+};
+
+/* Helper function for adding a "slim" representation of a tree to
+   an optinfo, e.g.:
+      OPTINFO_NOTE (stmt) << "expr is: " << slim (expr);  */
+
+static inline optinfo_tree slim (tree node)
+{
+  return optinfo_tree (node, TDF_SLIM);
+}
+
+/* Helper function for adding a "detailed" representation of a tree to
+   an optinfo, e.g.:
+      OPTINFO_NOTE (stmt) << "expr is: " << details (expr);  */
+
+static inline optinfo_tree details (tree node)
+{
+  return optinfo_tree (node, TDF_DETAILS);
+}
+
+/* A class for adding printf-style formatted strings to an optinfo,
+   e.g.:
+     OPTINFO_SUCCESS (edge->call_stmt)
+       << optinfo_printf ("devirtualizing call in %s to %s",
+                          edge->caller->dump_name (),
+                          target->dump_name ());  */
+class optinfo_printf
+{
+ public:
+  optinfo_printf (const char *fmt, ...)
+   ATTRIBUTE_PRINTF_2
+  {
+    va_list ap;
+    va_start (ap, fmt);
+    m_formatted_text = xvasprintf (fmt, ap);
+    va_end (ap);
+  }
+
+  ~optinfo_printf () { free (m_formatted_text); } // FIXME: really?
+
+  const char *get_formatted_text () const { return m_formatted_text; }
+
+ private:
+  char *m_formatted_text;
+};
+
+/* Helper struct for adding a wide_int_ref to an optinfo.  */
+
+struct optinfo_print_dec
+{
+  optinfo_print_dec (const wide_int_ref &wi, signop sgn)
+  : m_wi (wi), m_sgn (sgn) {}
+
+  const wide_int_ref &m_wi;
+  signop m_sgn;
+};
+
+/* Helper function for adding a signed decimal representation of a
+   wide_int_ref to an optinfo.  */
+
+static inline optinfo_print_dec
+decs (const wide_int_ref &wi)
+{
+  return optinfo_print_dec (wi, SIGNED);
+}
+
+/* Helper function for adding an unsigned decimal representation of
+   a wide_int_ref to an optinfo.  */
+
+static inline optinfo_print_dec
+decu (const wide_int_ref &wi)
+{
+  return optinfo_print_dec (wi, UNSIGNED);
+}
+
+/* Support class for building and emitting optinfo instances.
+   Intended to be used via the macros below, e.g.:
+
+     if (optinfo_enabled_p ())
+       OPTINFO_NOTE (stmt) << "some message about: " << stmt;
+
+   or, if control flow is needed:
+
+     if (optinfo_enabled_p ())
+       {
+         pending_optinfo info = OPTINFO_NOTE (stmt);
+         info << "some message about: " << stmt;
+         if (some_condition)
+	   info << " etc";
+         else
+           info << " some other message";
+       }
+
+   The info is emitted to all active sinks when the pending_optinfo
+   goes out of scope.  */
+
+class pending_optinfo
+{
+ public:
+  pending_optinfo (const optinfo_impl_location &impl_location,
+		   enum optinfo_kind kind,
+		   optinfo_location loc,
+		   bool details);
+
+  /* Pre-canned ways of adding information to the optinfo.  */
+  pending_optinfo& operator<< (const char *);
+  pending_optinfo& operator<< (optinfo_tree);
+  pending_optinfo& operator<< (gimple *);
+  pending_optinfo& operator<< (symtab_node *node);
+  pending_optinfo& operator<< (const optinfo_printf &);
+  pending_optinfo& operator<< (const optinfo_print_dec &);
+
+  template<unsigned int N, typename C>
+  pending_optinfo& operator<< (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      *this << optinfo_print_dec (value.coeffs[0], sgn);
+    else
+      {
+	*this << "[";
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    *this << optinfo_print_dec (value.coeffs[i], sgn);
+	    *this << (i == N - 1 ? "]" : ",");
+	  }
+      }
+    return *this;
+  }
+
+ private:
+  // FIXME: are we allowed this yet?  or do we need a gnu::shared_ptr?
+  std::shared_ptr <optinfo> m_optinfo;
+};
+
+/* Emit optimization information at LOC of the given KIND.  */
+
+#define OPTINFO(KIND, LOC) \
+  pending_optinfo ((OPTINFO_IMPL_LOCATION), (KIND), (LOC), false)
+
+/* Emit that a successful optimization happened at LOC.  */
+
+#define OPTINFO_SUCCESS(LOC)\
+  OPTINFO(OPTINFO_KIND_SUCCESS, (LOC))
+
+/* Emit that a failed optimization happened at LOC.  */
+
+#define OPTINFO_FAILURE(LOC)\
+  OPTINFO(OPTINFO_KIND_FAILURE, (LOC))
+
+/* Emit a note relating to an optimization at LOC.  */
+
+#define OPTINFO_NOTE(LOC)\
+  OPTINFO(OPTINFO_KIND_NOTE, (LOC))
+
+/* Emit optimization information at LOC of the given KIND,
+   tagging it with TDF_DETAILS for -fopt-info.  */
+
+#define OPTINFO_DETAILS(KIND, LOC) \
+  pending_optinfo ((OPTINFO_IMPL_LOCATION), (KIND), (LOC), true)
+
+/* Emit that a successful optimization happened at LOC,
+   tagging it with TDF_DETAILS for -fopt-info.  */
+
+#define OPTINFO_SUCCESS_DETAILS(LOC)\
+  OPTINFO_DETAILS(OPTINFO_KIND_SUCCESS, (LOC))
+
+extern void push_optinfo_scope ();
+extern void pop_optinfo_scope ();
+extern unsigned int get_optinfo_scope_depth ();
+
+/* Implementation detail of the OPTINFO_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit optinfo remarks
+   about entering and exiting the body of a given function.  */
+
+class optinfo_scope
+{
+ public:
+  optinfo_scope (const char *name, optinfo_location loc,
+		 const optinfo_impl_location &impl_location)
+  : m_name (name), m_loc (loc)
+  {
+    if (optinfo_enabled_p ())
+      {
+	pending_optinfo (impl_location, OPTINFO_KIND_SCOPE, loc, false)
+	  << optinfo_printf ("=== %s ===", name);
+	push_optinfo_scope ();
+      }
+  }
+  ~optinfo_scope ()
+  {
+    if (optinfo_enabled_p ())
+      pop_optinfo_scope ();
+  }
+
+ private:
+  const char *m_name;
+  optinfo_location m_loc;
+};
+
+/* A macro for emitting an optinfo note about entering a scope,
+   pushing and popping the scope, so that all optinfos "within"
+   the scope are nested within it.  */
+
+#define OPTINFO_SCOPE(NAME, LOC) \
+  optinfo_scope scope (NAME, LOC, (OPTINFO_IMPL_LOCATION))
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/opts.c b/gcc/opts.c
index 33efcc0..8029c08 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
 	case CL_WARNING:
 	  description = _("The following options control compiler warning messages");
 	  break;
+	case CL_REMARK:
+	  description = _("The following options control compiler remarks");
+	  break;
 	case CL_OPTIMIZATION:
 	  description = _("The following options control optimizations");
 	  break;
@@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
 	      { "optimizers", CL_OPTIMIZATION },
 	      { "target", CL_TARGET },
 	      { "warnings", CL_WARNING },
+	      { "remarks", CL_REMARK },
 	      { "undocumented", CL_UNDOCUMENTED },
 	      { "params", CL_PARAMS },
 	      { "joined", CL_JOINED },
diff --git a/gcc/opts.h b/gcc/opts.h
index 484fc1c..31ea706 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -135,20 +135,21 @@ extern const unsigned int cl_lang_count;
 #define CL_DRIVER		(1U << 19) /* Driver option.  */
 #define CL_TARGET		(1U << 20) /* Target-specific option.  */
 #define CL_COMMON		(1U << 21) /* Language-independent.  */
+#define CL_REMARK		(1U << 22) /* Enables an (optional) remark.  */
 
 #define CL_MIN_OPTION_CLASS	CL_PARAMS
-#define CL_MAX_OPTION_CLASS	CL_COMMON
+#define CL_MAX_OPTION_CLASS	CL_REMARK
 
 /* From here on the bits describe attributes of the options.
    Before this point the bits have described the class of the option.
    This distinction is important because --help will not list options
    which only have these higher bits set.  */
 
-#define CL_JOINED		(1U << 22) /* If takes joined argument.  */
-#define CL_SEPARATE		(1U << 23) /* If takes a separate argument.  */
-#define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
-#define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
-#define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_JOINED		(1U << 23) /* If takes joined argument.  */
+#define CL_SEPARATE		(1U << 24) /* If takes a separate argument.  */
+#define CL_UNDOCUMENTED		(1U << 25) /* Do not output with --help.  */
+#define CL_NO_DWARF_RECORD	(1U << 26) /* Do not add to producer string.  */
+#define CL_PCH_IGNORE		(1U << 27) /* Do compare state for pch.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
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/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5a19fc9..1f0a079 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -96,6 +96,8 @@ set plugin_test_list [list \
 	  must-tail-call-2.c } \
     { expensive_selftests_plugin.c \
 	  expensive-selftests-1.c } \
+    { remarks_plugin.c \
+	  remarks-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
new file mode 100644
index 0000000..c42ad96
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-fremarks" } */
+
+extern void test_string_literal (void);
+extern void test_tree (void);
+extern void test_gimple (int);
+extern void test_cgraph_node (void);
+extern void test_printf (void);
+extern void test_wide_int_signed (void);
+extern void test_wide_int_unsigned (void);
+extern void test_poly_int (void);
+extern void test_scopes (void);
+
+void test_remarks (void)
+{
+  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
+  test_tree (); /* { dg-remark "test of tree: '0'" } */
+  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
+  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
+  test_printf (); /* { dg-remark "test of optinfo_printf: 42" } */
+  test_wide_int_signed (); /* { dg-remark "test of wide int: 0" } */
+  test_wide_int_unsigned (); /* { dg-remark "test of wide int: 0" } */
+  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
+
+  test_scopes (); /* { dg-line test_scopes_line } */
+  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
new file mode 100644
index 0000000..09fa4a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,158 @@
+/* Test of remark-emission by optinfo.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "optinfo.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cgraph.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_remarks =
+{
+  GIMPLE_PASS, /* type */
+  "test_remarks", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_remarks : public gimple_opt_pass
+{
+public:
+  pass_test_remarks(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_remarks, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_remarks
+
+unsigned int
+pass_test_remarks::execute (function *fun)
+{
+  basic_block bb;
+  
+  FOR_ALL_BB_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+	 !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+	gimple *stmt = gsi_stmt (gsi);
+	gcall *call = dyn_cast <gcall *> (stmt);
+	if (!call)
+	  continue;
+	tree callee_decl = gimple_call_fndecl (call);
+	if (!callee_decl)
+	  continue;
+	tree callee_name = DECL_NAME (callee_decl);
+	if (!callee_name)
+	  continue;
+	const char *callee = IDENTIFIER_POINTER (callee_name);
+
+	/* Various optinfo tests, done at callsites,
+	   controlled by the callee name.  */
+	if (strcmp (callee, "test_string_literal") == 0)
+	  {
+	    OPTINFO_NOTE (stmt) << "test of remark for " << callee;
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of tree: "
+	      << slim (integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of gimple: "
+	      << stmt;
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of callgraph node: "
+	      << cgraph_node::get (callee_decl);
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << optinfo_printf ("test of optinfo_printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int_signed") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of wide int: "
+	      << decs (wi::to_wide (integer_zero_node));
+	  }
+	else if (strcmp (callee, "test_wide_int_unsigned") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of wide int: "
+	      << decu (wi::to_wide (integer_zero_node));
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of poly int: "
+	      << poly_int64 (42);
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    OPTINFO_SCOPE ("outer scope", stmt);
+	    {
+	      OPTINFO_NOTE (stmt) << "at outer scope";
+	      OPTINFO_SCOPE ("middle scope", stmt);
+	      {
+		OPTINFO_NOTE (stmt) << "at middle scope";
+		OPTINFO_SCOPE ("innermost scope", stmt);
+		OPTINFO_NOTE (stmt) << "at innermost scope";
+	      }
+	    }
+	  }
+      }
+
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_remarks (gcc::context *ctxt)
+{
+  return new pass_test_remarks (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  pass_info.pass = make_pass_test_remarks (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index 3770f69..4d7a450 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1152,6 +1152,15 @@ proc dg-locus { args } {
     verbose "process-message:\n${dg-messages}" 2
 }
 
+# Handle remarks.
+
+proc dg-remark { args } {
+    # Make this variable available here and to the saved proc.
+    upvar dg-messages dg-messages
+
+    process-message saved-dg-error "remark: " "$args"
+}
+
 # Check the existence of a gdb in the path, and return true if there
 # is one.
 #
diff --git a/gcc/toplev.c b/gcc/toplev.c
index b066bcc..cb12f73 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -84,6 +84,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"
@@ -488,6 +489,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;
 
@@ -2094,6 +2097,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 62316ba..9ff03fe 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);
 
@@ -526,7 +527,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

