[PATCH] v3 of optinfo, remarks and optimization records

David Malcolm dmalcolm@redhat.com
Wed Jun 20 16:35:00 GMT 2018


Here's v3 of the patch (one big patch this time, rather than a kit).

Like the v2 patch kit, this patch reuses the existing dump API,
rather than inventing its own.

Specifically, it uses the dump_* functions in dumpfile.h that don't
take a FILE *, the ones that implicitly write to dump_file and/or
alt_dump_file.  I needed a name for them, so I've taken to calling
them the "structured dump API" (better name ideas welcome).

v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
calls as delimiters when consolidating "dump_*" calls.  There's a
new dump_context class which has responsibility for consolidating
them into optimization records.

The dump_*_loc calls now capture more than just a location_t: they
capture the profile_count and the location in GCC's own sources where
the dump is being emitted from.

This works by introducing a new "dump_location_t" class as the
argument of those dump_*_loc calls.  The dump_location_t can
be constructed from a gimple * or from an rtx_insn *, so that
rather than writing:

  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
                   "some message: %i", 42);

you can write:

  dump_printf_loc (MSG_NOTE, stmt,
                   "some message: %i", 42);

and the dump_location_t constructor will grab the location_t and
profile_count of stmt, and the location of the "dump_printf_loc"
callsite (and gracefully handle "stmt" being NULL).

Earlier versions of the patch captured the location of the
dump_*_loc call via preprocessor hacks, or didn't work properly;
this version of the patch works more cleanly: internally,
dump_location_t is split into two new classes:
  * dump_user_location_t: the location_t and profile_count within
    the *user's code*, and
  * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
    the *implementation* code (i.e. GCC or a plugin), captured
    "automagically" via default params

These classes are sometimes used elsewhere in the code.  For
example, "vect_location" becomes a dump_user_location_t
(location_t and profile_count), so that in e.g:

  vect_location = find_loop_location (loop);

it's capturing the location_t and profile_count, and then when
it's used here:

  dump_printf_loc (MSG_NOTE, vect_location, "foo");

the dump_location_t is constructed from the vect_location
plus the dump_impl_location_t at that callsite.

In contrast, loop-unroll.c's report_unroll's "locus" param
becomes a dump_location_t: we're interested in where it was
called from, not in the locations of the various dump_*_loc calls
within it.

Previous versions of the patch captured a gimple *, and needed
GTY markers; in this patch, the dump_user_location_t is now just a
location_t and a profile_count.

The v2 patch added an overload for dump_printf_loc so that you
could pass in either a location_t, or the new type; this version
of the patch eliminates that: they all now take dump_location_t.

Doing so required adding support for rtx_insn *, so that one can
write this kind of thing in RTL passes:

  dump_printf_loc (MSG_NOTE, insn, "foo");

One knock-on effect is that get_loop_location now returns a
dump_user_location_t rather than a location_t, so that it has
hotness information.

Richi: would you like me to split out this location-handling
code into a separate patch?  (It's kind of redundant without
adding the remarks and optimization records work, but if that's
easier I can do it)

The v3 patch adds more detail to remarks: they now show the gcc
source location that emitted them e.g.:

test.c:8:3: remark:   Symbolic number of iterations is '(unsigned int)
n_9(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]

and I added new command-line options for controlling the above:
* -fno-diagnostics-show-remark-hotness
* -fno-diagnostics-show-remark-origin
* -fno-diagnostics-show-remark-pass

An example of remark output (showing colors) can be seen here:

  https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html

I haven't yet implemented options for filtering remarks (I'm thinking
of using the optgroups from -fopt-info, a hotness threshold, and pragmas
for narrowing them to a specific region of the user's code); though
the HTML visualization from the JSON output is likely to provide more
flexibility.

I got rid of GCC_UNLIKELY in favor of a __builtin_expect in
dump_enabled_p, but I haven't measured the impact yet.

Other changes relative to v2:
* added class dump_context; eliminate class pending_optinfo; moved optinfo
  emission from optinfo dtor and into dump_context
* added selftests for dump_* consolidation, for JSON emission, and for
  remarks
* minimized global state in JSON emission
* renamed OPTINFO_SCOPE to AUTO_DUMP_SCOPE and moved from optinfo.h to
  dumpfile.h (and similar changes to the support class)
* added "m_pass" field to optinfo, reading current_pass at time of
  creation, rather than as needed later on

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
(relative to r261555).

How is this looking?

As before, this patch requires the JSON support patch from v1 of the kit:
  "[PATCH 02/10] Add JSON implementation"
    https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01676.html

This approach means we can use all of the existing dump_* calls without
having to rewrite anything, gaining the ability to prioritize dump messages
by code hotness and to track where in GCC any dump message came from
without needing to resort to "grep".

But in terms of actually helping users figure out "how do I get gcc to
vectorize this loop?", it's only a start.  Maybe followup activity would be
to go through the existing dump messages and to make them more user-friendly.
Or to add new dump API entrypoints?
I find the MSG_* a bit verbose, so maybe instead of e.g.:

  dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                   "can't inline function call: ");
  dump_symtab_node (callee);
  dump_printf (" into ");
  dump_symtab_node (caller);
  dump_printf (" as body is not available\n");

there could be, say:

 missed_opt_at (call_stmt,
                ("can't inline function call: %N into %N"
                 " as body is not available\n"),
                callee, caller);

(plus, say "optimized_at", "opt_note_at"), introducing a new family
of format codes (so that the optimization records "know" how to
embed them).

This would also allow for i18n; such API calls would be more explicitly
aimed at tech-savvy end-users.

Anyway, that last bit is more me just thinking aloud about possible future
work here, so here's the v3 patch as it is:

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
	optinfo-emit-json.o.
	(CFLAGS-optinfo-emit-json.o): Add -DTARGET_NAME as per toplev.o.
	* cfgloop.c (get_loop_location): Convert return type from
	location_t to dump_user_location_t, replacing INSN_LOCATION lookups
	by implicit construction from rtx_insn *, and using
	dump_user_location_t::from_function_decl for the fallback case.
	* cfgloop.h (get_loop_location): Convert return type from
	location_t to dump_user_location_t.
	* cgraph.c (cgraph_node::get_body): Replace assignment to
	"dump_file" with call to set_dump_file.
	* cgraphunit.c (walk_polymorphic_call_targets): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	* common.opt (fremarks): New option.
	(fdiagnostics-show-remark-hotness): New option.
	(fdiagnostics-show-remark-origin): New option.
	(fdiagnostics-show-remark-pass): New option.
	(fsave-optimization-record): New option.
	* coretypes.h  (class symtab_node): New forward declaration.
	(struct cgraph_node): Likewise.
	(class varpool_node): Likewise.
	(struct kv_pair): Move here from dumpfile.c.
	* coverage.c (get_coverage_counts): Update calls to
	dump_printf_loc to pass in dump_location_t rather than a
	location_t.
	* 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.
	(-fno-diagnostics-show-remark-hotness): New option.
	(-fno-diagnostics-show-remark-origin): New option.
	(-fno-diagnostics-show-remark-pass): New option.
	* dump-context.h: New file.
	* dumpfile.c: Include "optinfo.h", "cgraph.h",
	"optinfo-emit-json.h", "optinfo-internal.h", "backend.h",
	"gimple.h", "rtl.h", "dump-context.h", "tree-pass.h", "selftest.h"
	(alt_dump_file): Make static, and group with...
	(alt_flags): ...this definition.
	(dumps_are_enabled): New variable.
	(refresh_dumps_are_enabled): New function.
	(set_dump_file): New function.
	(set_alt_dump_file): New function.
	(struct kv_pair): Move from here to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_user_location_t::dump_user_location_t): New ctors
	(dump_user_location_t::from_function_decl): New function.
	(dump_loc): Make static.  Add indentation based on scope depth.
	(dump_context::~dump_context): New dtor.
	(dump_gimple_stmt): Move implementation to...
	(dump_context::dump_gimple_stmt): ...this new method.  Add the stmt
	to any pending optinfo, creating one if need be.
	(dump_gimple_stmt_loc): Move implementation to...
	(dump_context::dump_gimple_stmt_loc): ...this new method.  Convert
	param "loc" from location_t to const dump_location_t &.  Start a
	new optinfo and add the stmt to it.
	(dump_generic_expr): Move implementation to...
	(dump_context::dump_generic_expr): ...this new method.  Add the
	tree to any pending optinfo, creating one if need be.
	(dump_generic_expr_loc): Delete.
	(dump_printf): Move implementation to...
	(dump_context::dump_printf_va): ...this new method.  Add the
	text to any pending optinfo, creating one if need be.
	(dump_printf_loc): Move implementation to...
	(dump_context::dump_printf_loc_va): ...this new method.  Convert
	param "loc" from location_t to const dump_location_t &.  Start a
	new optinfo and add the stmt to it.
	(dump_dec): Move implementation to...
	(dump_context::dump_dec): ...this new method.  Add the value to
	any pending optinfo, creating one if need be.
	(dump_context::dump_symtab_node): New method.
	(dump_context::get_scope_depth): New method.
	(dump_context::begin_scope): New method.
	(dump_context::end_scope): New method.
	(dump_context::ensure_pending_optinfo): New method.
	(dump_context::begin_next_optinfo): New method.
	(dump_context::end_any_optinfo): New method.
	(dump_context::s_current): New global.
	(dump_context::s_default): New global.
	(dump_symtab_node): New function.
	(get_dump_scope_depth): New function.
	(dump_begin_scope): New function.
	(dump_end_scope): New function.
	(gcc::dump_manager::dump_start): Replace assignments to
	"dump_file" and "alt_dump_file" with call to set_dump_file and
	set_alt_dump_file.
	(gcc::dump_manager::dump_finish): Likewise.
	(selftest::temp_dump_context::temp_dump_context): New ctor.
	(selftest::temp_dump_context::~temp_dump_context): New dtor.
	(selftest::test_impl_location): New test.
	(selftest::assert_is_text): New support function.
	(selftest::assert_is_tree): New support function.
	(selftest::assert_is_gimple): New support function.
	(selftest::test_capture_of_dump_calls): New test.
	(selftest::dumpfile_c_tests): New function.
	* dumpfile.h: Include "profile-count.h".
	(dump_file, dump_flags, dump_file_name): Move decls to top of
	file, to split them out from the "Structured dumping" API.
	(set_dump_file): New function decl.
	(dumps_are_enabled): New variable decl.
	(dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
	global.
	(class dump_user_location_t): New class.
	(struct dump_impl_location_t): New struct.
	(class dump_location_t): New class.
	(dump_printf_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	(dump_generic_expr_loc): Delete.
	(dump_gimple_stmt_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	(dump_symtab_node): New decl.
	(get_dump_scope_depth): New decl.
	(dump_begin_scope): New decl.
	(dump_end_scope): New decl.
	(class auto_dump_scope): New class.
	(AUTO_DUMP_SCOPE): New macro.
	(dump_function, print_combine_total_stats, enable_rtl_dump_file):
	Move these decls, to split them out from the "Structured dumping"
	API.
	(alt_dump_file): Delete decl.
	(optgroup_options): New decl.
	(class dump_manager): Add leading comment.
	* gimple-fold.c (fold_gimple_assign): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(gimple_fold_call): Likewise.
	* gimple-loop-interchange.cc
	(loop_cand::analyze_iloop_reduction_var): Update for change to
	check_reduction_path.
	(tree_loop_interchange::interchange): Update for change to
	find_loop_location.  Add a usage of AUTO_DUMP_SCOPE.
	* gimple-pretty-print.c (gimple_dump_bb_buff): Make non-static.
	* gimple-pretty-print.h (gimple_dump_bb_buff): New decl.
	* graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
	change in return-type of find_loop_location.
	(graphite_regenerate_ast_isl): Likewise.
	* graphite-optimize-isl.c (optimize_isl): Likewise.
	* graphite.c (graphite_transform_loops): Update for change in
	return-type of find_loop_location.
	* ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
	pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
	(ipa_make_edge_direct_to_target): Likewise.
	* ipa.c (walk_polymorphic_call_targets): Likewise.
	* loop-unroll.c (report_unroll): Convert "locus" param from
	location_t to dump_location_t.
	(decide_unrolling): Update for change to get_loop_location's
	return type.
	* omp-grid.c (struct grid_prop): Convert field "target_loc" from
	location_t to dump_user_location_t.
	(grid_find_single_omp_among_assignments_1): Updates calls to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(grid_parallel_clauses_gridifiable): Convert "tloc" from
	location_t to dump_location_t.  Updates calls to dump_printf_loc
	to pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	(grid_inner_loop_gridifiable_p): Likewise.
	(grid_dist_follows_simple_pattern): Likewise.
	(grid_gfor_follows_tiling_pattern): Likewise.
	(grid_target_follows_gridifiable_pattern): Likewise.
	(grid_attempt_target_gridification): Convert initialization
	of local "grid" from memset to zero-initialization; FIXME: does
	this require C++11?  Update call to dump_printf_loc to pass in a
	optinfo_location rather than a location_t, via the gimple stmt.
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.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.
	* passes.c: Include "optinfo.h" and "optinfo-emit-json.h".
	(execute_optinfo_function_dump): New function.
	(execute_one_ipa_transform_pass): Add per-function call to
	execute_optinfo_function_dump.
	(execute_one_pass): Likewise.
	* pretty-print.c (test_pp_format): Move save and restore of quotes
	to class auto_fix_quotes, and add an instance.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* profile.c (read_profile_edge_counts): Updates call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t
	(compute_branch_probabilities): Likewise.
	* selftest-run-tests.c (selftest::run_tests): Call
	dumpfile_c_tests, optinfo_cc_tests,
	optinfo_emit_diagnostics_cc_tests, and optinfo_emit_json_cc_tests.
	* selftest.c: Include "intl.h".
	(selftest::auto_fix_quotes::auto_fix_quotes): New ctor.
	(selftest::auto_fix_quotes::~auto_fix_quotes): New dtor.
	* selftest.h (selftest::auto_fix_quotes): New class.
	(dumpfile_c_tests): New decl.
	(optinfo_cc_tests): New decl.
	(optinfo_emit_diagnostics_cc_tests): New decl.
	(optinfo_emit_json_cc_tests): New decl.
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_start and
	optimization_records_finish.
	* tree-loop-distribution.c (pass_loop_distribution::execute):
	Update for change in return type of find_loop_location.
	* tree-nested.c (lower_nested_functions): Replace assignments to
	"dump_file" with calls to set_dump_file.
	* tree-parloops.c (parallelize_loops): Update for change in return
	type of find_loop_location.
	* tree-ssa-live.c: Include "optinfo.h".
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
	* tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
	"locus" from location_t to dump_user_location_t.
	(canonicalize_loop_induction_variables): Likewise.
	* tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
	for change in return type of find_loop_location.
	* tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
	to dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the stmt.
	* tree-vect-loop-manip.c (find_loop_location): Convert return
	type from source_location to dump_user_location_t.
	(vect_do_peeling): Update for above change.
	(vect_loop_versioning): Update for change in type of
	vect_location.
	* tree-vect-loop.c (check_reduction_path): Convert "loc" param
	from location_t to dump_user_location_t.
	(vect_estimate_min_profitable_iters): Update for change in type
	of vect_location.
	* tree-vect-slp.c (vect_print_slp_tree): Convert param "loc" from
	location_t to dump_location_t.
	(vect_slp_bb): Update for change in type of vect_location.
	* tree-vectorizer.c (vect_location): Convert from source_location
	to dump_user_location_t.
	(vectorize_loops): Update for change in vect_location's type.  Add
	top-level DUMP_VECT_SCOPE.
	(increase_alignment): Update for change in vect_location's type.
	* tree-vectorizer.h: Include "optinfo.h".
	(vect_location): Convert from source_location to
	dump_user_location_t.
	(DUMP_VECT_SCOPE): Convert to usage of AUTO_DUMP_SCOPE.
	(find_loop_location): Convert return type from source_location to
	dump_user_location_t.
	(check_reduction_path): Convert 1st param from location_t to
	dump_user_location_t.
	* value-prof.c (check_counter): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t; update
	call to error_at for change in type of "locus".
	(check_ic_target): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t, via the
	call_stmt.

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                              |   4 +
 gcc/cfgloop.c                                |  12 +-
 gcc/cfgloop.h                                |   2 +-
 gcc/cgraph.c                                 |   4 +-
 gcc/cgraphunit.c                             |   3 +-
 gcc/common.opt                               |  21 +
 gcc/coretypes.h                              |  15 +
 gcc/coverage.c                               |  22 +-
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 +
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  83 ++-
 gcc/dump-context.h                           | 112 ++++
 gcc/dumpfile.c                               | 648 +++++++++++++++++++---
 gcc/dumpfile.h                               | 258 ++++++++-
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gimple-fold.c                            |   6 +-
 gcc/gimple-loop-interchange.cc               |   7 +-
 gcc/gimple-pretty-print.c                    |   2 +-
 gcc/gimple-pretty-print.h                    |   2 +
 gcc/graphite-isl-ast-to-gimple.c             |   4 +-
 gcc/graphite-optimize-isl.c                  |   4 +-
 gcc/graphite.c                               |   2 +-
 gcc/ipa-devirt.c                             |   3 +-
 gcc/ipa-prop.c                               |  10 +-
 gcc/ipa.c                                    |   9 +-
 gcc/loop-unroll.c                            |   4 +-
 gcc/omp-grid.c                               |  47 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 +
 gcc/optinfo-emit-json.cc                     | 773 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  39 ++
 gcc/optinfo-internal.h                       | 145 +++++
 gcc/optinfo.cc                               | 254 +++++++++
 gcc/optinfo.h                                | 147 +++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/passes.c                                 |  17 +
 gcc/pretty-print.c                           |   9 +-
 gcc/profile-count.c                          |  28 +
 gcc/profile-count.h                          |   5 +
 gcc/profile.c                                |  14 +-
 gcc/selftest-run-tests.c                     |   4 +
 gcc/selftest.c                               |  20 +
 gcc/selftest.h                               |  24 +
 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 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-loop-distribution.c                 |   2 +-
 gcc/tree-nested.c                            |   4 +-
 gcc/tree-parloops.c                          |   3 +-
 gcc/tree-ssa-live.c                          |   4 +-
 gcc/tree-ssa-loop-ivcanon.c                  |   8 +-
 gcc/tree-ssa-loop-ivopts.c                   |   2 +-
 gcc/tree-ssa-loop-niter.c                    |   2 +-
 gcc/tree-ssa-sccvn.c                         |   3 +-
 gcc/tree-vect-loop-manip.c                   |  16 +-
 gcc/tree-vect-loop.c                         |   8 +-
 gcc/tree-vect-slp.c                          |   5 +-
 gcc/tree-vectorizer.c                        |  17 +-
 gcc/tree-vectorizer.h                        |  25 +-
 gcc/value-prof.c                             |  15 +-
 66 files changed, 3229 insertions(+), 230 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.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 9b85787..23060a3 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1421,6 +1421,9 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
+	optinfo-emit-diagnostics.o \
+	optinfo-emit-json.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
@@ -2248,6 +2251,7 @@ s-bversion: BASE-VER
 	$(STAMP) s-bversion
 
 CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
 
 pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
 		    $(srcdir)/gen-pass-instances.awk
diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c
index 8af793c..e27cd39 100644
--- a/gcc/cfgloop.c
+++ b/gcc/cfgloop.c
@@ -1800,7 +1800,7 @@ loop_exits_from_bb_p (struct loop *loop, basic_block bb)
 
 /* Return location corresponding to the loop control condition if possible.  */
 
-location_t
+dump_user_location_t
 get_loop_location (struct loop *loop)
 {
   rtx_insn *insn = NULL;
@@ -1819,7 +1819,7 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* If loop has a single exit, then the loop control branch
@@ -1829,24 +1829,24 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (exit->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* Next check the latch, to see if it is non-empty.  */
   FOR_BB_INSNS_REVERSE (loop->latch, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* Finally, if none of the above identifies the loop control branch,
      return the first location in the loop header.  */
   FOR_BB_INSNS (loop->header, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* If all else fails, simply return the current function location.  */
-  return DECL_SOURCE_LOCATION (current_function_decl);
+  return dump_user_location_t::from_function_decl (current_function_decl);
 }
 
 /* Records that every statement in LOOP is executed I_BOUND times.
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index af9bfab..80a31c4 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -357,7 +357,7 @@ extern bool loop_exit_edge_p (const struct loop *, const_edge);
 extern bool loop_exits_to_bb_p (struct loop *, basic_block);
 extern bool loop_exits_from_bb_p (struct loop *, basic_block);
 extern void mark_loop_exit_edges (void);
-extern location_t get_loop_location (struct loop *loop);
+extern dump_user_location_t get_loop_location (struct loop *loop);
 
 /* Loops & cfg manipulation.  */
 extern basic_block *get_loop_body (const struct loop *);
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 3899467..d19f1aa 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
       const char *saved_dump_file_name = dump_file_name;
       dump_flags_t saved_dump_flags = dump_flags;
       dump_file_name = NULL;
-      dump_file = NULL;
+      set_dump_file (NULL);
 
       push_cfun (DECL_STRUCT_FUNCTION (decl));
       execute_all_ipa_transforms ();
@@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
       updated = true;
 
       current_pass = saved_current_pass;
-      dump_file = saved_dump_file;
+      set_dump_file (saved_dump_file);
       dump_file_name = saved_dump_file_name;
       dump_flags = saved_dump_flags;
     }
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 04b6919..7cfb8a0 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 	    }
           if (dump_enabled_p ())
             {
-	      location_t locus = gimple_location_safe (edge->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->name (), target->name ());
 	    }
diff --git a/gcc/common.opt b/gcc/common.opt
index 4aebcaf..141f5dd 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -506,6 +506,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
 
@@ -1273,6 +1278,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.
@@ -1942,6 +1959,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..33f3d21 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@ struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node_def, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
@@ -325,6 +332,14 @@ namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 84fff13..350cc45 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -342,12 +342,16 @@ get_coverage_counts (unsigned counter, unsigned expected,
       static int warned = 0;
 
       if (!warned++ && dump_enabled_p ())
-	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
-                         (flag_guess_branch_prob
-                          ? "file %s not found, execution counts estimated\n"
-                          : "file %s not found, execution counts assumed to "
-                            "be zero\n"),
-                         da_file_name);
+	{
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			   (flag_guess_branch_prob
+			    ? "file %s not found, execution counts estimated\n"
+			    : "file %s not found, execution counts assumed to "
+			    "be zero\n"),
+			   da_file_name);
+	}
       return NULL;
     }
   if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
@@ -378,7 +382,9 @@ get_coverage_counts (unsigned counter, unsigned expected,
 		    "its profile data (counter %qs)", id, ctr_names[counter]);
       if (warning_printed && dump_enabled_p ())
 	{
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                            "use -Wno-error=coverage-mismatch to tolerate "
                            "the mismatch but performance may drop if the "
                            "function is hot\n");
@@ -386,7 +392,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
 	  if (!seen_error ()
 	      && !warned++)
 	    {
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                                "coverage mismatch ignored\n");
 	      dump_printf (MSG_OPTIMIZED_LOCATIONS,
                            flag_guess_branch_prob
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 b06ea6e..7454ae5 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
@@ -470,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
@@ -9904,6 +9911,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
@@ -12037,6 +12053,71 @@ 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.
+
+Rather than attempting to parse the textual remark format, consider
+using @option{-fsave-optimization-record}, which works from the same
+data internally.
+
+@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/dump-context.h b/gcc/dump-context.h
new file mode 100644
index 0000000..2986317
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,112 @@
+/* Support code for handling the various dump_* calls in dumpfile.h
+   Copyright (C) 2018 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+
+#ifndef GCC_DUMP_CONTEXT_H
+#define GCC_DUMP_CONTEXT_H 1
+
+/* A class for handling the various dump_* calls.
+
+   In particular, this class has responsibility for consolidating
+   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
+   calls), and emitting them.
+
+   Putting this in a class (rather than as global state) allows
+   for selftesting of this code.  */
+
+class dump_context
+{
+  friend class temp_dump_context;
+ public:
+  static dump_context &get () { return *s_current; }
+
+  ~dump_context ();
+
+  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
+			     const dump_location_t &loc,
+			     dump_flags_t extra_dump_flags,
+			     gimple *gs, int spc);
+
+  void dump_generic_expr (dump_flags_t dump_kind,
+			  dump_flags_t extra_dump_flags,
+			  tree t);
+
+  void dump_printf_va (dump_flags_t dump_kind, const char *format,
+		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
+
+  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+			   const char *format, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0);
+
+  template<unsigned int N, typename C>
+  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
+
+  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
+
+  /* Managing nested scopes.  */
+  unsigned int get_scope_depth () const;
+  void begin_scope (const char *name, const dump_location_t &loc);
+  void end_scope ();
+
+ private:
+  optinfo &ensure_pending_optinfo ();
+  optinfo &begin_next_optinfo (const dump_location_t &loc);
+  void end_any_optinfo ();
+
+  /* The current nesting depth of dump scopes, for showing nesting
+     via indentation).  */
+  unsigned int m_scope_depth;
+
+  /* The optinfo currently being accumulated since the last dump_*_loc call,
+     if any.  */
+  optinfo *m_pending;
+
+  /* The currently active dump_context, for use by the dump_* API calls.  */
+  static dump_context *s_current;
+
+  /* The default active context.  */
+  static dump_context s_default;
+};
+
+#if CHECKING_P
+
+/* An RAII class for use in selftests for temporarily using a different
+   dump_context.  */
+
+class temp_dump_context
+{
+ public:
+  temp_dump_context (bool new_flag_remarks);
+  ~temp_dump_context ();
+
+  /* Support for selftests.  */
+  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+
+ private:
+  dump_context m_context;
+  dump_context *m_saved;
+  bool m_saved_flag_remarks;
+};
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 2f11284..3be4af5 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -29,6 +29,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "tree-cfg.h"
 #include "langhooks.h"
+#include "optinfo.h"
+#include "cgraph.h" /* for selftests.  */
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h" /* for selftests.  */
+#include "backend.h"
+#include "gimple.h" /* for dump_user_location_t ctor.  */
+#include "rtl.h" /* for dump_user_location_t ctor.  */
+#include "dump-context.h"
+#include "tree-pass.h" /* for "current_pass".  */
+#include "selftest.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -36,18 +46,52 @@ along with GCC; see the file COPYING3.  If not see
    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
 
 static dump_flags_t pflags;		      /* current dump_flags */
-static dump_flags_t alt_flags;		      /* current opt_info flags */
 
 static void dump_loc (dump_flags_t, FILE *, source_location);
+
+/* Current -fopt-info output stream, if any, and flags.  */
+static FILE *alt_dump_file = NULL;
+static dump_flags_t alt_flags;
+
 static FILE *dump_open_alternate_stream (struct dump_file_info *);
 
 /* These are currently used for communicating between passes.
    However, instead of accessing them directly, the passes can use
    dump_printf () for dumps.  */
 FILE *dump_file = NULL;
-FILE *alt_dump_file = NULL;
 const char *dump_file_name;
 dump_flags_t dump_flags;
+bool dumps_are_enabled = false;
+
+
+/* Update the "dumps_are_enabled" global; to be called whenever dump_file
+   or alt_dump_file change.  */
+
+static void
+refresh_dumps_are_enabled ()
+{
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
+}
+
+/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+void
+set_dump_file (FILE *new_dump_file)
+{
+  dump_file = new_dump_file;
+  refresh_dumps_are_enabled ();
+}
+
+/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+static void
+set_alt_dump_file (FILE *new_alt_dump_file)
+{
+  alt_dump_file = new_alt_dump_file;
+  refresh_dumps_are_enabled ();
+}
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
   {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
@@ -74,14 +118,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[] =
@@ -132,7 +168,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},
@@ -358,9 +394,53 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
   return stream;
 }
 
+/* Implementation of "structured dumping API".  */
+
+/* Construct a dump_user_location_t from STMT (using its location and
+   hotness).  */
+
+dump_user_location_t::dump_user_location_t (gimple *stmt)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (stmt)
+    {
+      if (stmt->bb)
+	m_count = stmt->bb->count;
+      m_loc = gimple_location (stmt);
+    }
+}
+
+/* Construct a dump_user_location_t from an RTL instruction (using its
+   location and hotness).  */
+dump_user_location_t::dump_user_location_t (rtx_insn *insn)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (insn)
+    {
+      basic_block bb = BLOCK_FOR_INSN (insn);
+      if (bb)
+	m_count = bb->count;
+      m_loc = INSN_LOCATION (insn);
+    }
+}
+
+/* Construct from a function declaration.  This one requires spelling out
+   to avoid accidentally constructing from other kinds of tree.  */
+
+dump_user_location_t
+dump_user_location_t::from_function_decl (tree fndecl)
+{
+  gcc_assert (fndecl);
+
+  // FIXME: profile count for function?
+  return dump_user_location_t (profile_count (),
+			       DECL_SOURCE_LOCATION (fndecl));
+}
+
+
 /* 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)
@@ -373,137 +453,363 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
                  DECL_SOURCE_FILE (current_function_decl),
                  DECL_SOURCE_LINE (current_function_decl),
                  DECL_SOURCE_COLUMN (current_function_decl));
+      /* Indentation based on scope depth.  */
+      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
     }
 }
 
+/* Implementation of dump_context methods.  */
+
+/* dump_context's dtor.  */
+
+dump_context::~dump_context ()
+{
+  delete m_pending;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
 void
-dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
+				dump_flags_t extra_dump_flags,
+				gimple *gs, int spc)
 {
   if (dump_file && (dump_kind & pflags))
     print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs, int spc)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
    DUMP_KIND is enabled.  */
 
 void
-dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		   tree t)
+dump_context::dump_generic_expr (dump_flags_t dump_kind,
+				 dump_flags_t extra_dump_flags,
+				 tree t)
 {
   if (dump_file && (dump_kind & pflags))
       print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
-}
 
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, extra_dump_flags);
+    }
+}
 
-/* Similar to dump_generic_expr, except additionally print the source
-   location.  */
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
+			      va_list ap)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Output a formatted message using FORMAT on appropriate dump streams.  */
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+				  const char *format, va_list ap)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      dump_loc (dump_kind, dump_file, srcloc);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* Output VALUE in decimal to appropriate dump streams.  */
 
+template<unsigned int N, typename C>
 void
-dump_printf_loc (dump_flags_t dump_kind, source_location loc,
-		 const char *format, ...)
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
+  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
   if (dump_file && (dump_kind & pflags))
+    print_dec (value, dump_file, sgn);
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
     {
-      va_list ap;
-      dump_loc (dump_kind, dump_file, loc);
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
     }
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", node->dump_name ());
 
   if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", node->dump_name ());
+
+  if (optinfo_enabled_p ())
     {
-      va_list ap;
-      dump_loc (dump_kind, alt_dump_file, loc);
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
     }
 }
 
+/* Get the current dump scope-nesting depth.
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+dump_context::get_scope_depth () const
+{
+  return m_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Push a "scope" opinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_context::begin_scope (const char *name, const dump_location_t &loc)
+{
+  /* Specialcase, to avoid going through dump_printf_loc,
+     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
+
+  if (dump_file)
+    {
+      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      fprintf (dump_file, "=== %s ===\n", name);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      end_any_optinfo ();
+      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
+      info.add_printf ("=== %s ===", name);
+      info.emit ();
+    }
+
+  m_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_context::end_scope ()
+{
+  end_any_optinfo ();
+  m_scope_depth--;
+  optimization_records_maybe_pop_dump_scope ();
+}
+
+/* Return the optinfo currently being accumulated, creating one if
+   necessary.  */
+
+optinfo &
+dump_context::ensure_pending_optinfo ()
+{
+  if (!m_pending)
+    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
+  return *m_pending;
+}
+
+/* Start a new optinfo and return it, ending any optinfo that was already
+   accumulated.  */
+
+optinfo &
+dump_context::begin_next_optinfo (const dump_location_t &loc)
+{
+  end_any_optinfo ();
+  gcc_assert (m_pending == NULL);
+  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
+  return *m_pending;
+}
+
+/* End any optinfo that has been accumulated within this context, emitting
+   it as a diagnostic remark or to optimization records as appropriate.  */
+
+void
+dump_context::end_any_optinfo ()
+{
+  if (m_pending)
+    m_pending->emit ();
+  delete m_pending;
+  m_pending = NULL;
+}
+
+/* The current singleton dump_context, and its default.  */
+
+dump_context *dump_context::s_current = &dump_context::s_default;
+dump_context dump_context::s_default;
+
+/* Implementation of dump_* API calls, calling into dump_context
+   methods.  */
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_stmt, except additionally print source location.  */
+
+void
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
+   DUMP_KIND is enabled.  */
+
+void
+dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		   tree t)
+{
+  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
+}
+
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
+void
+dump_printf (dump_flags_t dump_kind, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  va_end (ap);
+}
+
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
+
+void
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		 const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  va_end (ap);
+}
+
 /* Output VALUE in decimal to appropriate dump streams.  */
 
 template<unsigned int N, typename C>
 void
 dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
-  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
-  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
-  if (dump_file && (dump_kind & pflags))
-    print_dec (value, dump_file, sgn);
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    print_dec (value, alt_dump_file, sgn);
+  dump_context::get ().dump_dec (dump_kind, value);
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -512,6 +818,45 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  dump_context::get ().dump_symtab_node (dump_kind, node);
+}
+
+/* Get the current dump scope-nesting depth.
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+get_dump_scope_depth ()
+{
+  return dump_context::get ().get_scope_depth ();
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Push a "scope" opinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_begin_scope (const char *name, const dump_location_t &loc)
+{
+  dump_context::get ().begin_scope (name, loc);
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_end_scope ()
+{
+  dump_context::get ().end_scope ();
+}
+
+/* End of implementation of "structured dumping API".  */
+
 /* Start a dump for PHASE. Store user-supplied dump flags in
    *FLAG_PTR.  Return the number of streams opened.  Set globals
    DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
@@ -541,7 +886,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
         }
       free (name);
       dfi->pstream = stream;
-      dump_file = dfi->pstream;
+      set_dump_file (dfi->pstream);
       /* Initialize current dump flags. */
       pflags = dfi->pflags;
     }
@@ -551,7 +896,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
     {
       dfi->alt_stream = stream;
       count++;
-      alt_dump_file = dfi->alt_stream;
+      set_alt_dump_file (dfi->alt_stream);
       /* Initialize current -fopt-info flags. */
       alt_flags = dfi->alt_flags;
     }
@@ -582,8 +927,8 @@ dump_finish (int phase)
 
   dfi->alt_stream = NULL;
   dfi->pstream = NULL;
-  dump_file = NULL;
-  alt_dump_file = NULL;
+  set_dump_file (NULL);
+  set_alt_dump_file (NULL);
   dump_flags = TDF_NONE;
   alt_flags = TDF_NONE;
   pflags = TDF_NONE;
@@ -1021,6 +1366,7 @@ dump_basic_block (dump_flags_t dump_kind, basic_block bb, int indent)
     dump_bb (dump_file, bb, indent, TDF_DETAILS);
   if (alt_dump_file && (dump_kind & alt_flags))
     dump_bb (alt_dump_file, bb, indent, TDF_DETAILS);
+  // TODO: should this also write to optinfo?
 }
 
 /* Dump FUNCTION_DECL FN as tree dump PHASE.  */
@@ -1059,3 +1405,179 @@ enable_rtl_dump_file (void)
 			    NULL);
   return num_enabled > 0;
 }
+
+#if CHECKING_P
+
+/* temp_dump_context's ctor.  Temporarily override the dump_context,
+   and the value of "flag_remarks" (to forcibly enable optinfo-generation).  */
+
+temp_dump_context::temp_dump_context (bool new_flag_remarks)
+: m_context (),
+  m_saved (&dump_context ().get ()),
+  m_saved_flag_remarks (flag_remarks)
+{
+  dump_context::s_current = &m_context;
+  flag_remarks = new_flag_remarks;
+}
+
+/* temp_dump_context's dtor.  Restore the saved values of dump_context and
+   "flag_remarks".  */
+
+temp_dump_context::~temp_dump_context ()
+{
+  dump_context::s_current = m_saved;
+  flag_remarks = m_saved_flag_remarks;
+}
+
+namespace selftest {
+
+/* Verify that the dump_location_t constructor captures the source location
+   at which it was called (provided that the build compiler is sufficiently
+   recent).  */
+
+static void
+test_impl_location ()
+{
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+  /* Default ctor.  */
+  {
+    dump_location_t loc;
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from a gimple.  */
+  {
+    dump_location_t loc ((gimple *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from an rtx_insn.  */
+  {
+    dump_location_t loc ((rtx_insn *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+#endif
+}
+
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+static void
+assert_is_text (const optinfo_item *item, const char *expected_text)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
+  const optinfo_item_text * text_item
+    = static_cast <const optinfo_item_text *> (item);
+  ASSERT_STREQ (text_item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+static void
+assert_is_tree (const optinfo_item *item, tree expected_tree,
+		dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
+  const optinfo_item_tree * tree_item
+    = static_cast <const optinfo_item_tree *> (item);
+  ASSERT_EQ (tree_item->get_node (), expected_tree);
+  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+static void
+assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
+		  dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
+  const optinfo_item_gimple * gimple_item
+    = static_cast <const optinfo_item_gimple *> (item);
+  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
+  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls ()
+{
+  dump_location_t loc;
+
+  /* Tree.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    assert_is_text (info->get_item (0), "test of tree: ");
+    assert_is_tree (info->get_item (1), integer_zero_node, TDF_SLIM);
+  }
+
+  /* Gimple.  */
+  {
+    greturn *stmt = gimple_build_return (NULL);
+
+    temp_dump_context tmp (true);
+    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
+  }
+
+  /* poly_int.  */
+  {
+    temp_dump_context tmp (true);
+    dump_dec (MSG_NOTE, poly_int64 (42));
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_text (info->get_item (0), "42");
+  }
+
+  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
+     above.  */
+  {
+    /* MSG_OPTIMIZED_LOCATIONS.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_SUCCESS);
+    }
+
+    /* MSG_MISSED_OPTIMIZATION.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_FAILURE);
+    }
+  }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+dumpfile_c_tests ()
+{
+  test_impl_location ();
+  test_capture_of_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index f6ad670..96bdb30 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DUMPFILE_H
 #define GCC_DUMPFILE_H 1
 
+#include "profile-count.h"
 
 /* Different tree dump places.  When you add new tree dump places,
    extend the DUMP_FILES array in dumpfile.c.  */
@@ -273,22 +274,247 @@ extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
+
+/* Global variables used to communicate with passes.  */
+extern FILE *dump_file;
+extern dump_flags_t dump_flags;
+extern const char *dump_file_name;
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
+
+/* Return true if any of the dumps are enabled, false otherwise. */
+static inline bool
+dump_enabled_p (void)
+{
+  return __builtin_expect (dumps_are_enabled, false);
+}
+
+/* "Structured dumping" API.
+
+   The following API calls (which *don't* take a "FILE *")
+   write the output to zero or more locations:
+   (a) the active dump_file, if any
+   (b) the -fopt-info destination, if any
+   (c) to the "optinfo" destinations, if any:
+     (c.1) as diagnostic "remarks"
+     (c.2) as optimization records
+   For optinfos, the dump_*_loc mark the beginning of an optinfo
+   instance: all subsequent dump_* calls are consolidated into
+   that optinfo, until the next dump_*_loc call (or a change in
+   dump scope).
+
+   A group of dump_* calls should be guarded by:
+
+     if (dump_enabled_p ())
+
+   to minimize the work done for the common case where dumps
+   are disabled.  */
+
+/* A class for describing where in the user's source that a dump message
+   relates to, with various constructors for convenience.
+   In particular, this lets us associate dump messages
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.  */
+
+class dump_user_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_user_location_t () : m_count (), m_loc (UNKNOWN_LOCATION) {}
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_user_location_t (gimple *stmt);
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_user_location_t (rtx_insn *insn);
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information); it thus needs to be spelled out.  */
+  static dump_user_location_t
+  from_location_t (location_t loc)
+  {
+    return dump_user_location_t (profile_count (), loc);
+  }
+
+  /* Construct from a function declaration.  This one requires spelling out
+     to avoid accidentally constructing from other kinds of tree.  */
+  static dump_user_location_t
+  from_function_decl (tree fndecl);
+
+  profile_count get_count () const { return m_count; }
+  location_t get_location_t () const { return m_loc; }
+
+ private:
+  /* Private ctor from count and location, for use by from_location_t.  */
+  dump_user_location_t (profile_count count, location_t loc)
+    : m_count (count), m_loc (loc)
+  {}
+
+  profile_count m_count;
+  location_t m_loc;
+};
+
+/* A class for identifying where in the compiler's own source
+   (or a plugin) that a dump message is being emitted from.  */
+
+struct dump_impl_location_t
+{
+  dump_impl_location_t (
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+			const char *file = __builtin_FILE (),
+			int line = __builtin_LINE (),
+			const char *function = __builtin_FUNCTION ()
+#else
+			const char *file = __FILE__,
+			int line = __LINE__,
+			const char *function = NULL
+#endif
+  )
+  : m_file (file), m_line (line), m_function (function)
+  {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* A bundle of information for describing the location of a dump message:
+   (a) the source location and hotness within the user's code, together with
+   (b) the source location within the compiler/plugin.
+
+   The constructors use default parameters so that (b) gets sets up
+   automatically.
+
+   The upshot is that you can pass in e.g. a gimple * to dump_printf_loc,
+   and the dump call will automatically record where in GCC's source
+   code the dump was emitted from.  */
+
+class dump_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_location_t (const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t ()),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_location_t (gimple *stmt,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (stmt)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_location_t (rtx_insn *insn,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (insn)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a dump_user_location_t.  */
+  dump_location_t (const dump_user_location_t &user_location,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (user_location),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information), and thus requires spelling out.  */
+  static dump_location_t
+  from_location_t (location_t loc,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  {
+    return dump_location_t (dump_user_location_t::from_location_t (loc),
+			    impl_location);
+  }
+
+  const dump_user_location_t &
+  get_user_location () const { return m_user_location; }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_impl_location; }
+
+  location_t get_location_t () const
+  {
+    return m_user_location.get_location_t ();
+  }
+
+  profile_count get_count () const { return m_user_location.get_count (); }
+
+ private:
+  dump_user_location_t m_user_location;
+  dump_impl_location_t m_impl_location;
+};
+
 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_function (int phase, tree fn);
+extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
+			     const char *, ...) ATTRIBUTE_PRINTF_3;
 extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
 extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
-				  gimple *, int);
+extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
+				  dump_flags_t, gimple *, int);
 extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
 
 template<unsigned int N, typename C>
 void dump_dec (dump_flags_t, const poly_int<N, C> &);
 
+/* Managing nested scopes, so that dumps can express the call chain
+   leading to a dump message.  */
+
+extern unsigned int get_dump_scope_depth ();
+extern void dump_begin_scope (const char *name, const dump_location_t &loc);
+extern void dump_end_scope ();
+
+/* Implementation detail of the AUTO_DUMP_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit dump
+   information about entering and exiting a collection of nested
+   function calls.  */
+
+class auto_dump_scope
+{
+ public:
+  auto_dump_scope (const char *name, dump_location_t loc)
+  {
+    if (dump_enabled_p ())
+      dump_begin_scope (name, loc);
+  }
+  ~auto_dump_scope ()
+  {
+    if (dump_enabled_p ())
+      dump_end_scope ();
+  }
+};
+
+/* A macro for calling:
+     dump_begin_scope (NAME, LOC);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
+
+#define AUTO_DUMP_SCOPE(NAME, LOC) \
+  auto_dump_scope scope (NAME, LOC)
+
+/* End of "structured dumping" API.  */
+
+extern void dump_function (int phase, tree fn);
+extern void print_combine_total_stats (void);
+extern bool enable_rtl_dump_file (void);
+
 /* In tree-dump.c  */
 extern void dump_node (const_tree, dump_flags_t, FILE *);
 
@@ -297,21 +523,13 @@ extern void dump_combine_total_stats (FILE *);
 /* In cfghooks.c  */
 extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
 
-/* Global variables used to communicate with passes.  */
-extern FILE *dump_file;
-extern FILE *alt_dump_file;
-extern dump_flags_t dump_flags;
-extern const char *dump_file_name;
-
-/* Return true if any of the dumps is enabled, false otherwise. */
-static inline bool
-dump_enabled_p (void)
-{
-  return (dump_file || alt_dump_file);
-}
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
 
 namespace gcc {
 
+/* A class for managing all of the various dump files used by the
+   optimization passes.  */
+
 class dump_manager
 {
 public:
diff --git a/gcc/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/gimple-fold.c b/gcc/gimple-fold.c
index c1d8442..c023274 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -347,8 +347,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 		  {
 		    if (dump_enabled_p ())
 		      {
-			location_t loc = gimple_location_safe (stmt);
-			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 					 "resolving virtual function address "
 					 "reference to function %s\n",
 					 targets.length () == 1
@@ -4060,8 +4059,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 	      tree lhs = gimple_call_lhs (stmt);
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				   "folding virtual function call to %s\n",
 		 		   targets.length () == 1
 		  		   ? targets[0]->name ()
diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index eb35263..2230af7 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -523,7 +523,7 @@ loop_cand::analyze_iloop_reduction_var (tree var)
 
   /* Handle and verify a series of stmts feeding the reduction op.  */
   if (single_use != next_def
-      && !check_reduction_path (UNKNOWN_LOCATION, m_loop, phi, next,
+      && !check_reduction_path (dump_user_location_t (), m_loop, phi, next,
 				gimple_assign_rhs_code (single_use)))
     return false;
 
@@ -1578,7 +1578,10 @@ bool
 tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    vec<ddr_p> ddrs)
 {
-  location_t loc = find_loop_location (m_loop_nest[0]);
+  dump_user_location_t loc = find_loop_location (m_loop_nest[0]);
+
+  AUTO_DUMP_SCOPE ("tree_loop_interchange::interchange", loc);
+
   bool changed_p = false;
   /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
      The overall effect is to push inner loop to outermost level in whole
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 405d9e3..c1f28cd 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2796,7 +2796,7 @@ dump_implicit_edges (pretty_printer *buffer, basic_block bb, int indent,
 /* Dumps basic block BB to buffer BUFFER with details described by FLAGS and
    indented by INDENT spaces.  */
 
-static void
+void
 gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
 		     dump_flags_t flags)
 {
diff --git a/gcc/gimple-pretty-print.h b/gcc/gimple-pretty-print.h
index 6ae6a3b..e7a5607 100644
--- a/gcc/gimple-pretty-print.h
+++ b/gcc/gimple-pretty-print.h
@@ -34,6 +34,8 @@ extern void print_gimple_expr (FILE *, gimple *, int, dump_flags_t = TDF_NONE);
 extern void pp_gimple_stmt_1 (pretty_printer *, gimple *, int, dump_flags_t);
 extern void gimple_dump_bb (FILE *, basic_block, int, dump_flags_t);
 extern void gimple_dump_bb_for_graph (pretty_printer *, basic_block);
+extern void gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
+				 dump_flags_t flags);
 extern void dump_ssaname_info_to_file (FILE *, tree, int);
 extern void percent_G_format (text_info *);
 
diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c
index b607b12..9e78465 100644
--- a/gcc/graphite-isl-ast-to-gimple.c
+++ b/gcc/graphite-isl-ast-to-gimple.c
@@ -1409,7 +1409,7 @@ scop_to_isl_ast (scop_p scop)
   isl_ctx_set_max_operations (scop->isl_context, old_max_operations);
   if (isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -1518,7 +1518,7 @@ graphite_regenerate_ast_isl (scop_p scop)
 
   if (t.codegen_error_p ())
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
 		       "loop nest not optimized, code generation error\n");
diff --git a/gcc/graphite-optimize-isl.c b/gcc/graphite-optimize-isl.c
index 456a797..35e9ac0 100644
--- a/gcc/graphite-optimize-isl.c
+++ b/gcc/graphite-optimize-isl.c
@@ -160,7 +160,7 @@ optimize_isl (scop_p scop)
   if (!scop->transformed_schedule
       || isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -182,7 +182,7 @@ optimize_isl (scop_p scop)
 
   if (same_schedule)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_NOTE, loc,
 		       "loop nest not optimized, optimized schedule is "
diff --git a/gcc/graphite.c b/gcc/graphite.c
index bcf4828..ddf16a8 100644
--- a/gcc/graphite.c
+++ b/gcc/graphite.c
@@ -412,7 +412,7 @@ graphite_transform_loops (void)
 	changed = true;
 	if (graphite_regenerate_ast_isl (scop))
 	  {
-	    location_t loc = find_loop_location
+	    dump_user_location_t loc = find_loop_location
 	      (scops[i]->scop_info->region.entry->dest->loop_father);
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
 			     "loop nest optimized\n");
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 308b6e6..e99d8cc 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -3755,8 +3755,7 @@ ipa_devirt (void)
 	      {
 		if (dump_enabled_p ())
                   {
-                    location_t locus = gimple_location_safe (e->call_stmt);
-                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
 				     "speculatively devirtualizing call "
 				     "in %s to %s\n",
 				     n->dump_name (),
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index daada4d..bc02fd0 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -2843,8 +2843,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 	    {
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (ie->call_stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 				   "discovered direct call non-invariant %s\n",
 				   ie->caller->dump_name ());
 		}
@@ -2854,8 +2853,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 
           if (dump_enabled_p ())
 	    {
-	      location_t loc = gimple_location_safe (ie->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 			       "discovered direct call to non-function in %s, "
 			       "making it __builtin_unreachable\n",
 			       ie->caller->dump_name ());
@@ -2943,9 +2941,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
      }
   if (dump_enabled_p ())
     {
-      location_t loc = gimple_location_safe (ie->call_stmt);
-
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 		       "converting indirect call in %s to direct call to %s\n",
 		       ie->caller->name (), callee->name ());
     }
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 82fc334..3b6b5e5 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -225,13 +225,8 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
 	  if (dump_enabled_p ())
-            {
-	      location_t locus;
-	      if (edge->call_stmt)
-		locus = gimple_location (edge->call_stmt);
-	      else
-		locus = UNKNOWN_LOCATION;
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	    {
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->dump_name (),
 			       target->dump_name ());
diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c
index 5a03932..48bbda0 100644
--- a/gcc/loop-unroll.c
+++ b/gcc/loop-unroll.c
@@ -189,7 +189,7 @@ static rtx get_expansion (struct var_to_expand *);
    appropriate given the dump or -fopt-info settings.  */
 
 static void
-report_unroll (struct loop *loop, location_t locus)
+report_unroll (struct loop *loop, dump_location_t locus)
 {
   dump_flags_t report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS;
 
@@ -220,7 +220,7 @@ decide_unrolling (int flags)
   FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
     {
       loop->lpt_decision.decision = LPT_NONE;
-      location_t locus = get_loop_location (loop);
+      dump_user_location_t locus = get_loop_location (loop);
 
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, locus,
diff --git a/gcc/omp-grid.c b/gcc/omp-grid.c
index ffa301e..6edc92f 100644
--- a/gcc/omp-grid.c
+++ b/gcc/omp-grid.c
@@ -91,7 +91,7 @@ struct grid_prop
   bool tiling;
   /* Location of the target construct for optimization information
      messages.  */
-  location_t target_loc;
+  dump_user_location_t target_loc;
   /* The collapse clause of the involved loops.  Collapse value of all of them
      must be the same for gridification to take place.  */
   size_t collapse;
@@ -177,10 +177,10 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "%s construct "
 				   "contains multiple OpenMP constructs\n",
 				   name);
-		  dump_printf_loc (MSG_NOTE, gimple_location (*ret),
+		  dump_printf_loc (MSG_NOTE, *ret,
 				   "The first OpenMP construct within "
 				   "a parallel\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "The second OpenMP construct within "
 				   "a parallel\n");
 		}
@@ -195,7 +195,7 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "%s construct contains "
 			       "a complex statement\n", name);
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "gridification\n");
 	    }
@@ -286,7 +286,7 @@ grid_find_ungridifiable_statement (gimple_stmt_iterator *gsi,
    loop that is evaluated for possible gridification.  */
 
 static bool
-grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
+grid_parallel_clauses_gridifiable (gomp_parallel *par, dump_user_location_t tloc)
 {
   tree clauses = gimple_omp_parallel_clauses (par);
   while (clauses)
@@ -300,7 +300,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 			       GRID_MISSED_MSG_PREFIX "because there is "
 			       "a num_threads clause of the parallel "
 			       "construct\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a num_threads clause\n");
 	    }
 	  return false;
@@ -311,7 +311,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, tloc,
 			       GRID_MISSED_MSG_PREFIX "a reduction clause "
 			       "is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a reduction clause\n");
 	    }
 	  return false;
@@ -341,7 +341,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			   GRID_MISSED_MSG_PREFIX "the inner loop "
 			   "loop bounds computation contains a complex "
 			   "statement\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -361,7 +361,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the inner loop "
 				   "has a non-automatic schedule clause\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+		  dump_printf_loc (MSG_NOTE, gfor,
 				   "Loop construct has a non automatic "
 				   "schedule clause\n");
 		}
@@ -375,7 +375,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "a reduction "
 			       "clause is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct has a reduction schedule "
 			       "clause\n");
 	    }
@@ -404,7 +404,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			     GRID_MISSED_MSG_PREFIX "the inner loop contains "
 			     "statement %s which cannot be transformed\n",
 			     gimple_code_name[(int) gimple_code (bad)]);
-	  dump_printf_loc (MSG_NOTE, gimple_location (bad),
+	  dump_printf_loc (MSG_NOTE, bad,
 			   "This statement cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -422,7 +422,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 static bool
 grid_dist_follows_simple_pattern (gomp_for *dist, grid_prop *grid)
 {
-  location_t tloc = grid->target_loc;
+  dump_user_location_t tloc = grid->target_loc;
   gimple *stmt = grid_find_single_omp_among_assignments (gimple_omp_body (dist),
 							 grid, "distribute");
   gomp_parallel *par;
@@ -468,7 +468,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop is not "
 			   "a simple for loop\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "This statement is not a simple for loop\n");
 	}
       return false;
@@ -484,7 +484,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop does not "
 			   "have use the same collapse clause\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct uses a different collapse clause\n");
 	}
       return false;
@@ -524,7 +524,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute and "
 			       "an internal loop do not agree on tile size\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct does not seem to loop over "
 			       "a tile size\n");
 	    }
@@ -636,7 +636,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the distribute "
 				   "construct contains a try..catch region\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (try_stmt),
+		  dump_printf_loc (MSG_NOTE, try_stmt,
 				   "This statement cannot be analyzed for "
 				   "tiled gridification\n");
 		}
@@ -661,7 +661,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a call\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -677,7 +677,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a parallel "
 				   "construct contains another parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This parallel construct is nested in "
 				   "another one\n");
 		}
@@ -698,7 +698,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a loop "
 				   "construct is not nested within a parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This loop construct is not nested in "
 				   "a parallel construct\n");
 		}
@@ -714,7 +714,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a complex statement\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -734,7 +734,7 @@ grid_target_follows_gridifiable_pattern (gomp_target *target, grid_prop *grid)
   if (gimple_omp_target_kind (target) != GF_OMP_TARGET_KIND_REGION)
     return false;
 
-  location_t tloc = gimple_location (target);
+  dump_user_location_t tloc = target;
   grid->target_loc = tloc;
   gimple *stmt
     = grid_find_single_omp_among_assignments (gimple_omp_body (target),
@@ -1257,14 +1257,13 @@ grid_attempt_target_gridification (gomp_target *target,
 				   gbind *tgt_bind)
 {
   /* removed group_size */
-  grid_prop grid;
-  memset (&grid, 0, sizeof (grid));
+  grid_prop grid = {};
   if (!target || !grid_target_follows_gridifiable_pattern (target, &grid))
     return;
 
   location_t loc = gimple_location (target);
   if (dump_enabled_p ())
-    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, target,
 		     "Target construct will be turned into a gridified HSA "
 		     "kernel\n");
 
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-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..93de887
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,773 @@
+/* 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"
+#include "version.h"
+#include "context.h"
+#include "pass_manager.h"
+#include "selftest.h"
+#include "dump-context.h"
+
+/* A class for writing out optimization records in JSON format.  */
+
+class optrecord_json_writer
+{
+public:
+  optrecord_json_writer ();
+  ~optrecord_json_writer ();
+  void write () const;
+  void add_record (const optinfo *optinfo);
+  void pop_scope ();
+  void record_function_state (function *fn, opt_pass *pass);
+
+  void add_record (json::object *obj);
+  json::object *impl_location_to_json (dump_impl_location_t loc);
+  json::object *location_to_json (location_t loc);
+  json::object *profile_count_to_json (profile_count count);
+  json::string *get_id_value_for_pass (opt_pass *pass);
+  json::object *pass_to_json (opt_pass *pass);
+  json::value *inlining_chain_to_json (location_t loc);
+  json::object *optinfo_to_json (const optinfo *optinfo);
+  void add_pass_list (json::array *arr, opt_pass *pass);
+  void add_bb_to_json (basic_block bb, json::array *cfg_obj);
+  void add_function_state_to_json (function *fun, json::object *fn_obj);
+  json::object *function_state_to_json (function *fn, opt_pass *pass);
+
+private:
+  /* The root value for the JSON file.
+     Currently the JSON values are stored in memory, and flushed when the
+     compiler exits.  It would probably be better to simply write out
+     the JSON as we go.  */
+  json::array *m_root_tuple;
+
+  /* The currently open scopes, for expressing nested optimization records.  */
+  vec<json::array *> m_scopes;
+};
+
+/* optrecord_json_writer's ctor.  Populate the top-level parts of the
+   in-memory JSON representation.  */
+
+optrecord_json_writer::optrecord_json_writer ()
+  : m_root_tuple (NULL), m_scopes ()
+{
+  m_root_tuple = new json::array ();
+
+  /* Populate with metadata; compare with toplev.c: print_version.  */
+  json::object *metadata = new json::object ();
+  m_root_tuple->append (metadata);
+  metadata->set ("format", new json::string ("1"));
+  json::object *generator = new json::object ();
+  metadata->set ("generator", generator);
+  generator->set ("name", new json::string (lang_hooks.name));
+  generator->set ("pkgversion", new json::string (pkgversion_string));
+  generator->set ("version", new json::string (version_string));
+  /* TARGET_NAME is passed in by the Makefile.  */
+  generator->set ("target", new json::string (TARGET_NAME));
+
+  /* TODO: capture command-line?
+     see gen_producer_string in dwarf2out.c (currently static).  */
+
+  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
+
+  json::array *passes = new json::array ();
+  m_root_tuple->append (passes);
+
+  /* Call add_pass_list for all of the pass lists.  */
+  {
+#define DEF_PASS_LIST(LIST) \
+    add_pass_list (passes, g->get_passes ()->LIST);
+    GCC_PASS_LISTS
+#undef DEF_PASS_LIST
+  }
+
+  json::array *records = new json::array ();
+  m_root_tuple->append (records);
+
+  m_scopes.safe_push (records);
+}
+
+/* optrecord_json_writer's ctor.
+   Delete the in-memory JSON representation.  */
+
+optrecord_json_writer::~optrecord_json_writer ()
+{
+  delete m_root_tuple;
+}
+
+/* Choose an appropriate filename, and write the saved records to it.  */
+
+void
+optrecord_json_writer::write () const
+{
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      m_root_tuple->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+}
+
+/* Add a record for OPTINFO to the queue of records to be written.  */
+
+void
+optrecord_json_writer::add_record (const optinfo *optinfo)
+{
+  json::object *obj = optinfo_to_json (optinfo);
+
+  add_record (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      m_scopes.safe_push (children);
+    }
+}
+
+/* Private methods of optrecord_json_writer.  */
+
+/* Add record OBJ to the the innermost scope.  */
+
+void
+optrecord_json_writer::add_record (json::object *obj)
+{
+  /* Add to innermost scope.  */
+  gcc_assert (m_scopes.length () > 0);
+  m_scopes[m_scopes.length () - 1]->append (obj);
+}
+
+/* Pop the innermost scope.  */
+
+void
+optrecord_json_writer::pop_scope ()
+{
+  m_scopes.pop ();
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  if (loc.m_function)
+    obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+json::object *
+optrecord_json_writer::profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Get a string for use when referring to PASS in the saved optimization
+   records.  */
+
+json::string *
+optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
+{
+  pretty_printer pp;
+  /* this is host-dependent, but will be consistent for a given host.  */
+  pp_pointer (&pp, static_cast<void *> (pass));
+  return new json::string (pp_formatted_text (&pp));
+}
+
+/* Create a JSON object representing PASS.  */
+
+json::object *
+optrecord_json_writer::pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("id", get_id_value_for_pass (pass));
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+json::value *
+optrecord_json_writer::inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  const char *printable_name
+	    = lang_hooks.decl_printable_name (fndecl, 2);
+	  obj->set ("fndecl", new json::string (printable_name));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* Create a JSON object representing OPTINFO.  */
+
+json::object *
+optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
+{
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    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 (optinfo->get_pass ())
+    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
+
+  profile_count count = optinfo->get_count ();
+  if (count.initialized_p ())
+    obj->set ("count", profile_count_to_json (count));
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  location_t loc = optinfo->get_location_t ();
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  return obj;
+}
+
+/* Add a json description of PASS and its siblings to ARR, recursing into
+   child passes (adding their descriptions within a "children" array).  */
+
+void
+optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
+{
+  do
+    {
+      json::object *pass_obj = pass_to_json (pass);
+      arr->append (pass_obj);
+      if (pass->sub)
+	{
+	  json::array *sub = new json::array ();
+	  pass_obj->set ("children", sub);
+	  add_pass_list (sub, pass->sub);
+	}
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* Generate a JSON object describing BB, adding it to CFG_OBJ.
+   Compare with dump_bb, dump_bb_info etc.  */
+
+void
+optrecord_json_writer::add_bb_to_json (basic_block bb, json::array *cfg_obj)
+{
+  json::object *bb_obj = new json::object ();
+  cfg_obj->append (bb_obj);
+
+  /* Basic metadata.  Compare with dump_bb_info.  */
+
+  bb_obj->set ("index", new json::number (bb->index));
+  if (bb->count.initialized_p ())
+    bb_obj->set ("count", profile_count_to_json (bb->count));
+
+  /* Flags.  */
+  static const char * const bb_bitnames[] =
+    {
+#define DEF_BASIC_BLOCK_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+      NULL
+#undef DEF_BASIC_BLOCK_FLAG
+    };
+  const unsigned n_bitnames = sizeof (bb_bitnames) / sizeof (char *);
+  json::array *bb_flags = new json::array ();
+  bb_obj->set ("flags", bb_flags);
+  for (unsigned i = 0; i < n_bitnames; i++)
+    if (bb->flags & (1 << i))
+      bb_flags->append (new json::string (bb_bitnames[i]));
+
+  bb_obj->set ("discriminator", new json::number (bb->discriminator));
+
+  if (bb->index >= NUM_FIXED_BLOCKS)
+    {
+      /* For now, just capture all of the statements as one string.  */
+      pretty_printer pp;
+      pp.buffer->stream = NULL;
+      pp.buffer->flush_p = false;
+      pp_needs_newline (&pp) = true;
+      gimple_dump_bb_buff (&pp, bb, 0, TDF_NONE);
+      bb_obj->set ("stmts", new json::string (pp_formatted_text (&pp)));
+    }
+
+  json::array *succs = new json::array ();
+  bb_obj->set ("succs", succs);
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      /* compare with dump_edge_info.  */
+      json::object *edge_obj = new json::object ();
+      succs->append (edge_obj);
+      edge_obj->set ("dest", new json::number (e->dest->index));
+
+      /* Edge flags.  */
+      static const char * const bitnames[] =
+	{
+#define DEF_EDGE_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+	  NULL
+#undef DEF_EDGE_FLAG
+	};
+
+      json::array *edge_flags = new json::array ();
+      edge_obj->set ("flags", edge_flags);
+
+      gcc_assert (e->flags <= EDGE_ALL_FLAGS);
+      int flags = e->flags;
+      for (int i = 0; flags; i++)
+	if (flags & (1 << i))
+	  {
+	    flags &= ~(1 << i);
+	    edge_flags->append (new json::string (bitnames[i]));
+	  }
+    }
+}
+
+/* Populate FN_OBJ based on FUN.
+   Compare with dump_function_to_file.  */
+
+void
+optrecord_json_writer::add_function_state_to_json (function *fun,
+						   json::object *fn_obj)
+{
+  tree old_current_fndecl = current_function_decl;
+  basic_block bb;
+  tree fndecl = fun->decl;
+
+  current_function_decl = fndecl;
+
+  if (fun && fun->decl == fndecl
+      && fun->cfg
+      && basic_block_info_for_fn (fun))
+    {
+      json::array *cfg_obj = new json::array ();
+      fn_obj->set ("cfg", cfg_obj);
+
+      FOR_ALL_BB_FN (bb, fun)
+	add_bb_to_json (bb, cfg_obj);
+    }
+
+  current_function_decl = old_current_fndecl;
+}
+
+/* Add a record describing the state of FN after PASS to the queue of
+   records to be written.  */
+
+
+void
+optrecord_json_writer::record_function_state (function *fn,
+					      opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  json::object *obj = function_state_to_json (fn, pass);
+  add_record (obj);
+}
+
+/* Create a JSON object representing the state of FN after PASS.  */
+
+json::object *
+optrecord_json_writer::function_state_to_json (function *fn,
+					       opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  tree fndecl = fn->decl;
+
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("state"));
+  obj->set ("pass", get_id_value_for_pass (pass));
+  const char *fnname = get_fnname_from_decl (fndecl);
+  obj->set ("function", new json::string (fnname));
+
+  if (fn->curr_properties & PROP_trees)
+    {
+      add_function_state_to_json (fn, obj);
+    }
+  else
+    {
+      // TODO?  RTL equivalent when dumping is:
+      //print_rtl_with_bb (dump_file, get_insns (), dump_flags);
+    }
+  return obj;
+}
+
+/* File-level interface to rest of compiler (to avoid exposing
+   class optrecord_json_writer outside of this file).  */
+
+static optrecord_json_writer *the_json_writer;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  the_json_writer = new optrecord_json_writer ();
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go, before cleaning
+   up.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->write ();
+
+  delete the_json_writer;
+  the_json_writer = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return the_json_writer != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->add_record (optinfo);
+}
+
+/* Handling for the end of a dump scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_dump_scope ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->pop_scope ();
+}
+
+/* If optimization records were requested, then  add a record describing the
+   state of FN after PASS to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_function (function *fn,
+					    opt_pass *pass)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->record_function_state (fn, pass);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that we can build a JSON optimization record from dump_*
+   calls.  */
+
+static void
+test_building_json_from_dump_calls ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+  optinfo *info = tmp.get_pending_optinfo ();
+  ASSERT_TRUE (info != NULL);
+  ASSERT_EQ (info->num_items (), 2);
+
+  optrecord_json_writer writer;
+  json::object *json_obj = writer.optinfo_to_json (info);
+  ASSERT_TRUE (json_obj != NULL);
+
+  /* Verify that the json is sane.  */
+  char *json_str = json_obj->to_str ();
+  ASSERT_STR_CONTAINS (json_str, "impl_location");
+  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
+  ASSERT_STR_CONTAINS (json_str,
+		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
+
+  free (json_str);
+  delete json_obj;
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_json_cc_tests ()
+{
+  test_building_json_from_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..9f2ceaa
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,39 @@
+/* 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_dump_scope ();
+extern void optimization_records_maybe_record_function (function *fun,
+							opt_pass *pass);
+
+
+#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..3f267ee
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,145 @@
+/* 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; }
+
+  void trim_trailing_whitespace ();
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node, dump_flags_t dump_flags)
+    : m_node (node), m_dump_flags (dump_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  tree m_node;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
+    : m_stmt (stmt), m_dump_flags (dump_flags) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  gimple *m_stmt;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked, 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..015248b
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,254 @@
+/* Optimization information.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "selftest.h"
+
+/* Remove any trailing whitespace characters from this text item.
+   Primarily for use in stripping trailing newline characters when
+   emitting remarks (since the diagnostic subsystem doesn't expect
+   trailing newlines in messages).  */
+
+void
+optinfo_item_text::trim_trailing_whitespace ()
+{
+  size_t len = strlen (m_text);
+  if (len == 0)
+    return;
+
+  size_t new_len = len;
+  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
+    new_len--;
+
+  if (new_len == len)
+    return;
+
+  if (m_owned)
+    m_text[new_len] = '\0';
+  else
+    {
+      m_text = xstrndup (m_text, new_len);
+      m_owned = true;
+    }
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit ()
+{
+  /* Eliminate any trailing whitespace.  */
+  while (m_items.length () > 0)
+    {
+      optinfo_item *last_item = m_items[m_items.length () - 1];
+      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
+	break;
+
+      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
+      last_text->trim_trailing_whitespace ();
+
+      if (strlen (last_text->get_text ()) > 0)
+	break;
+
+      m_items.pop ();
+      delete last_item;
+    }
+
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
+
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
+}
+
+/* Update the optinfo's kind based on DUMP_KIND.  */
+
+void
+optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    m_kind = OPTINFO_KIND_NOTE;
+}
+
+/* Append a string literal to this optinfo.  */
+
+void
+optinfo::add_string (const char *str)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (str), false);
+  m_items.safe_push (item);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  add_printf_va (format, ap);
+  va_end (ap);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf_va (const char *format, va_list ap)
+{
+  char *formatted_text = xvasprintf (format, ap);
+  optinfo_item *item
+    = new optinfo_item_text (formatted_text, true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo.  */
+
+void
+optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
+}
+
+/* Append a tree node to this optinfo.  */
+
+void
+optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
+}
+
+/* Append a symbol table node to this optinfo.  */
+
+void
+optinfo::add_symtab_node (symtab_node *node)
+{
+  m_items.safe_push (new optinfo_item_symtab_node (node));
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   optinfo.  */
+
+void
+optinfo::add_dec (const wide_int_ref &wi, signop sgn)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (wi, buf, sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_items.safe_push (item);
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  return 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 ();
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that optinfo_item_text::trim_trailing_whitespace turns
+   INPUT into EXPECTED.  */
+
+static void
+test_trim_trailing_whitespace (const char *input, const char *expected)
+{
+  optinfo_item_text item (const_cast <char *> (input), false);
+  item.trim_trailing_whitespace ();
+  ASSERT_STREQ (item.get_text (), expected);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_cc_tests ()
+{
+  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
+  test_trim_trailing_whitespace ("", "");
+  test_trim_trailing_whitespace ("\n", "");
+  test_trim_trailing_whitespace ("foo", "foo");
+  test_trim_trailing_whitespace ("foo\n", "foo");
+  test_trim_trailing_whitespace ("foo\n\n", "foo");
+  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..ed7abbd
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,147 @@
+/* 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"
+#include "dumpfile.h"
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file via "-fsave-optimization-record"
+
+   They are generated in response to calls to calls to the "dump_*" API
+   in dumpfile.h
+
+   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.  */
+struct opt_pass;
+class optinfo_item; /* optinfo-internal.h.  */
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.  */
+
+class optinfo
+{
+  friend class dump_context;
+
+ public:
+  optinfo (const dump_location_t &loc,
+	   enum optinfo_kind kind,
+	   opt_pass *pass)
+  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
+  {}
+  ~optinfo ();
+
+  const dump_user_location_t &
+  get_user_location () const { return m_loc.get_user_location (); }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_loc.get_impl_location (); }
+
+  enum optinfo_kind get_kind () const { return m_kind; }
+  opt_pass *get_pass () const { return m_pass; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const { return m_loc.get_location_t (); }
+  profile_count get_count () const { return m_loc.get_count (); }
+
+ private:
+  void emit ();
+
+  /* Pre-canned ways of manipulating the optinfo, for use by friend class
+     dump_context.  */
+  void handle_dump_file_kind (dump_flags_t);
+  void add_string (const char *str);
+  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
+  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
+  void add_tree (tree node, dump_flags_t dump_flags);
+  void add_symtab_node (symtab_node *node);
+  void add_dec (const wide_int_ref &wi, signop sgn);
+
+  template<unsigned int N, typename C>
+  void add_poly_int (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      add_dec (value.coeffs[0], sgn);
+    else
+      {
+	add_string ("[");
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    add_dec (value.coeffs[i], sgn);
+	    add_string (i == N - 1 ? "]" : ",");
+	  }
+      }
+  }
+
+ private:
+  dump_location_t m_loc;
+  enum optinfo_kind m_kind;
+  opt_pass *m_pass;
+  auto_vec <optinfo_item *> m_items;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/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 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/passes.c b/gcc/passes.c
index 832f0b3..6cbc5f8 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h" /* for fnotice */
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
 
 using namespace gcc;
 
@@ -1734,6 +1736,16 @@ execute_function_dump (function *fn, void *data)
     }
 }
 
+/* Helper function to perform function body dump.  */
+
+static void
+execute_optinfo_function_dump (function *fn, void *data)
+{
+  opt_pass *pass = (opt_pass *)data;
+
+  optimization_records_maybe_record_function (fn, pass);
+}
+
 /* This function is called when an internal compiler error is encountered.
    Ensure that function dump is made available before compiler is aborted.  */
 
@@ -2202,6 +2214,8 @@ execute_one_ipa_transform_pass (struct cgraph_node *node,
 
   if (dump_file)
     do_per_function (execute_function_dump, pass);
+  if (optinfo_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
   pass_fini_dump_file (pass);
 
   current_pass = NULL;
@@ -2503,6 +2517,9 @@ execute_one_pass (opt_pass *pass)
   else if (dump_file)
     do_per_function (execute_function_dump, pass);
 
+  if (optimization_records_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
+
   if (!current_function_decl)
     symtab->process_new_functions ();
 
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index 8babbffd..df3ee81 100644
--- a/gcc/pretty-print.c
+++ b/gcc/pretty-print.c
@@ -2102,10 +2102,7 @@ test_pp_format ()
 {
   /* Avoid introducing locale-specific differences in the results
      by hardcoding open_quote and close_quote.  */
-  const char *old_open_quote = open_quote;
-  const char *old_close_quote = close_quote;
-  open_quote = "`";
-  close_quote = "'";
+  auto_fix_quotes fix_quotes;
 
   /* Verify that plain text is passed through unchanged.  */
   assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted");
@@ -2187,10 +2184,6 @@ test_pp_format ()
   assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7);
   assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10",
 		    "problem with %qs at line %i", "bar", 10);
-
-  /* Restore old values of open_quote and close_quote.  */
-  open_quote = old_open_quote;
-  close_quote = old_close_quote;
 }
 
 /* Run all of the selftests within this file.  */
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/profile.c b/gcc/profile.c
index 8ba6dc7..0cd0270 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -447,9 +447,14 @@ read_profile_edge_counts (gcov_type *exec_counts)
 		      {
 			static bool informed = 0;
 			if (dump_enabled_p () && !informed)
-		          dump_printf_loc (MSG_NOTE, input_location,
-                                           "corrupted profile info: edge count"
-                                           " exceeds maximal count\n");
+			  {
+			    dump_location_t loc
+			      = dump_location_t::from_location_t
+			        (input_location);
+			    dump_printf_loc (MSG_NOTE, loc,
+					     "corrupted profile info: edge count"
+					     " exceeds maximal count\n");
+			  }
 			informed = 1;
 		      }
 		    else
@@ -672,7 +677,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum)
          if (dump_enabled_p () && informed == 0)
            {
              informed = 1;
-             dump_printf_loc (MSG_NOTE, input_location,
+             dump_printf_loc (MSG_NOTE,
+			      dump_location_t::from_location_t (input_location),
                               "correcting inconsistent profile data\n");
            }
          correct_negative_edge_counts ();
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 36879cf..9d588fa 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -59,6 +59,7 @@ selftest::run_tests ()
   /* Low-level data structures.  */
   bitmap_c_tests ();
   sbitmap_c_tests ();
+  dumpfile_c_tests ();
   et_forest_c_tests ();
   hash_map_tests_c_tests ();
   hash_set_tests_c_tests ();
@@ -71,6 +72,9 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   json_cc_tests ();
+  optinfo_cc_tests ();
+  optinfo_emit_diagnostics_cc_tests ();
+  optinfo_emit_json_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.c b/gcc/selftest.c
index 74adc63..b333ca8 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "selftest.h"
+#include "intl.h"
 
 #if CHECKING_P
 
@@ -158,6 +159,25 @@ temp_source_file::temp_source_file (const location &loc,
   fclose (out);
 }
 
+/* Avoid introducing locale-specific differences in the results
+   by hardcoding open_quote and close_quote.  */
+
+auto_fix_quotes::auto_fix_quotes ()
+{
+  m_saved_open_quote = open_quote;
+  m_saved_close_quote = close_quote;
+  open_quote = "`";
+  close_quote = "'";
+}
+
+/* Restore old values of open_quote and close_quote.  */
+
+auto_fix_quotes::~auto_fix_quotes ()
+{
+  open_quote = m_saved_open_quote;
+  close_quote = m_saved_close_quote;
+}
+
 /* Read the contents of PATH into memory, returning a 0-terminated buffer
    that must be freed by the caller.
    Fail (and abort) if there are any problems, with LOC as the reported
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 173700b..1434329 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -104,6 +104,26 @@ class temp_source_file : public named_temp_file
 		    const char *content);
 };
 
+/* RAII class for avoiding introducing locale-specific differences
+   in strings containing localized quote marks, by temporarily overriding
+   the "open_quote" and "close_quote" globals to something hardcoded.
+
+   Specifically, the C locale's values are used:
+   - open_quote becomes "`"
+   - close_quote becomes "'"
+   for the lifetime of the object.  */
+
+class auto_fix_quotes
+{
+ public:
+  auto_fix_quotes ();
+  ~auto_fix_quotes ();
+
+ private:
+  const char *m_saved_open_quote;
+  const char *m_saved_close_quote;
+};
+
 /* Various selftests involving location-handling require constructing a
    line table and one or more line maps within it.
 
@@ -188,6 +208,7 @@ extern void attribute_c_tests ();
 extern void bitmap_c_tests ();
 extern void diagnostic_c_tests ();
 extern void diagnostic_show_locus_c_tests ();
+extern void dumpfile_c_tests ();
 extern void edit_context_c_tests ();
 extern void et_forest_c_tests ();
 extern void fibonacci_heap_c_tests ();
@@ -199,6 +220,9 @@ extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void json_cc_tests ();
+extern void optinfo_cc_tests ();
+extern void optinfo_emit_diagnostics_cc_tests ();
+extern void optinfo_emit_json_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/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 a15c5d5..906ee3b 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1154,6 +1154,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 d108096..a047390 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -487,6 +488,8 @@ compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2048,6 +2051,8 @@ do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
index c6e0a60..1206614 100644
--- a/gcc/tree-loop-distribution.c
+++ b/gcc/tree-loop-distribution.c
@@ -3117,7 +3117,7 @@ pass_loop_distribution::execute (function *fun)
 	    break;
 
 	  const char *str = loop->inner ? " nest" : "";
-	  location_t loc = find_loop_location (loop);
+	  dump_user_location_t loc = find_loop_location (loop);
 	  if (!cd)
 	    {
 	      calculate_dominance_info (CDI_DOMINATORS);
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index f1557c9..2a12eef 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -3334,7 +3334,7 @@ lower_nested_functions (tree fndecl)
 
   gimplify_all_functions (cgn);
 
-  dump_file = dump_begin (TDI_nested, &dump_flags);
+  set_dump_file (dump_begin (TDI_nested, &dump_flags));
   if (dump_file)
     fprintf (dump_file, "\n;; Function %s\n\n",
 	     lang_hooks.decl_printable_name (fndecl, 2));
@@ -3361,7 +3361,7 @@ lower_nested_functions (tree fndecl)
   if (dump_file)
     {
       dump_end (TDI_nested, dump_file);
-      dump_file = NULL;
+      set_dump_file (NULL);
     }
 }
 
diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
index aa74427..64712e4 100644
--- a/gcc/tree-parloops.c
+++ b/gcc/tree-parloops.c
@@ -3284,7 +3284,6 @@ parallelize_loops (bool oacc_kernels_p)
   struct tree_niter_desc niter_desc;
   struct obstack parloop_obstack;
   HOST_WIDE_INT estimated;
-  source_location loop_loc;
 
   /* Do not parallelize loops in the functions created by parallelization.  */
   if (!oacc_kernels_p
@@ -3409,7 +3408,7 @@ parallelize_loops (bool oacc_kernels_p)
       changed = true;
       skip_loop = loop->inner;
 
-      loop_loc = find_loop_location (loop);
+      dump_user_location_t loop_loc = find_loop_location (loop);
       if (loop->inner)
 	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loop_loc,
 			 "parallelizing outer loop %d\n", loop->num);
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 7447f7a..2623d9b 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 24bf60e..5f741c3 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -691,7 +691,7 @@ try_unroll_loop_completely (struct loop *loop,
 			    edge exit, tree niter, bool may_be_zero,
 			    enum unroll_level ul,
 			    HOST_WIDE_INT maxiter,
-			    location_t locus, bool allow_peel)
+			    dump_user_location_t locus, bool allow_peel)
 {
   unsigned HOST_WIDE_INT n_unroll = 0;
   bool n_unroll_found = false;
@@ -1162,7 +1162,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
   tree niter;
   HOST_WIDE_INT maxiter;
   bool modified = false;
-  location_t locus = UNKNOWN_LOCATION;
+  dump_user_location_t locus;
   struct tree_niter_desc niter_desc;
   bool may_be_zero = false;
 
@@ -1177,7 +1177,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	= niter_desc.may_be_zero && !integer_zerop (niter_desc.may_be_zero);
     }
   if (TREE_CODE (niter) == INTEGER_CST)
-    locus = gimple_location (last_stmt (exit->src));
+    locus = last_stmt (exit->src);
   else
     {
       /* For non-constant niter fold may_be_zero into niter again.  */
@@ -1204,7 +1204,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	niter = find_loop_niter_by_eval (loop, &exit);
 
       if (exit)
-        locus = gimple_location (last_stmt (exit->src));
+        locus = last_stmt (exit->src);
 
       if (TREE_CODE (niter) != INTEGER_CST)
 	exit = NULL;
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index b313571..d80541c 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -7525,7 +7525,7 @@ tree_ssa_iv_optimize_loop (struct ivopts_data *data, struct loop *loop)
 
   gcc_assert (!data->niters);
   data->current_loop = loop;
-  data->loop_loc = find_loop_location (loop);
+  data->loop_loc = find_loop_location (loop).get_location_t ();
   data->speed = optimize_loop_for_speed_p (loop);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 7a54c5f..3aee0f6 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -2447,7 +2447,7 @@ number_of_iterations_exit (struct loop *loop, edge exit,
     return true;
 
   if (warn)
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
+    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
 		     "missed loop optimization: niters analysis ends up "
 		     "with assumptions.\n");
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 3d025c2..e5eddf9 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5866,8 +5866,7 @@ eliminate_dom_walker::before_dom_children (basic_block b)
 		    fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
 		  if (dump_enabled_p ())
 		    {
-		      location_t loc = gimple_location (stmt);
-		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				       "converting indirect call to "
 				       "function %s\n",
 				       lang_hooks.decl_printable_name (fn, 2));
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index 3eab650..227f993 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -1301,7 +1301,7 @@ create_lcssa_for_virtual_phi (struct loop *loop)
    location is calculated.
    Return the loop location if succeed and NULL if not.  */
 
-source_location
+dump_user_location_t
 find_loop_location (struct loop *loop)
 {
   gimple *stmt = NULL;
@@ -1309,19 +1309,19 @@ find_loop_location (struct loop *loop)
   gimple_stmt_iterator si;
 
   if (!loop)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   stmt = get_loop_exit_condition (loop);
 
   if (stmt
       && LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-    return gimple_location (stmt);
+    return stmt;
 
   /* If we got here the loop is probably not "well formed",
      try to estimate the loop location */
 
   if (!loop->header)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   bb = loop->header;
 
@@ -1329,10 +1329,10 @@ find_loop_location (struct loop *loop)
     {
       stmt = gsi_stmt (si);
       if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-        return gimple_location (stmt);
+        return stmt;
     }
 
-  return UNKNOWN_LOCATION;
+  return dump_user_location_t ();
 }
 
 /* Return true if PHI defines an IV of the loop to be vectorized.  */
@@ -2494,7 +2494,7 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1,
 	}
     }
 
-  source_location loop_loc = find_loop_location (loop);
+  dump_user_location_t loop_loc = find_loop_location (loop);
   struct loop *scalar_loop = LOOP_VINFO_SCALAR_LOOP (loop_vinfo);
   if (prolog_peeling)
     {
@@ -3068,7 +3068,7 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       loop_constraint_set (loop, LOOP_C_INFINITE);
     }
 
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
       && dump_enabled_p ())
     {
       if (version_alias)
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 8e45aec..7198826 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -2723,8 +2723,8 @@ needs_fold_left_reduction_p (tree type, tree_code code,
    reduction operation CODE has a handled computation expression.  */
 
 bool
-check_reduction_path (location_t loc, loop_p loop, gphi *phi, tree loop_arg,
-		      enum tree_code code)
+check_reduction_path (dump_user_location_t loc, loop_p loop, gphi *phi,
+		      tree loop_arg, enum tree_code code)
 {
   auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
   auto_bitmap visited;
@@ -3742,8 +3742,8 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
   else
     {
       if (LOOP_VINFO_LOOP (loop_vinfo)->force_vectorize)
-	warning_at (vect_location, OPT_Wopenmp_simd, "vectorization "
-		    "did not happen for a simd loop");
+	warning_at (vect_location.get_location_t (), OPT_Wopenmp_simd,
+		    "vectorization did not happen for a simd loop");
 
       if (dump_enabled_p ())
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 1bbe468..562f835 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -1510,7 +1510,8 @@ fail:
 /* Dump a slp tree NODE using flags specified in DUMP_KIND.  */
 
 static void
-vect_print_slp_tree (dump_flags_t dump_kind, location_t loc, slp_tree node)
+vect_print_slp_tree (dump_flags_t dump_kind, dump_location_t loc,
+		     slp_tree node)
 {
   int i;
   gimple *stmt;
@@ -3001,7 +3002,7 @@ vect_slp_bb (basic_block bb)
 	  insns++;
 
 	  if (gimple_location (stmt) != UNKNOWN_LOCATION)
-	    vect_location = gimple_location (stmt);
+	    vect_location = stmt;
 
 	  if (!vect_find_stmt_data_reference (NULL, stmt, &datarefs))
 	    break;
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 8ff90b3..fa4909e 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -81,8 +81,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 
 
-/* Loop or bb location.  */
-source_location vect_location;
+/* Loop or bb location, with hotness information.  */
+dump_user_location_t vect_location;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> *stmt_vec_info_vec;
@@ -749,11 +749,12 @@ vectorize_loops (void)
 	loop_dist_alias_call = vect_loop_dist_alias_call (loop);
        vectorize_epilogue:
 	vect_location = find_loop_location (loop);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+	DUMP_VECT_SCOPE ("analyzing loop");
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
 	    && dump_enabled_p ())
 	  dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
-                       LOCATION_FILE (vect_location),
-		       LOCATION_LINE (vect_location));
+                       LOCATION_FILE (vect_location.get_location_t ()),
+		       LOCATION_LINE (vect_location.get_location_t ()));
 
 	loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo);
 	loop->aux = loop_vinfo;
@@ -827,7 +828,7 @@ vectorize_loops (void)
 
 	if (loop_vectorized_call)
 	  set_uid_loop_bbs (loop_vinfo, loop_vectorized_call);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
 	    && dump_enabled_p ())
           dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
                            "loop vectorized\n");
@@ -872,7 +873,7 @@ vectorize_loops (void)
 	  }
       }
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
   if (dump_enabled_p ()
@@ -1207,7 +1208,7 @@ increase_alignment (void)
 {
   varpool_node *vnode;
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
   type_align_map = new hash_map<tree, unsigned>;
 
   /* Increase the alignment of all global arrays for vectorization.  */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 8bb9e3e..5d8ba94 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-data-ref.h"
 #include "tree-hash-traits.h"
 #include "target.h"
+#include "optinfo.h"
 
 /* Used for naming of new temporaries.  */
 enum vect_var_kind {
@@ -1422,18 +1423,20 @@ vect_get_scalar_dr_size (struct data_reference *dr)
   return tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr))));
 }
 
-/* Source location */
-extern source_location vect_location;
+/* Source location + hotness information. */
 
-/* If dumping is enabled, emit a MSG_NOTE at vect_location about
-   entering MSG within the vectorizer.  MSG should be a string literal. */
+extern dump_user_location_t vect_location;
+
+/* A macro for calling:
+     dump_begin_scope (MSG, vect_location);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
 
 #define DUMP_VECT_SCOPE(MSG) \
-  do {						\
-    if (dump_enabled_p ())			\
-      dump_printf_loc (MSG_NOTE, vect_location, \
-		       "=== " MSG " ===\n");	\
-  } while (0)
+  AUTO_DUMP_SCOPE (MSG, vect_location)
 
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
@@ -1451,7 +1454,7 @@ extern void vect_loop_versioning (loop_vec_info, unsigned int, bool,
 extern struct loop *vect_do_peeling (loop_vec_info, tree, tree,
 				     tree *, tree *, tree *, int, bool, bool);
 extern void vect_prepare_for_masked_peels (loop_vec_info);
-extern source_location find_loop_location (struct loop *);
+extern dump_user_location_t find_loop_location (struct loop *);
 extern bool vect_can_advance_ivs_p (loop_vec_info);
 
 /* In tree-vect-stmts.c.  */
@@ -1567,7 +1570,7 @@ extern tree vect_create_addr_base_for_vector_ref (gimple *, gimple_seq *,
 extern gimple *vect_force_simple_reduction (loop_vec_info, gimple *,
 					    bool *, bool);
 /* Used in gimple-loop-interchange.c.  */
-extern bool check_reduction_path (location_t, loop_p, gphi *, tree,
+extern bool check_reduction_path (dump_user_location_t, loop_p, gphi *, tree,
 				  enum tree_code);
 /* Drive for loop analysis stage.  */
 extern loop_vec_info vect_analyze_loop (struct loop *, loop_vec_info);
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index d50a179..77d4849 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -585,10 +585,11 @@ check_counter (gimple *stmt, const char * name,
   gcov_type bb_count = bb_count_d.ipa ().to_gcov_type ();
   if (*all != bb_count || *count > *all)
     {
-      location_t locus;
-      locus = (stmt != NULL)
-              ? gimple_location (stmt)
-              : DECL_SOURCE_LOCATION (current_function_decl);
+      dump_user_location_t locus;
+      locus = ((stmt != NULL)
+	       ? dump_user_location_t (stmt)
+	       : dump_user_location_t::from_function_decl
+		   (current_function_decl));
       if (flag_profile_correction)
         {
           if (dump_enabled_p ())
@@ -603,7 +604,7 @@ check_counter (gimple *stmt, const char * name,
 	}
       else
 	{
-	  error_at (locus, "corrupted value profile: %s "
+	  error_at (locus.get_location_t (), "corrupted value profile: %s "
 		    "profile counter (%d out of %d) inconsistent with "
 		    "basic-block count (%d)",
 		    name,
@@ -1271,13 +1272,11 @@ find_func_by_profile_id (int profile_id)
 bool
 check_ic_target (gcall *call_stmt, struct cgraph_node *target)
 {
-   location_t locus;
    if (gimple_check_call_matching_types (call_stmt, target->decl, true))
      return true;
 
-   locus =  gimple_location (call_stmt);
    if (dump_enabled_p ())
-     dump_printf_loc (MSG_MISSED_OPTIMIZATION, locus,
+     dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                       "Skipping target %s with mismatching types for icall\n",
                       target->name ());
    return false;
-- 
1.8.5.3



More information about the Gcc-patches mailing list