From 033af2881cbbbb9ea9e570215fc620fc6a09c91e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 2 Jul 2018 01:43:19 -0400
Subject: [PATCH 08/58] optinfo: add diagnostic remarks

This patch adds the first destination for optinfo instances to be
emitted to: as "remarks" through the diagnostics subsystem.

Examples can be seen at
  https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html

Remarks look a lot like the output of -fopt-info, with the following
differences:

* announcing "In function 'blah':" etc when the function changes.

* printing the corresponding source lines (unless
  "-fno-diagnostics-show-caret"), as per other diagnostics, and

* colorizing the various parts of the message if stderr is at a tty.

* showing extra metadata:
  * the pass that emitted the remark,
  * the execution count of the code in question
  * which file/line/function of GCC emitted the remark

* possibly allowing for remarks to be used in DejaGnu tests to better
  associate testing of an optimization with the source line in
  question, rather than relying on a scan of the dumpfile (which is
  per-source file and thus rather "coarse-grained").*

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo-emit-diagnostics.o.
	* common.opt (fremarks): New option.
	(fdiagnostics-show-remark-hotness): New option.
	(fdiagnostics-show-remark-origin): New option.
	(fdiagnostics-show-remark-pass): New option.
	* 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.
	(-fremarks): New option.
	(-fno-diagnostics-show-remark-hotness): New option.
	(-fno-diagnostics-show-remark-origin): New option.
	(-fno-diagnostics-show-remark-pass): New option.
	* dumpfile.c (dump_context::get_scope_depth): Update comment.
	(dump_context::end_any_optinfo): Likewise.
	* dumpfile.h: Update comment.
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo.cc: Include "optinfo-emit-diagnostics.h".
	(optinfo::emit): Call emit_optinfo_as_diagnostic_remark.
	(optinfo_enabled_p): Use flag_remarks.
	* optinfo.h: Update comment.
	* 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.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::optinfo_emit_diagnostics_cc_tests.
	* selftest.h (selftest::optinfo_emit_diagnostics_cc_tests): New
	decl.

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

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/Makefile.in                              |   1 +
 gcc/common.opt                               |  17 ++
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 ++
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  67 ++++++
 gcc/dumpfile.c                               |   5 +-
 gcc/dumpfile.h                               |   2 +
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++++++++++++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 +++
 gcc/optinfo.cc                               |   9 +-
 gcc/optinfo.h                                |   4 +-
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/profile-count.c                          |  28 +++
 gcc/profile-count.h                          |   5 +
 gcc/selftest-run-tests.c                     |   1 +
 gcc/selftest.h                               |   1 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 +++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 +++++++++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 25 files changed, 701 insertions(+), 16 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.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 dd1dfc1..33483ea 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1428,6 +1428,7 @@ OBJS = \
 	optabs-query.o \
 	optabs-tree.o \
 	optinfo.o \
+	optinfo-emit-diagnostics.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 5a50bc27..908432e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -510,6 +510,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
 
@@ -1281,6 +1286,18 @@ fdiagnostics-show-option
 Common Var(flag_diagnostics_show_option) Init(1)
 Amend appropriate diagnostic messages with the command line option that controls them.
 
+fdiagnostics-show-remark-hotness
+Common Var(flag_diagnostics_show_remark_hotness) Init(1)
+When emitting optimization remarks, show the execution count of the code being optimized.
+
+fdiagnostics-show-remark-origin
+Common Var(flag_diagnostics_show_remark_origin) Init(1)
+When emitting optimization remarks, show which line of GCC code produced the remark.
+
+fdiagnostics-show-remark-pass
+Common Var(flag_diagnostics_show_remark_pass) Init(1)
+When emitting optimization remarks, show which optimization pass produced the remark.
+
 fdisable-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 disables an optimization pass.
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 56cd122..c96d202 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.
@@ -471,6 +472,11 @@ 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 -fno-diagnostics-show-remark-hotness @gol
+-fno-diagnostics-show-remark-origin -fno-diagnostics-show-remark-pass}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -12043,6 +12049,67 @@ 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.
+
+The precise messages and their format are subject to change.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+
+Enabling this option leads to GCC emitting diagnostics detailing the
+optimization decisions it is making.
+
+For example, this message:
+
+@smallexample
+test.c:13:3: remark:   Symbolic number of iterations is '(unsigned int) n_8(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
+@end smallexample
+
+describes an internal detail of the ``vect'' optimization pass, acting at
+the given source location within ``test.c'', where the remark was emitted
+by the function ``vect_analyze_loop_form'' at line 1387 of GCC's source
+file ``tree-vect-loop.c''.
+
+@item -fno-diagnostics-show-remark-hotness
+@opindex fno-diagnostics-show-remark-hotness
+@opindex fdiagnostics-show-remark-hotness
+By default, if diagnostic remarks are enabled, they include information
+on the execution count, or ``hotness'', of the code being optimized:
+the ``[count(precise)=76800000]'' in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-origin
+@opindex fno-diagnostics-show-remark-origin
+@opindex fdiagnostics-show-remark-origin
+By default, if diagnostic remarks are enabled, they include information
+on where in GCC's own source code (or a plugin's source code) the remark
+is being emitted from; the
+``[../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-pass
+@opindex fno-diagnostics-show-remark-pass
+@opindex fdiagnostics-show-remark-pass
+By default, if diagnostic remarks are enabled, they include information
+on which optimization pass emitted the remark: the ``[pass=vect]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index d931d6a..b3fce50 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -726,7 +726,7 @@ dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
 }
 
 /* Get the current dump scope-nesting depth.
-   For use by -fopt-info (for showing nesting via indentation).  */
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
 
 unsigned int
 dump_context::get_scope_depth () const
@@ -802,8 +802,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 a diagnostic "remark".  */
 
 void
 dump_context::end_any_optinfo ()
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 3096a89..1e3fdcb 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -442,6 +442,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 diagnostic "remarks"
 
    dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                    |
@@ -449,6 +450,7 @@ dump_enabled_p (void)
                                    |
                                    `--> (c) optinfo
                                             `---> optinfo destinations
+                                                  (c.1) diagnostic "remarks"
 
    For optinfos, the dump_*_loc mark the beginning of an optinfo
    instance: all subsequent dump_* calls are consolidated into
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/opt-functions.awk b/gcc/opt-functions.awk
index 2c371e5..48ecac5 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..5320379
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,317 @@
+/* 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"
+#include "dump-context.h"
+#include "selftest.h"
+#include "context.h"
+#include "pass_manager.h"
+
+/* Print the items within OPTINFO to PP (as part of a remark).  */
+
+static void
+print_optinfo_items (pretty_printer *pp, const optinfo *optinfo)
+{
+  bool show_color = pp_show_color (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;
+	}
+    }
+}
+
+/* Print PASS to PP (as part of a remark).  */
+
+static void
+print_pass (pretty_printer *pp, opt_pass *pass)
+{
+  if (pass == NULL)
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp, colorize_start (show_color,
+				 diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_string (pp, "pass=");
+  pp_string (pp, pass->name);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print COUNT to PP (as part of a remark).  */
+
+static void
+print_count (pretty_printer *pp, profile_count count)
+{
+  if (!count.initialized_p ())
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  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, "]");
+}
+
+/* Print IMPL_LOC to PP (as part of a remark).  */
+
+static void
+print_impl_location (pretty_printer *pp, const dump_impl_location_t &impl_loc)
+{
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp,
+	     colorize_start (show_color,
+			     diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_printf (pp, "%s:%i", impl_loc.m_file, impl_loc.m_line);
+  if (impl_loc.m_function)
+    pp_printf (pp, ":%s", impl_loc.m_function);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print OPTINFO to PP.  */
+
+static void
+print_optinfo_as_remark (pretty_printer *pp, const optinfo *optinfo)
+{
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_dump_scope_depth (); i > 0; i--)
+    pp_space (pp);
+
+  /* Print the items into PP.  */
+  print_optinfo_items (pp, optinfo);
+
+  /* Add metadata: which pass?  */
+  if (flag_diagnostics_show_remark_pass)
+    print_pass (pp, optinfo->get_pass ());
+
+  /* Add metadata: hotness.  */
+  if (flag_diagnostics_show_remark_hotness)
+    print_count (pp, optinfo->get_count ());
+
+  /* Add metadata: where was this emitted from.  */
+  if (flag_diagnostics_show_remark_origin)
+    print_impl_location (pp, optinfo->get_impl_location ());
+}
+
+/* Subclass of pretty_printer for building up the text of a remark.  */
+
+class remark_printer : public pretty_printer
+{
+public:
+  remark_printer (bool show_color_);
+};
+
+/* remark_printer's ctor.  */
+
+remark_printer::remark_printer (bool show_color_)
+{
+  pp_needs_newline (this) = true;
+  pp_translate_identifiers (this) = false;
+  pp_show_color (this) = show_color_;
+}
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  remark_printer pp (pp_show_color (global_dc->printer));
+
+  print_optinfo_as_remark (&pp, optinfo);
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that print_optinfo_items works.  */
+
+static void
+test_print_optinfo_items ()
+{
+  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);
+
+  /* Avoid introducing locale-specific differences in the results
+     by hardcoding open_quote and close_quote.  */
+  auto_fix_quotes fix_quotes;
+
+  remark_printer pp (false);
+  print_optinfo_items (&pp, info);
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, "test of tree: `0'");
+}
+
+/* Verify that print_pass works.  */
+
+static void
+test_print_pass ()
+{
+  /* NULL pass.  */
+  {
+    remark_printer pp (false);
+    print_pass (&pp, NULL);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, "");
+  }
+
+  /* Non-NULL pass.  */
+  {
+    remark_printer pp (false);
+    opt_pass *pass = make_pass_ipa_increase_alignment (NULL);
+    print_pass (&pp, pass);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [pass=increase_alignment]");
+    delete pass;
+  }
+}
+
+/* Verify that print_count works.  */
+
+static void
+test_print_count ()
+{
+  remark_printer pp (false);
+  print_count (&pp, profile_count ());
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, " [count(uninitialized)=0]");
+}
+
+/* Verify that print_impl_location works.  */
+
+static void
+test_print_impl_location ()
+{
+  /* Non-NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, "funcname");
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42:funcname]");
+  }
+
+  /* NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, NULL);
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42]");
+  }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_diagnostics_cc_tests ()
+{
+  test_print_optinfo_items ();
+  test_print_pass ();
+  test_print_count ();
+  test_print_impl_location ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
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.cc b/gcc/optinfo.cc
index 1da7d37..bc86696 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "optinfo.h"
 #include "optinfo-internal.h"
+#include "optinfo-emit-diagnostics.h"
 #include "dump-context.h"
 #include "selftest.h"
 
@@ -112,7 +113,8 @@ optinfo::emit ()
       delete last_item;
     }
 
-  /* currently this is a no-op.  */
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
 }
 
 /* Update the optinfo's kind based on DUMP_KIND.  */
@@ -203,9 +205,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 ()
+	  || flag_remarks);
 }
 
 /* Return true if any of the active optinfo destinations make use
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index 0d49823..8a8f812 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -27,9 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 
    * as a "remark" through the diagnostics subsystem
 
-   * saved to a file as an "optimization record"
-
-   Currently no such destinations are implemented.
+   * saved to a file as an "optimization record" (not yet implemented)
 
    They are generated in response to calls to the "dump_*" API in
    dumpfile.h; repeated calls to the "dump_*" API are consolidated
diff --git a/gcc/opts.c b/gcc/opts.c
index ed102c0..5cc8801 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 3c4065ea..d9df788 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -137,20 +137,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/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 989c50a..c0acf19 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -73,6 +73,7 @@ selftest::run_tests ()
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
   optinfo_cc_tests ();
+  optinfo_emit_diagnostics_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 48881c9..1594d1d 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -229,6 +229,7 @@ extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void optinfo_cc_tests ();
+extern void optinfo_emit_diagnostics_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
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..9139b9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,30 @@
+/* { 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 (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 (); /* { 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..332bba6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,152 @@
+/* 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;
+
+  if (!dump_enabled_p ())
+    return 0;
+  
+  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)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of remark for ");
+	    dump_printf (MSG_NOTE, callee);
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of tree: ");
+	    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of gimple: ");
+	    dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of callgraph node: ");
+	    dump_symtab_node (MSG_NOTE, cgraph_node::get (callee_decl));
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of optinfo printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int") == 0)
+	  {
+	    HOST_WIDE_INT val = 0;
+	    dump_printf_loc (MSG_NOTE, stmt,
+			     "test of wide int: " HOST_WIDE_INT_PRINT_DEC,
+			     val);
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of poly int: ");
+	    dump_dec (MSG_NOTE, poly_int64 (42));
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    AUTO_DUMP_SCOPE ("outer scope", stmt);
+	    {
+	      dump_printf_loc (MSG_NOTE, stmt, "at outer scope");
+	      AUTO_DUMP_SCOPE ("middle scope", stmt);
+	      {
+		dump_printf_loc (MSG_NOTE, stmt, "at middle scope");
+		AUTO_DUMP_SCOPE ("innermost scope", stmt);
+		dump_printf_loc (MSG_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 9e0b3f4..a42aa53 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1195,6 +1195,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.
 #
-- 
1.8.5.3

