[3/3] v3: Report vectorization problems via a new opt_problem class

Message ID 1538161880-64793-4-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • Higher-level reporting of vectorization problems (v3)
Related show

Commit Message

David Malcolm Sept. 28, 2018, 7:11 p.m.
This is v3 of the patch; previous versions were:
  v2: https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
  v1: https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html

This patch introduces a class opt_problem, along with wrapper
classes for bool (opt_result) and for pointers (e.g. opt_loop_vec_info
for loop_vec_info).

opt_problem instances are created when an optimization problem
is encountered, but only if dump_enabled_p.  They are manually
propagated up the callstack, and are manually reported at the
"top level" of an optimization if dumping is enabled, to give the user
a concise summary of the problem *after* the failure is reported.
In particular, the location of the problematic statement is
captured and emitted, rather than just the loop's location.

For example:

no-vfa-vect-102.c:24:3: missed: couldn't vectorize loop
no-vfa-vect-102.c:27:7: missed: statement clobbers memory: __asm__ __volatile__("" :  :  : "memory");

Changed in v3:
* This version bootstraps and passes regression testing (on
  x86_64-pc-linux-gnu).
* added selftests, to exercise the opt_problem machinery
* removed the "bool to opt_result" ctor, so that attempts to
  use e.g. return a bool from an opt_result-returning function
  will fail at compile time
* use formatted printing within opt_problem ctor to replace the
  various dump_printf_loc calls
* dropped i18n
* changed the sense of vect_analyze_data_ref_dependence's return
  value (see the ChangeLog)
* add MSG_PRIORITY_REEMITTED, so that -fopt-info can show the
  messages, without them messing up the counts in scan-tree-dump-times
  in DejaGnu tests

Re Richard Sandiford's feedback on the v2 patch:
  https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00560.html
> Since the creation of the opt_problem depends on dump_enabled_p, would

> it make sense for the dump_printf_loc to happen automatically on

> opt_result::failure, rather than have both?


Yes; this v3 patch does that: opt_result::failure_at is passed a format
string with variadic args.  If dumping is enabled, it performs the
equivalent of dump_printf_loc in a form that will reach dumpfiles
(and -fopt-info-internals), stashing the dumped items in the opt_problem.
When the opt_problem is emitted at the top-level, the message is re-emitted
(but only for -fopt-info, not for dumpfiles, to avoid duplicates that mess
up scan-tree-dump-times in DejaGnu tests)

> I guess this is bike-shedding, but personally I'd prefer an explicit

> test for success rather than operator bool, so that:

>

>    opt_result foo = ...;

>    bool bar = foo;

>

> is ill-formed.  The danger otherwise might be that we drop a useful

> opt_problem and replace it with something more generic.  E.g. the

> opt_result form of:

>   if (!ok)

>     {

>       if (dump_enabled_p ())

>         {

>           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>                            "not vectorized: relevant stmt not ");

>           dump_printf (MSG_MISSED_OPTIMIZATION, "supported: ");

>           dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);

>         }

>

>       return false;

>     }

>

> in vect_analyze_stmt could silently drop the information provided by

> the subroutine if we forgot to change "ok" from "bool" to "opt_result".


I didn't make that change in v3: if the function returns an opt_result, then
the "return false;" will be a compile-time failure, alerting us to the
problem.

I guess this is a matter of style, whether explicit is better than
implicit.  Dropping the operator bool would require an explicit approach,
with something like:

    // Explicit style:
    opt_result res = ...;
    if (res.failed_p ())
       return res;

and:

    // Explicit style:
    // It's often currently called "ok":
    opt_result ok = ...;
    if (ok.failed_p ())
       return ok;

as opposed to:

    // Implicit style:
    opt_result res = ...;
    if (!res)
       return res;

and:

    // Implicit style:
    opt_result ok = ...;
    if (!ok)
       return ok;

I think I went with the implicit style to minimize the lines touched by
the patch, but I'm happy with either approach.  [If we're bikeshedding,
would renaming those "ok" to "res" be acceptable also?  "ok" reads to
me like a "success" value for a status variable, rather than the status
variable itself; it's presumably meant to be pronounced with a rising
interrogative as it were a question - "ok?" - but that's not visible in
the code when reading the usage sites].

Similarly, the pointer wrappers use an implicit style:

  // Implicit style:

  opt_loop_vec_info loop_vinfo
    = vect_analyze_loop (loop, orig_loop_vinfo, &shared);
  loop->aux = loop_vinfo;

  if (!loop_vinfo)
    if (dump_enabled_p ())
      if (opt_problem *problem = loop_vinfo.get_problem ())
	{

but maybe an explicit style is more readable:

  // Explicit style:

  opt_loop_vec_info opt_loop_vinfo
    = vect_analyze_loop (loop, orig_loop_vinfo, &shared);
  loop_vec_info loop_vinfo = loop_vinfo.get_pointer ();
  loop->aux = loop_vinfo

  if (opt_loop_vinfo.failed_p ())
    if (dump_enabled_p ())
      if (opt_problem *problem = loop_vinfo.get_problem ())
	{


How would you want the code to look?

Richi: do you have an opinion here?

(or is that style in the patch OK as-is?)

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

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add opt-problem.o.
	* dump-context.h: Include "selftest.h.
	(selftest::temp_dump_context): New forward decl.
	(class dump_context): Make friend of class
	selftest::temp_dump_context.
	(dump_context::dump_loc_immediate): New decl.
	(class dump_pretty_printer): Move here from dumpfile.c.
	(class temp_dump_context): Move to namespace selftest.
	(temp_dump_context::temp_dump_context): Add param
	"forcibly_enable_dumping".
	(selftest::verify_dumped_text):
	(ASSERT_DUMPED_TEXT_EQ): Move here from dumpfile.c.
	(selftest::verify_item):
	(ASSERT_IS_TEXT): Move here from dumpfile.c.
	(ASSERT_IS_TREE): Likewise.
	(ASSERT_IS_GIMPLE): Likewise.
	* dumpfile.c (dump_context::dump_loc): Move immediate dumping
	to...
	(dump_context::dump_loc_immediate): ...this new function.
	(class dump_pretty_printer): Move to dump-context.h.
	(dump_switch_p_1): Don't enable MSG_PRIORITY_REEMITTED.
	(opt_info_switch_p_1): Enable MSG_PRIORITY_REEMITTED.
	(temp_dump_context::temp_dump_context): Move to "selftest"
	namespace.  Add param "forcibly_enable_dumping", and use it to
	conditionalize the use of m_pp;
	(selftest::verify_dumped_text): Make non-static.
	(ASSERT_DUMPED_TEXT_EQ): Move to dump-context.h.
	(selftest::verify_item): Make non-static.
	(ASSERT_IS_TEXT): Move to dump-context.h.
	(ASSERT_IS_TREE): Likewise.
	(ASSERT_IS_GIMPLE): Likewise.
	(selftest::test_capture_of_dump_calls): Pass "true" for new
	param of temp_dump_context.
	* dumpfile.h (enum dump_flag): Add MSG_PRIORITY_REEMITTED, adding
	it to MSG_ALL_PRIORITIES.  Update values of TDF_COMPARE_DEBUG and
	TDF_COMPARE_DEBUG.
	* opt-problem.cc: New file.
	* opt-problem.h: New file.
	* optinfo-emit-json.cc
	(selftest::test_building_json_from_dump_calls): Pass "true" for
	new param of temp_dump_context.
	* optinfo.cc (optinfo_kind_to_dump_flag): New function.
	(optinfo::emit_for_opt_problem): New function.
	(optinfo::emit): Clarity which emit_item is used.
	* optinfo.h (optinfo::get_dump_location): New accessor.
	(optinfo::emit_for_opt_problem): New decl.
	(optinfo::emit): Make const.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::opt_problem_cc_tests.
	* selftest.h (selftest::opt_problem_cc_tests): New decl.
	* tree-data-ref.c (dr_analyze_innermost): Convert return type from
	bool to opt_result, converting fprintf messages to
	opt_result::failure_at calls.  Add "stmt" param for use by the
	failure_at calls.
	(create_data_ref): Pass "stmt" to the dr_analyze_innermost call.
	(runtime_alias_check_p): Convert return type from bool to
	opt_result, converting dump_printf calls to
	opt_result::failure_at, using the statement DDR_A for their
	location.
	(find_data_references_in_stmt): Convert return type from bool to
	opt_result, converting "return false" to opt_result::failure_at
	with a new message.
	* tree-data-ref.h: Include "opt-problem.h".
	(dr_analyze_innermost): Convert return type from bool to opt_result,
	and add a const gimple * param.
	(find_data_references_in_stmt): Convert return type from bool to
	opt_result.
	(runtime_alias_check_p): Likewise.
	* tree-predcom.c (find_looparound_phi): Pass "init_stmt" to
	dr_analyze_innermost.
	* tree-vect-data-refs.c (vect_mark_for_runtime_alias_test):
	Convert return type from bool to opt_result, adding a message for
	the PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS zero case.
	(vect_analyze_data_ref_dependence): Convert return type from bool
	to opt_result.  Change sense of return type from "false"
	effectively meaning "no problems" to "false" meaning a problem,
	so that "return false" becomes "return opt_result::success".
	Convert "return true" calls to opt_result::failure_at, using
	the location of statement A rather than vect_location.
	(vect_analyze_data_ref_dependences): Convert return type from bool
	to opt_result.
	(verify_data_ref_alignment): Likewise, converting dump_printf_loc
	calls to opt_result::failure_at, using the stmt location rather
	than vect_location.
	(vect_verify_datarefs_alignment): Convert return type from bool
	to opt_result.
	(vect_enhance_data_refs_alignment): Likewise.  Split local "stat"
	into multiple more-tightly-scoped copies.
	(vect_analyze_data_refs_alignment): Convert return type from bool
	to opt_result.
	(vect_analyze_data_ref_accesses): Likewise, converting a
	"return false" to a "return opt_result::failure_at", adding a
	new message.
	(vect_prune_runtime_alias_test_list): Convert return type from
	bool to opt_result, converting dump_printf_loc to
	opt_result::failure_at.  Add a %G to show the pertinent statement,
	and use the stmt's location rather than vect_location.
	(vect_find_stmt_data_reference): Convert return type from
	bool to opt_result, converting dump_printf_loc to
	opt_result::failure_at, using stmt's location.
	(vect_analyze_data_refs):  Convert return type from bool to
	opt_result.  Convert "return false" to "return
	opt_result::failure_at", adding messages as needed.
	* tree-vect-loop.c (vect_determine_vf_for_stmt_1): Convert return
	type from bool to opt_result.
	(vect_determine_vf_for_stmt): Likewise.
	(vect_determine_vectorization_factor): Likewise, converting
	dump_printf_loc to opt_result::failure_at, using location of phi
	rather than vect_location.
	(vect_analyze_loop_form_1): Convert return type from bool to
	opt_result, converting dump_printf_loc calls, retaining the use of
	vect_location.
	(vect_analyze_loop_form): Convert return type from loop_vec_info
	to opt_loop_vec_info.
	(vect_analyze_loop_operations): Convert return type from bool to
	opt_result, converting dump_printf_loc calls, using the location
	of phi/stmt rather than vect_location where available.  Convert
	various "return false" to "return opt_result::failure_at" with
	"unsupported phi" messages.
	(vect_get_datarefs_in_loop): Convert return type from bool to
	opt_result.  Add a message for the
	PARAM_LOOP_MAX_DATAREFS_FOR_DATADEPS failure.
	(vect_analyze_loop_2): Convert return type from bool to
	opt_result.  Ensure "ok" is set to a opt_result::failure_at before
	each "goto again;", adding new messages where needed.
	Add "unsupported grouped {store|load}" messages.
	(vect_analyze_loop): Convert return type from loop_vec_info to
	opt_loop_vec_info.
	* tree-vect-slp.c (vect_analyze_slp): Convert return type from
	bool to opt_result.
	* tree-vect-stmts.c (process_use): Likewise, converting
	dump_printf_loc call and using stmt location, rather than
	vect_location.
	(vect_mark_stmts_to_be_vectorized): Likeise.
	(vect_analyze_stmt): Likewise, adding a %G.
	(vect_get_vector_types_for_stmt): Convert return type from bool to
	opt_result, converting dump_printf_loc calls and using stmt
	location, rather than vect_location.
	(vect_get_mask_type_for_stmt): Convert return type from tree to
	opt_tree, converting dump_printf_loc calls and using stmt location.
	* tree-vectorizer.c: Include "opt-problem.h.
	(try_vectorize_loop_1): Flag "Analyzing loop at" dump message as
	MSG_PRIORITY_INTERNALS.  Convert local "loop_vinfo" from
	loop_vec_info to opt_loop_vec_info.  If if fails, and dumping is
	enabled, use it to report at the top level "couldn't vectorize
	loop" followed by the problem.
	* tree-vectorizer.h (opt_loop_vec_info): New typedef.
	(vect_mark_stmts_to_be_vectorized): Convert return type from bool
	to opt_result.
	(vect_analyze_stmt): Likewise.
	(vect_get_vector_types_for_stmt): Likewise.
	(tree vect_get_mask_type_for_stmt): Likewise.
	(vect_analyze_data_ref_dependences): Likewise.
	(vect_enhance_data_refs_alignment): Likewise.
	(vect_analyze_data_refs_alignment): Likewise.
	(vect_verify_datarefs_alignment): Likewise.
	(vect_analyze_data_ref_accesses): Likewise.
	(vect_prune_runtime_alias_test_list): Likewise.
	(vect_find_stmt_data_reference): Likewise.
	(vect_analyze_data_refs): Likewise.
	(vect_analyze_loop): Convert return type from loop_vec_info to
	opt_loop_vec_info.
	(vect_analyze_loop_form): Likewise.
	(vect_analyze_slp): Convert return type from bool to opt_result.

gcc/testsuite/ChangeLog:
	* gcc.dg/vect/nodump-vect-opt-info-2.c: New test.
	* gcc.dg/vect/vect-alias-check-4.c: Add "-fopt-info-vec-all" to
	dg-additional-options.  Add dg-message and dg-missed directives
	to verify that -fopt-info messages are written at the correct
	locations.
---
 gcc/Makefile.in                                    |   1 +
 gcc/dump-context.h                                 | 104 ++++-
 gcc/dumpfile.c                                     | 160 +++-----
 gcc/dumpfile.h                                     |  13 +-
 gcc/opt-problem.cc                                 | 335 ++++++++++++++++
 gcc/opt-problem.h                                  | 289 ++++++++++++++
 gcc/optinfo-emit-json.cc                           |   2 +-
 gcc/optinfo.cc                                     |  44 +-
 gcc/optinfo.h                                      |   7 +-
 gcc/selftest-run-tests.c                           |   1 +
 gcc/selftest.h                                     |   1 +
 gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c |  12 +
 gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c     |  18 +-
 gcc/tree-data-ref.c                                |  70 ++--
 gcc/tree-data-ref.h                                |  10 +-
 gcc/tree-predcom.c                                 |   3 +-
 gcc/tree-vect-data-refs.c                          | 347 ++++++++--------
 gcc/tree-vect-loop.c                               | 442 +++++++++------------
 gcc/tree-vect-slp.c                                |   4 +-
 gcc/tree-vect-stmts.c                              | 275 ++++++-------
 gcc/tree-vectorizer.c                              |  17 +-
 gcc/tree-vectorizer.h                              |  45 ++-
 22 files changed, 1408 insertions(+), 792 deletions(-)
 create mode 100644 gcc/opt-problem.cc
 create mode 100644 gcc/opt-problem.h
 create mode 100644 gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c

-- 
1.8.5.3

Comments

Richard Biener Oct. 4, 2018, 10:52 a.m. | #1
On Fri, 28 Sep 2018, David Malcolm wrote:

> This is v3 of the patch; previous versions were:

>   v2: https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html

>   v1: https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html

> 

> This patch introduces a class opt_problem, along with wrapper

> classes for bool (opt_result) and for pointers (e.g. opt_loop_vec_info

> for loop_vec_info).

> 

> opt_problem instances are created when an optimization problem

> is encountered, but only if dump_enabled_p.  They are manually

> propagated up the callstack, and are manually reported at the

> "top level" of an optimization if dumping is enabled, to give the user

> a concise summary of the problem *after* the failure is reported.

> In particular, the location of the problematic statement is

> captured and emitted, rather than just the loop's location.

> 

> For example:

> 

> no-vfa-vect-102.c:24:3: missed: couldn't vectorize loop

> no-vfa-vect-102.c:27:7: missed: statement clobbers memory: __asm__ __volatile__("" :  :  : "memory");

> 

> Changed in v3:

> * This version bootstraps and passes regression testing (on

>   x86_64-pc-linux-gnu).

> * added selftests, to exercise the opt_problem machinery

> * removed the "bool to opt_result" ctor, so that attempts to

>   use e.g. return a bool from an opt_result-returning function

>   will fail at compile time

> * use formatted printing within opt_problem ctor to replace the

>   various dump_printf_loc calls

> * dropped i18n

> * changed the sense of vect_analyze_data_ref_dependence's return

>   value (see the ChangeLog)

> * add MSG_PRIORITY_REEMITTED, so that -fopt-info can show the

>   messages, without them messing up the counts in scan-tree-dump-times

>   in DejaGnu tests

> 

> Re Richard Sandiford's feedback on the v2 patch:

>   https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00560.html

> > Since the creation of the opt_problem depends on dump_enabled_p, would

> > it make sense for the dump_printf_loc to happen automatically on

> > opt_result::failure, rather than have both?

> 

> Yes; this v3 patch does that: opt_result::failure_at is passed a format

> string with variadic args.  If dumping is enabled, it performs the

> equivalent of dump_printf_loc in a form that will reach dumpfiles

> (and -fopt-info-internals), stashing the dumped items in the opt_problem.

> When the opt_problem is emitted at the top-level, the message is re-emitted

> (but only for -fopt-info, not for dumpfiles, to avoid duplicates that mess

> up scan-tree-dump-times in DejaGnu tests)

> 

> > I guess this is bike-shedding, but personally I'd prefer an explicit

> > test for success rather than operator bool, so that:

> >

> >    opt_result foo = ...;

> >    bool bar = foo;

> >

> > is ill-formed.  The danger otherwise might be that we drop a useful

> > opt_problem and replace it with something more generic.  E.g. the

> > opt_result form of:

> >   if (!ok)

> >     {

> >       if (dump_enabled_p ())

> >         {

> >           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> >                            "not vectorized: relevant stmt not ");

> >           dump_printf (MSG_MISSED_OPTIMIZATION, "supported: ");

> >           dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);

> >         }

> >

> >       return false;

> >     }

> >

> > in vect_analyze_stmt could silently drop the information provided by

> > the subroutine if we forgot to change "ok" from "bool" to "opt_result".

> 

> I didn't make that change in v3: if the function returns an opt_result, then

> the "return false;" will be a compile-time failure, alerting us to the

> problem.

> 

> I guess this is a matter of style, whether explicit is better than

> implicit.  Dropping the operator bool would require an explicit approach,

> with something like:

> 

>     // Explicit style:

>     opt_result res = ...;

>     if (res.failed_p ())

>        return res;

> 

> and:

> 

>     // Explicit style:

>     // It's often currently called "ok":

>     opt_result ok = ...;

>     if (ok.failed_p ())

>        return ok;

> 

> as opposed to:

> 

>     // Implicit style:

>     opt_result res = ...;

>     if (!res)

>        return res;

> 

> and:

> 

>     // Implicit style:

>     opt_result ok = ...;

>     if (!ok)

>        return ok;

> 

> I think I went with the implicit style to minimize the lines touched by

> the patch, but I'm happy with either approach.  [If we're bikeshedding,

> would renaming those "ok" to "res" be acceptable also?  "ok" reads to

> me like a "success" value for a status variable, rather than the status

> variable itself; it's presumably meant to be pronounced with a rising

> interrogative as it were a question - "ok?" - but that's not visible in

> the code when reading the usage sites].

> 

> Similarly, the pointer wrappers use an implicit style:

> 

>   // Implicit style:

> 

>   opt_loop_vec_info loop_vinfo

>     = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

>   loop->aux = loop_vinfo;

> 

>   if (!loop_vinfo)

>     if (dump_enabled_p ())

>       if (opt_problem *problem = loop_vinfo.get_problem ())

> 	{

> 

> but maybe an explicit style is more readable:

> 

>   // Explicit style:

> 

>   opt_loop_vec_info opt_loop_vinfo

>     = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

>   loop_vec_info loop_vinfo = loop_vinfo.get_pointer ();

>   loop->aux = loop_vinfo

> 

>   if (opt_loop_vinfo.failed_p ())

>     if (dump_enabled_p ())

>       if (opt_problem *problem = loop_vinfo.get_problem ())

> 	{

> 

> 

> How would you want the code to look?

> 

> Richi: do you have an opinion here?


I wouldn't mind about the explicit variant for the bool but
the explicit for the pointer looks odd.

In general I agree that

 opt_result foo = x;
 bool bar = x;

is confusing but I also like the brevity that is possible
with the automatic conversions so I am biased towards that.

> (or is that style in the patch OK as-is?)


For me it is OK as-is.

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

> 

> OK for trunk?


OK.  My question on 2/3 leaves Richard time to comment.

Maybe you can tackle the issue that -fopt-info-inline does
nothing at the moment and see if opt-problems are a good fit
there as well (reporting the CIF strings).

Thanks for working on all this!
Richard.

> 

> gcc/ChangeLog:

> 	* Makefile.in (OBJS): Add opt-problem.o.

> 	* dump-context.h: Include "selftest.h.

> 	(selftest::temp_dump_context): New forward decl.

> 	(class dump_context): Make friend of class

> 	selftest::temp_dump_context.

> 	(dump_context::dump_loc_immediate): New decl.

> 	(class dump_pretty_printer): Move here from dumpfile.c.

> 	(class temp_dump_context): Move to namespace selftest.

> 	(temp_dump_context::temp_dump_context): Add param

> 	"forcibly_enable_dumping".

> 	(selftest::verify_dumped_text):

> 	(ASSERT_DUMPED_TEXT_EQ): Move here from dumpfile.c.

> 	(selftest::verify_item):

> 	(ASSERT_IS_TEXT): Move here from dumpfile.c.

> 	(ASSERT_IS_TREE): Likewise.

> 	(ASSERT_IS_GIMPLE): Likewise.

> 	* dumpfile.c (dump_context::dump_loc): Move immediate dumping

> 	to...

> 	(dump_context::dump_loc_immediate): ...this new function.

> 	(class dump_pretty_printer): Move to dump-context.h.

> 	(dump_switch_p_1): Don't enable MSG_PRIORITY_REEMITTED.

> 	(opt_info_switch_p_1): Enable MSG_PRIORITY_REEMITTED.

> 	(temp_dump_context::temp_dump_context): Move to "selftest"

> 	namespace.  Add param "forcibly_enable_dumping", and use it to

> 	conditionalize the use of m_pp;

> 	(selftest::verify_dumped_text): Make non-static.

> 	(ASSERT_DUMPED_TEXT_EQ): Move to dump-context.h.

> 	(selftest::verify_item): Make non-static.

> 	(ASSERT_IS_TEXT): Move to dump-context.h.

> 	(ASSERT_IS_TREE): Likewise.

> 	(ASSERT_IS_GIMPLE): Likewise.

> 	(selftest::test_capture_of_dump_calls): Pass "true" for new

> 	param of temp_dump_context.

> 	* dumpfile.h (enum dump_flag): Add MSG_PRIORITY_REEMITTED, adding

> 	it to MSG_ALL_PRIORITIES.  Update values of TDF_COMPARE_DEBUG and

> 	TDF_COMPARE_DEBUG.

> 	* opt-problem.cc: New file.

> 	* opt-problem.h: New file.

> 	* optinfo-emit-json.cc

> 	(selftest::test_building_json_from_dump_calls): Pass "true" for

> 	new param of temp_dump_context.

> 	* optinfo.cc (optinfo_kind_to_dump_flag): New function.

> 	(optinfo::emit_for_opt_problem): New function.

> 	(optinfo::emit): Clarity which emit_item is used.

> 	* optinfo.h (optinfo::get_dump_location): New accessor.

> 	(optinfo::emit_for_opt_problem): New decl.

> 	(optinfo::emit): Make const.

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

> 	selftest::opt_problem_cc_tests.

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

> 	* tree-data-ref.c (dr_analyze_innermost): Convert return type from

> 	bool to opt_result, converting fprintf messages to

> 	opt_result::failure_at calls.  Add "stmt" param for use by the

> 	failure_at calls.

> 	(create_data_ref): Pass "stmt" to the dr_analyze_innermost call.

> 	(runtime_alias_check_p): Convert return type from bool to

> 	opt_result, converting dump_printf calls to

> 	opt_result::failure_at, using the statement DDR_A for their

> 	location.

> 	(find_data_references_in_stmt): Convert return type from bool to

> 	opt_result, converting "return false" to opt_result::failure_at

> 	with a new message.

> 	* tree-data-ref.h: Include "opt-problem.h".

> 	(dr_analyze_innermost): Convert return type from bool to opt_result,

> 	and add a const gimple * param.

> 	(find_data_references_in_stmt): Convert return type from bool to

> 	opt_result.

> 	(runtime_alias_check_p): Likewise.

> 	* tree-predcom.c (find_looparound_phi): Pass "init_stmt" to

> 	dr_analyze_innermost.

> 	* tree-vect-data-refs.c (vect_mark_for_runtime_alias_test):

> 	Convert return type from bool to opt_result, adding a message for

> 	the PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS zero case.

> 	(vect_analyze_data_ref_dependence): Convert return type from bool

> 	to opt_result.  Change sense of return type from "false"

> 	effectively meaning "no problems" to "false" meaning a problem,

> 	so that "return false" becomes "return opt_result::success".

> 	Convert "return true" calls to opt_result::failure_at, using

> 	the location of statement A rather than vect_location.

> 	(vect_analyze_data_ref_dependences): Convert return type from bool

> 	to opt_result.

> 	(verify_data_ref_alignment): Likewise, converting dump_printf_loc

> 	calls to opt_result::failure_at, using the stmt location rather

> 	than vect_location.

> 	(vect_verify_datarefs_alignment): Convert return type from bool

> 	to opt_result.

> 	(vect_enhance_data_refs_alignment): Likewise.  Split local "stat"

> 	into multiple more-tightly-scoped copies.

> 	(vect_analyze_data_refs_alignment): Convert return type from bool

> 	to opt_result.

> 	(vect_analyze_data_ref_accesses): Likewise, converting a

> 	"return false" to a "return opt_result::failure_at", adding a

> 	new message.

> 	(vect_prune_runtime_alias_test_list): Convert return type from

> 	bool to opt_result, converting dump_printf_loc to

> 	opt_result::failure_at.  Add a %G to show the pertinent statement,

> 	and use the stmt's location rather than vect_location.

> 	(vect_find_stmt_data_reference): Convert return type from

> 	bool to opt_result, converting dump_printf_loc to

> 	opt_result::failure_at, using stmt's location.

> 	(vect_analyze_data_refs):  Convert return type from bool to

> 	opt_result.  Convert "return false" to "return

> 	opt_result::failure_at", adding messages as needed.

> 	* tree-vect-loop.c (vect_determine_vf_for_stmt_1): Convert return

> 	type from bool to opt_result.

> 	(vect_determine_vf_for_stmt): Likewise.

> 	(vect_determine_vectorization_factor): Likewise, converting

> 	dump_printf_loc to opt_result::failure_at, using location of phi

> 	rather than vect_location.

> 	(vect_analyze_loop_form_1): Convert return type from bool to

> 	opt_result, converting dump_printf_loc calls, retaining the use of

> 	vect_location.

> 	(vect_analyze_loop_form): Convert return type from loop_vec_info

> 	to opt_loop_vec_info.

> 	(vect_analyze_loop_operations): Convert return type from bool to

> 	opt_result, converting dump_printf_loc calls, using the location

> 	of phi/stmt rather than vect_location where available.  Convert

> 	various "return false" to "return opt_result::failure_at" with

> 	"unsupported phi" messages.

> 	(vect_get_datarefs_in_loop): Convert return type from bool to

> 	opt_result.  Add a message for the

> 	PARAM_LOOP_MAX_DATAREFS_FOR_DATADEPS failure.

> 	(vect_analyze_loop_2): Convert return type from bool to

> 	opt_result.  Ensure "ok" is set to a opt_result::failure_at before

> 	each "goto again;", adding new messages where needed.

> 	Add "unsupported grouped {store|load}" messages.

> 	(vect_analyze_loop): Convert return type from loop_vec_info to

> 	opt_loop_vec_info.

> 	* tree-vect-slp.c (vect_analyze_slp): Convert return type from

> 	bool to opt_result.

> 	* tree-vect-stmts.c (process_use): Likewise, converting

> 	dump_printf_loc call and using stmt location, rather than

> 	vect_location.

> 	(vect_mark_stmts_to_be_vectorized): Likeise.

> 	(vect_analyze_stmt): Likewise, adding a %G.

> 	(vect_get_vector_types_for_stmt): Convert return type from bool to

> 	opt_result, converting dump_printf_loc calls and using stmt

> 	location, rather than vect_location.

> 	(vect_get_mask_type_for_stmt): Convert return type from tree to

> 	opt_tree, converting dump_printf_loc calls and using stmt location.

> 	* tree-vectorizer.c: Include "opt-problem.h.

> 	(try_vectorize_loop_1): Flag "Analyzing loop at" dump message as

> 	MSG_PRIORITY_INTERNALS.  Convert local "loop_vinfo" from

> 	loop_vec_info to opt_loop_vec_info.  If if fails, and dumping is

> 	enabled, use it to report at the top level "couldn't vectorize

> 	loop" followed by the problem.

> 	* tree-vectorizer.h (opt_loop_vec_info): New typedef.

> 	(vect_mark_stmts_to_be_vectorized): Convert return type from bool

> 	to opt_result.

> 	(vect_analyze_stmt): Likewise.

> 	(vect_get_vector_types_for_stmt): Likewise.

> 	(tree vect_get_mask_type_for_stmt): Likewise.

> 	(vect_analyze_data_ref_dependences): Likewise.

> 	(vect_enhance_data_refs_alignment): Likewise.

> 	(vect_analyze_data_refs_alignment): Likewise.

> 	(vect_verify_datarefs_alignment): Likewise.

> 	(vect_analyze_data_ref_accesses): Likewise.

> 	(vect_prune_runtime_alias_test_list): Likewise.

> 	(vect_find_stmt_data_reference): Likewise.

> 	(vect_analyze_data_refs): Likewise.

> 	(vect_analyze_loop): Convert return type from loop_vec_info to

> 	opt_loop_vec_info.

> 	(vect_analyze_loop_form): Likewise.

> 	(vect_analyze_slp): Convert return type from bool to opt_result.

> 

> gcc/testsuite/ChangeLog:

> 	* gcc.dg/vect/nodump-vect-opt-info-2.c: New test.

> 	* gcc.dg/vect/vect-alias-check-4.c: Add "-fopt-info-vec-all" to

> 	dg-additional-options.  Add dg-message and dg-missed directives

> 	to verify that -fopt-info messages are written at the correct

> 	locations.

> ---

>  gcc/Makefile.in                                    |   1 +

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

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

>  gcc/dumpfile.h                                     |  13 +-

>  gcc/opt-problem.cc                                 | 335 ++++++++++++++++

>  gcc/opt-problem.h                                  | 289 ++++++++++++++

>  gcc/optinfo-emit-json.cc                           |   2 +-

>  gcc/optinfo.cc                                     |  44 +-

>  gcc/optinfo.h                                      |   7 +-

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

>  gcc/selftest.h                                     |   1 +

>  gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c |  12 +

>  gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c     |  18 +-

>  gcc/tree-data-ref.c                                |  70 ++--

>  gcc/tree-data-ref.h                                |  10 +-

>  gcc/tree-predcom.c                                 |   3 +-

>  gcc/tree-vect-data-refs.c                          | 347 ++++++++--------

>  gcc/tree-vect-loop.c                               | 442 +++++++++------------

>  gcc/tree-vect-slp.c                                |   4 +-

>  gcc/tree-vect-stmts.c                              | 275 ++++++-------

>  gcc/tree-vectorizer.c                              |  17 +-

>  gcc/tree-vectorizer.h                              |  45 ++-

>  22 files changed, 1408 insertions(+), 792 deletions(-)

>  create mode 100644 gcc/opt-problem.cc

>  create mode 100644 gcc/opt-problem.h

>  create mode 100644 gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c

> 

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

> index 4b7cec8..116ed6e 100644

> --- a/gcc/Makefile.in

> +++ b/gcc/Makefile.in

> @@ -1423,6 +1423,7 @@ OBJS = \

>  	omp-grid.o \

>  	omp-low.o \

>  	omp-simd-clone.o \

> +	opt-problem.o \

>  	optabs.o \

>  	optabs-libfuncs.o \

>  	optabs-query.o \

> diff --git a/gcc/dump-context.h b/gcc/dump-context.h

> index 20b94a7..3a45f23 100644

> --- a/gcc/dump-context.h

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

> @@ -24,6 +24,9 @@ along with GCC; see the file COPYING3.  If not see

>  

>  #include "dumpfile.h"

>  #include "pretty-print.h"

> +#include "selftest.h"

> +

> +namespace selftest { class temp_dump_context; }

>  

>  /* A class for handling the various dump_* calls.

>  

> @@ -36,7 +39,8 @@ along with GCC; see the file COPYING3.  If not see

>  

>  class dump_context

>  {

> -  friend class temp_dump_context;

> +  friend class selftest::temp_dump_context;

> +

>   public:

>    static dump_context &get () { return *s_current; }

>  

> @@ -45,6 +49,7 @@ class dump_context

>    void refresh_dumps_are_enabled ();

>  

>    void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);

> +  void dump_loc_immediate (dump_flags_t dump_kind, const dump_location_t &loc);

>  

>    void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,

>  			 gimple *gs, int spc);

> @@ -129,8 +134,53 @@ class dump_context

>    static dump_context s_default;

>  };

>  

> +/* A subclass of pretty_printer for implementing dump_context::dump_printf_va.

> +   In particular, the formatted chunks are captured as optinfo_item instances,

> +   thus retaining metadata about the entities being dumped (e.g. source

> +   locations), rather than just as plain text.  */

> +

> +class dump_pretty_printer : public pretty_printer

> +{

> +public:

> +  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);

> +

> +  void emit_items (optinfo *dest);

> +

> +private:

> +  /* Information on an optinfo_item that was generated during phase 2 of

> +     formatting.  */

> +  struct stashed_item

> +  {

> +    stashed_item (const char **buffer_ptr_, optinfo_item *item_)

> +      : buffer_ptr (buffer_ptr_), item (item_) {}

> +    const char **buffer_ptr;

> +    optinfo_item *item;

> +  };

> +

> +  static bool format_decoder_cb (pretty_printer *pp, text_info *text,

> +				 const char *spec, int /*precision*/,

> +				 bool /*wide*/, bool /*set_locus*/,

> +				 bool /*verbose*/, bool */*quoted*/,

> +				 const char **buffer_ptr);

> +

> +  bool decode_format (text_info *text, const char *spec,

> +		      const char **buffer_ptr);

> +

> +  void stash_item (const char **buffer_ptr, optinfo_item *item);

> +

> +  void emit_any_pending_textual_chunks (optinfo *dest);

> +

> +  void emit_item (optinfo_item *item, optinfo *dest);

> +

> +  dump_context *m_context;

> +  dump_flags_t m_dump_kind;

> +  auto_vec<stashed_item> m_stashed_items;

> +};

> +

>  #if CHECKING_P

>  

> +namespace selftest {

> +

>  /* An RAII-style class for use in selftests for temporarily using a different

>     dump_context.  */

>  

> @@ -138,6 +188,7 @@ class temp_dump_context

>  {

>   public:

>    temp_dump_context (bool forcibly_enable_optinfo,

> +		     bool forcibly_enable_dumping,

>  		     dump_flags_t test_pp_flags);

>    ~temp_dump_context ();

>  

> @@ -151,6 +202,57 @@ class temp_dump_context

>    dump_context *m_saved;

>  };

>  

> +/* Implementation detail of ASSERT_DUMPED_TEXT_EQ.  */

> +

> +extern void verify_dumped_text (const location &loc,

> +				temp_dump_context *context,

> +				const char *expected_text);

> +

> +/* Verify that the text dumped so far in CONTEXT equals

> +   EXPECTED_TEXT.

> +   As a side-effect, the internal buffer is 0-terminated.  */

> +

> +#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)			\

> +  SELFTEST_BEGIN_STMT							\

> +    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \

> +  SELFTEST_END_STMT

> +

> +

> +/* Verify that ITEM has the expected values.  */

> +

> +void

> +verify_item (const location &loc,

> +	     const optinfo_item *item,

> +	     enum optinfo_item_kind expected_kind,

> +	     location_t expected_location,

> +	     const char *expected_text);

> +

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

> +

> +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \

> +  SELFTEST_BEGIN_STMT						    \

> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \

> +		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \

> +  SELFTEST_END_STMT

> +

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

> +

> +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \

> +  SELFTEST_BEGIN_STMT						    \

> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \

> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \

> +  SELFTEST_END_STMT

> +

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

> +

> +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \

> +  SELFTEST_BEGIN_STMT						    \

> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \

> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \

> +  SELFTEST_END_STMT

> +

> +} // namespace selftest

> +

>  #endif /* CHECKING_P */

>  

>  #endif /* GCC_DUMP_CONTEXT_H */

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

> index e15edc7..0b140ff 100644

> --- a/gcc/dumpfile.c

> +++ b/gcc/dumpfile.c

> @@ -562,6 +562,21 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)

>  {

>    end_any_optinfo ();

>  

> +  dump_loc_immediate (dump_kind, loc);

> +

> +  if (optinfo_enabled_p ())

> +    {

> +      optinfo &info = begin_next_optinfo (loc);

> +      info.handle_dump_file_kind (dump_kind);

> +    }

> +}

> +

> +/* As dump_loc above, but without starting a new optinfo. */

> +

> +void

> +dump_context::dump_loc_immediate (dump_flags_t dump_kind,

> +				  const dump_location_t &loc)

> +{

>    location_t srcloc = loc.get_location_t ();

>  

>    if (dump_file && apply_dump_filter_p (dump_kind, pflags))

> @@ -573,12 +588,6 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)

>    /* Support for temp_dump_context in selftests.  */

>    if (m_test_pp && apply_dump_filter_p (dump_kind, m_test_pp_flags))

>      ::dump_loc (dump_kind, m_test_pp, srcloc);

> -

> -  if (optinfo_enabled_p ())

> -    {

> -      optinfo &info = begin_next_optinfo (loc);

> -      info.handle_dump_file_kind (dump_kind);

> -    }

>  }

>  

>  /* Make an item for the given dump call, equivalent to print_gimple_stmt.  */

> @@ -739,49 +748,6 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,

>    dump_generic_expr (dump_kind, extra_dump_flags, t);

>  }

>  

> -/* A subclass of pretty_printer for implementing dump_context::dump_printf_va.

> -   In particular, the formatted chunks are captured as optinfo_item instances,

> -   thus retaining metadata about the entities being dumped (e.g. source

> -   locations), rather than just as plain text.  */

> -

> -class dump_pretty_printer : public pretty_printer

> -{

> -public:

> -  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);

> -

> -  void emit_items (optinfo *dest);

> -

> -private:

> -  /* Information on an optinfo_item that was generated during phase 2 of

> -     formatting.  */

> -  struct stashed_item

> -  {

> -    stashed_item (const char **buffer_ptr_, optinfo_item *item_)

> -      : buffer_ptr (buffer_ptr_), item (item_) {}

> -    const char **buffer_ptr;

> -    optinfo_item *item;

> -  };

> -

> -  static bool format_decoder_cb (pretty_printer *pp, text_info *text,

> -				 const char *spec, int /*precision*/,

> -				 bool /*wide*/, bool /*set_locus*/,

> -				 bool /*verbose*/, bool */*quoted*/,

> -				 const char **buffer_ptr);

> -

> -  bool decode_format (text_info *text, const char *spec,

> -		      const char **buffer_ptr);

> -

> -  void stash_item (const char **buffer_ptr, optinfo_item *item);

> -

> -  void emit_any_pending_textual_chunks (optinfo *dest);

> -

> -  void emit_item (optinfo_item *item, optinfo *dest);

> -

> -  dump_context *m_context;

> -  dump_flags_t m_dump_kind;

> -  auto_vec<stashed_item> m_stashed_items;

> -};

> -

>  /* dump_pretty_printer's ctor.  */

>  

>  dump_pretty_printer::dump_pretty_printer (dump_context *context,

> @@ -1732,7 +1698,12 @@ dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob)

>      return 0;

>  

>    ptr = option_value;

> -  flags = MSG_ALL_PRIORITIES;

> +

> +  /* Retain "user-facing" and "internals" messages, but filter out

> +     those from an opt_problem being re-emitted at the top level

> +     (MSG_PRIORITY_REEMITTED), so as to avoid duplicate messages

> +     messing up scan-tree-dump-times" in DejaGnu tests.  */

> +  flags = MSG_PRIORITY_USER_FACING | MSG_PRIORITY_INTERNALS;

>  

>    while (*ptr)

>      {

> @@ -1830,8 +1801,9 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags,

>    *filename = NULL;

>  

>    /* Default to filtering out "internals" messages, and retaining

> -     "user-facing" messages.  */

> -  *flags = MSG_PRIORITY_USER_FACING;

> +     "user-facing" messages, and those from an opt_problem being

> +     re-emitted at the top level.  */

> +  *flags = MSG_PRIORITY_USER_FACING | MSG_PRIORITY_REEMITTED;

>  

>    *optgroup_flags = OPTGROUP_NONE;

>  

> @@ -1981,19 +1953,26 @@ enable_rtl_dump_file (void)

>  

>  #if CHECKING_P

>  

> +namespace selftest {

> +

>  /* temp_dump_context's ctor.  Temporarily override the dump_context

>     (to forcibly enable optinfo-generation).  */

>  

>  temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo,

> +				      bool forcibly_enable_dumping,

>  				      dump_flags_t test_pp_flags)

> -

>  : m_context (),

>    m_saved (&dump_context ().get ())

>  {

>    dump_context::s_current = &m_context;

>    m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;

> -  m_context.m_test_pp = &m_pp;

> -  m_context.m_test_pp_flags = test_pp_flags;

> +  /* Conditionally enable the test dump, so that we can verify both the

> +     dump_enabled_p and the !dump_enabled_p cases in selftests.  */

> +  if (forcibly_enable_dumping)

> +    {

> +      m_context.m_test_pp = &m_pp;

> +      m_context.m_test_pp_flags = test_pp_flags;

> +    }

>  

>    dump_context::get ().refresh_dumps_are_enabled ();

>  }

> @@ -2015,8 +1994,6 @@ temp_dump_context::get_dumped_text ()

>    return pp_formatted_text (&m_pp);

>  }

>  

> -namespace selftest {

> -

>  /* Verify that the dump_location_t constructors capture the source location

>     at which they were called (provided that the build compiler is sufficiently

>     recent).  */

> @@ -2055,7 +2032,7 @@ test_impl_location ()

>     EXPECTED_TEXT, using LOC for the location of any failure.

>     As a side-effect, the internal buffer is 0-terminated.  */

>  

> -static void

> +void

>  verify_dumped_text (const location &loc,

>  		    temp_dump_context *context,

>  		    const char *expected_text)

> @@ -2065,18 +2042,9 @@ verify_dumped_text (const location &loc,

>  		   expected_text);

>  }

>  

> -/* Verify that the text dumped so far in CONTEXT equals

> -   EXPECTED_TEXT.

> -   As a side-effect, the internal buffer is 0-terminated.  */

> -

> -#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)			\

> -  SELFTEST_BEGIN_STMT							\

> -    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \

> -  SELFTEST_END_STMT

> -

>  /* Verify that ITEM has the expected values.  */

>  

> -static void

> +void

>  verify_item (const location &loc,

>  	     const optinfo_item *item,

>  	     enum optinfo_item_kind expected_kind,

> @@ -2088,30 +2056,6 @@ verify_item (const location &loc,

>    ASSERT_STREQ_AT (loc, item->get_text (), expected_text);

>  }

>  

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

> -

> -#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \

> -  SELFTEST_BEGIN_STMT						    \

> -    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \

> -		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \

> -  SELFTEST_END_STMT

> -

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

> -

> -#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \

> -  SELFTEST_BEGIN_STMT						    \

> -    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \

> -		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \

> -  SELFTEST_END_STMT

> -

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

> -

> -#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \

> -  SELFTEST_BEGIN_STMT						    \

> -    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \

> -		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \

> -  SELFTEST_END_STMT

> -

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

>     optimization records. */

>  

> @@ -2144,7 +2088,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Test of dump_printf.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");

>  

> @@ -2161,7 +2105,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Test of dump_printf with %T.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);

>  

> @@ -2179,7 +2123,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Test of dump_printf with %E.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf (MSG_NOTE, "gimple: %E", stmt);

>  

> @@ -2197,7 +2141,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Test of dump_printf with %G.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf (MSG_NOTE, "gimple: %G", stmt);

>  

> @@ -2220,7 +2164,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  	 - multiple dump-specific format codes: some consecutive, others

>  	 separated by text, trailing text after the final one.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf_loc (MSG_NOTE, loc, "before %T and %T"

>  			 " %i consecutive %E%E after\n",

> @@ -2248,7 +2192,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Tree, via dump_generic_expr.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_printf_loc (MSG_NOTE, loc, "test of tree: ");

>  	dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);

> @@ -2268,7 +2212,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* Tree, via dump_generic_expr_loc.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);

>  

> @@ -2288,7 +2232,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>        {

>  	/* dump_gimple_stmt_loc.  */

>  	{

> -	  temp_dump_context tmp (with_optinfo,

> +	  temp_dump_context tmp (with_optinfo, true,

>  				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	  dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);

>  

> @@ -2304,7 +2248,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>  	/* dump_gimple_stmt.  */

>  	{

> -	  temp_dump_context tmp (with_optinfo,

> +	  temp_dump_context tmp (with_optinfo, true,

>  				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);

>  

> @@ -2320,7 +2264,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>  	/* dump_gimple_expr_loc.  */

>  	{

> -	  temp_dump_context tmp (with_optinfo,

> +	  temp_dump_context tmp (with_optinfo, true,

>  				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	  dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);

>  

> @@ -2336,7 +2280,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>  	/* dump_gimple_expr.  */

>  	{

> -	  temp_dump_context tmp (with_optinfo,

> +	  temp_dump_context tmp (with_optinfo, true,

>  				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	  dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);

>  

> @@ -2353,7 +2297,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>        /* poly_int.  */

>        {

> -	temp_dump_context tmp (with_optinfo,

> +	temp_dump_context tmp (with_optinfo, true,

>  			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);

>  	dump_dec (MSG_NOTE, poly_int64 (42));

>  

> @@ -2378,7 +2322,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  	  if (j / 2)

>  	    dump_filter |= MSG_PRIORITY_INTERNALS;

>  

> -	  temp_dump_context tmp (with_optinfo, dump_filter);

> +	  temp_dump_context tmp (with_optinfo, true, dump_filter);

>  	  /* Emit various messages, mostly with implicit priority.  */

>  	  dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");

>  	  dump_printf_loc (MSG_NOTE | MSG_PRIORITY_INTERNALS, stmt,

> @@ -2460,7 +2404,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>    {

>      /* MSG_OPTIMIZED_LOCATIONS.  */

>      {

> -      temp_dump_context tmp (true, MSG_ALL_KINDS);

> +      temp_dump_context tmp (true, true, MSG_ALL_KINDS);

>        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");

>        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),

>  		 OPTINFO_KIND_SUCCESS);

> @@ -2468,7 +2412,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>      /* MSG_MISSED_OPTIMIZATION.  */

>      {

> -      temp_dump_context tmp (true, MSG_ALL_KINDS);

> +      temp_dump_context tmp (true, true, MSG_ALL_KINDS);

>        dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");

>        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),

>  		 OPTINFO_KIND_FAILURE);

> @@ -2477,7 +2421,7 @@ test_capture_of_dump_calls (const line_table_case &case_)

>  

>    /* Verify that MSG_* affect AUTO_DUMP_SCOPE and the dump calls.  */

>    {

> -    temp_dump_context tmp (false,

> +    temp_dump_context tmp (false, true,

>  			   MSG_OPTIMIZED_LOCATIONS | MSG_ALL_PRIORITIES);

>      dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");

>      {

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

> index 5933905..c82157d 100644

> --- a/gcc/dumpfile.h

> +++ b/gcc/dumpfile.h

> @@ -179,15 +179,22 @@ enum dump_flag

>    /* Implicitly supplied for messages within nested dump scopes.  */

>    MSG_PRIORITY_INTERNALS = (1 << 26),

>  

> +  /* Supplied when an opt_problem generated in a nested scope is re-emitted

> +     at the top-level.   We want to default to showing these in -fopt-info

> +     output, but to *not* show them in dump files, as the message would be

> +     shown twice, messing up "scan-tree-dump-times" in DejaGnu tests.  */

> +  MSG_PRIORITY_REEMITTED = (1 << 27),

> +

>    /* Mask for selecting MSG_PRIORITY_* flags.  */

>    MSG_ALL_PRIORITIES = (MSG_PRIORITY_USER_FACING

> -			| MSG_PRIORITY_INTERNALS),

> +			| MSG_PRIORITY_INTERNALS

> +			| MSG_PRIORITY_REEMITTED),

>  

>    /* Dumping for -fcompare-debug.  */

> -  TDF_COMPARE_DEBUG = (1 << 27),

> +  TDF_COMPARE_DEBUG = (1 << 28),

>  

>    /* All values.  */

> -  TDF_ALL_VALUES = (1 << 28) - 1

> +  TDF_ALL_VALUES = (1 << 29) - 1

>  };

>  

>  /* Dump flags type.  */

> diff --git a/gcc/opt-problem.cc b/gcc/opt-problem.cc

> new file mode 100644

> index 0000000..dad3a8c

> --- /dev/null

> +++ b/gcc/opt-problem.cc

> @@ -0,0 +1,335 @@

> +/* Rich optional information on why an optimization wasn't possible.

> +   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 "pretty-print.h"

> +#include "opt-problem.h"

> +#include "dump-context.h"

> +#include "tree-pass.h"

> +#include "selftest.h"

> +

> +/* opt_problem's ctor.

> +

> +   Use FMT and AP to emit a message to the "immediate" dump destinations

> +   as if via:

> +     dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, ...)

> +

> +   The optinfo_item instances are not emitted yet.  Instead, they

> +   are retained internally so that the message can be replayed and

> +   emitted when this problem is handled, higher up the call stack.  */

> +

> +opt_problem::opt_problem (const dump_location_t &loc,

> +			  const char *fmt, va_list *ap)

> +: m_optinfo (loc, OPTINFO_KIND_FAILURE, current_pass)

> +{

> +  /* We shouldn't be bothering to construct these objects if

> +     dumping isn't enabled.  */

> +  gcc_assert (dump_enabled_p ());

> +

> +  /* Update the singleton.  */

> +  delete s_the_problem;

> +  s_the_problem = this;

> +

> +  /* Print the location to the "immediate" dump destinations.  */

> +  dump_context &dc = dump_context::get ();

> +  dc.dump_loc (MSG_MISSED_OPTIMIZATION, loc);

> +

> +  /* Print the formatted string to this opt_problem's optinfo, dumping

> +     the items to the "immediate" dump destinations, and storing items

> +     for later retrieval.  */

> +  {

> +    dump_pretty_printer pp (&dump_context::get (), MSG_MISSED_OPTIMIZATION);

> +

> +    text_info text;

> +    text.err_no = errno;

> +    text.args_ptr = ap;

> +    text.format_spec = fmt; /* No i18n is performed.  */

> +

> +    /* Phases 1 and 2, using pp_format.  */

> +    pp_format (&pp, &text);

> +

> +    /* Phase 3: dump the items to the "immediate" dump destinations,

> +       and storing them into m_optinfo for later retrieval.  */

> +    pp.emit_items (&m_optinfo);

> +  }

> +}

> +

> +/* Emit this problem and delete it, clearing the current opt_problem.  */

> +

> +void

> +opt_problem::emit_and_clear ()

> +{

> +  gcc_assert (this == s_the_problem);

> +

> +  m_optinfo.emit_for_opt_problem ();

> +

> +  delete this;

> +  s_the_problem = NULL;

> +}

> +

> +/* The singleton opt_problem *.  */

> +

> +opt_problem *opt_problem::s_the_problem;

> +

> +#if CHECKING_P

> +

> +namespace selftest {

> +

> +static opt_result

> +function_that_succeeds ()

> +{

> +  return opt_result::success ();

> +}

> +

> +/* Verify that opt_result::success works.  */

> +

> +static void

> +test_opt_result_success ()

> +{

> +  /* Run all tests twice, with and then without dumping enabled.  */

> +  for (int i = 0 ; i < 2; i++)

> +    {

> +      bool with_dumping = (i == 0);

> +

> +      temp_dump_context tmp (with_dumping, with_dumping,

> +			     MSG_ALL_KINDS | MSG_ALL_PRIORITIES);

> +

> +      if (with_dumping)

> +	gcc_assert (dump_enabled_p ());

> +      else

> +	gcc_assert (!dump_enabled_p ());

> +

> +      opt_result res = function_that_succeeds ();

> +

> +      /* Verify that "success" can be used as a "true" boolean.  */

> +      ASSERT_TRUE (res);

> +

> +      /* Verify the underlying opt_wrapper<bool>.  */

> +      ASSERT_TRUE (res.get_result ());

> +      ASSERT_EQ (res.get_problem (), NULL);

> +

> +      /* Nothing should have been dumped.  */

> +      ASSERT_DUMPED_TEXT_EQ (tmp, "");

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

> +      ASSERT_EQ (info, NULL);

> +    }

> +}

> +

> +/* Example of a function that fails, with a non-trivial

> +   pre-canned error message.  */

> +

> +static opt_result

> +function_that_fails (const greturn *stmt)

> +{

> +  gcc_assert (stmt);

> +  gcc_assert (gimple_return_retval (stmt));

> +

> +  AUTO_DUMP_SCOPE ("function_that_fails", stmt);

> +

> +  return opt_result::failure_at (stmt,

> +				 "can't handle return type: %T for stmt: %G",

> +				 TREE_TYPE (gimple_return_retval (stmt)),

> +				 static_cast <const gimple *> (stmt));

> +}

> +

> +/* Example of a function that indirectly fails.  */

> +

> +static opt_result

> +function_that_indirectly_fails (const greturn *stmt)

> +{

> +  AUTO_DUMP_SCOPE ("function_that_indirectly_fails", stmt);

> +

> +  opt_result res = function_that_fails (stmt);

> +  if (!res)

> +    return res;

> +  return opt_result::success ();

> +}

> +

> +/* Verify that opt_result::failure_at works.

> +   Simulate a failure handling a stmt at one location whilst considering

> +   an optimization that's notionally at another location (as a microcosm

> +   of e.g. a problematic statement within a loop that prevents loop

> +   vectorization).  */

> +

> +static void

> +test_opt_result_failure_at (const line_table_case &case_)

> +{

> +  /* Generate a location_t for testing.  */

> +  line_table_test ltt (case_);

> +  const line_map_ordinary *ord_map

> +    = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,

> +					   "test.c", 0));

> +  linemap_line_start (line_table, 5, 100);

> +

> +  /* A test location: "test.c:5:10".  */

> +  const location_t line_5 = linemap_position_for_column (line_table, 10);

> +

> +  /* Another test location: "test.c:6:12".  */

> +  const location_t line_6

> +    = linemap_position_for_line_and_column (line_table, ord_map, 6, 12);

> +

> +  if (line_6 > LINE_MAP_MAX_LOCATION_WITH_COLS)

> +    return;

> +

> +  /* Generate statements using "line_5" and "line_6" for testing.  */

> +  greturn *stmt_at_5 = gimple_build_return (integer_one_node);

> +  gimple_set_location (stmt_at_5, line_5);

> +

> +  greturn *stmt_at_6 = gimple_build_return (integer_zero_node);

> +  gimple_set_location (stmt_at_6, line_6);

> +

> +  /* Run with and then without dumping enabled.  */

> +  for (int i = 0; i < 2; i++)

> +    {

> +      bool with_dumping = (i == 0);

> +

> +      /* Run with all 4 combinations of

> +	 with and without MSG_PRIORITY_INTERNALS and

> +	 with and without MSG_PRIORITY_REEMITTED.  */

> +      for (int j = 0; j < 4; j++)

> +	{

> +	  dump_flags_t filter = MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING;

> +	  if (j / 2)

> +	    filter |= MSG_PRIORITY_INTERNALS;

> +	  if (j % 2)

> +	    filter |= MSG_PRIORITY_REEMITTED;

> +

> +	  temp_dump_context tmp (with_dumping, with_dumping, filter);

> +

> +	  if (with_dumping)

> +	    gcc_assert (dump_enabled_p ());

> +	  else

> +	    gcc_assert (!dump_enabled_p ());

> +

> +	  /* Simulate attempting to optimize "stmt_at_6".  */

> +	  opt_result res = function_that_indirectly_fails (stmt_at_6);

> +

> +	  /* Verify that "failure" can be used as a "false" boolean.  */

> +	  ASSERT_FALSE (res);

> +

> +	  /* Verify the underlying opt_wrapper<bool>.  */

> +	  ASSERT_FALSE (res.get_result ());

> +	  opt_problem *problem = res.get_problem ();

> +

> +	  if (with_dumping)

> +	    {

> +	      ASSERT_NE (problem, NULL);

> +	      ASSERT_EQ (problem->get_dump_location ().get_location_t (),

> +			 line_6);

> +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)

> +	      /* Verify that the problem captures the implementation location

> +		 it was emitted from.  */

> +	      const dump_impl_location_t &impl_location

> +		= problem->get_dump_location ().get_impl_location ();

> +	      ASSERT_STR_CONTAINS (impl_location.m_function,

> +				   "function_that_fails");

> +#endif

> +

> +	      /* Verify that the underlying dump items are retained in the

> +		 opt_problem.  */

> +	      const optinfo &info = problem->get_optinfo ();

> +	      ASSERT_EQ (info.get_dump_location ().get_location_t (), line_6);

> +	      ASSERT_EQ (info.num_items (), 4);

> +	      ASSERT_IS_TEXT (info.get_item (0), "can't handle return type: ");

> +	      ASSERT_IS_TREE (info.get_item (1), UNKNOWN_LOCATION, "int");

> +	      ASSERT_IS_TEXT (info.get_item (2), " for stmt: ");

> +	      ASSERT_IS_GIMPLE (info.get_item (3), line_6, "return 0;\n");

> +

> +	      /* ...but not in the dump_context's pending_optinfo.  */

> +	      ASSERT_EQ (tmp.get_pending_optinfo (), NULL);

> +

> +	      /* Simulate emitting a high-level summary message, followed

> +		 by the problem.  */

> +	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt_at_5,

> +			       "can't optimize loop\n");

> +	      problem->emit_and_clear ();

> +	      ASSERT_EQ (res.get_problem (), NULL);

> +

> +	      /* Verify that the error message was dumped (when the failure

> +		 occurred).  We can't use a switch here as not all of the

> +		 values are const expressions (using C++98).  */

> +	      dump_flags_t effective_filter

> +		= filter & (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED);

> +	      if (effective_filter

> +		  == (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED))

> +		/* The -fopt-info-internals case.  */

> +		ASSERT_DUMPED_TEXT_EQ

> +		  (tmp,

> +		   "test.c:6:12: note:  === function_that_indirectly_fails"

> +		   " ===\n"

> +		   "test.c:6:12: note:   === function_that_fails ===\n"

> +		   "test.c:6:12: missed:   can't handle return type: int"

> +		   " for stmt: return 0;\n"

> +		   "test.c:5:10: missed: can't optimize loop\n"

> +		   "test.c:6:12: missed: can't handle return type: int"

> +		   " for stmt: return 0;\n");

> +	      else if (effective_filter == MSG_PRIORITY_INTERNALS)

> +		/* The default for dump files.  */

> +		ASSERT_DUMPED_TEXT_EQ

> +		  (tmp,

> +		   "test.c:6:12: note:  === function_that_indirectly_fails"

> +		   " ===\n"

> +		   "test.c:6:12: note:   === function_that_fails ===\n"

> +		   "test.c:6:12: missed:   can't handle return type: int"

> +		     " for stmt: return 0;\n"

> +		   "test.c:5:10: missed: can't optimize loop\n");

> +	      else if (effective_filter == MSG_PRIORITY_REEMITTED)

> +		/* The default when -fopt-info is enabled.  */

> +		ASSERT_DUMPED_TEXT_EQ

> +		  (tmp,

> +		   "test.c:5:10: missed: can't optimize loop\n"

> +		   "test.c:6:12: missed: can't handle return type: int"

> +		   " for stmt: return 0;\n");

> +	      else

> +		{

> +		  gcc_assert (effective_filter == 0);

> +		  ASSERT_DUMPED_TEXT_EQ

> +		    (tmp,

> +		     "test.c:5:10: missed: can't optimize loop\n");

> +		}

> +	    }

> +	  else

> +	    {

> +	      /* If dumping was disabled, then no problem should have been

> +		 created, and nothing should have been dumped.  */

> +	      ASSERT_EQ (problem, NULL);

> +	      ASSERT_DUMPED_TEXT_EQ (tmp, "");

> +	    }

> +	}

> +    }

> +}

> +

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

> +

> +void

> +opt_problem_cc_tests ()

> +{

> +  test_opt_result_success ();

> +  for_each_line_table_case (test_opt_result_failure_at);

> +}

> +

> +} // namespace selftest

> +

> +#endif /* CHECKING_P */

> diff --git a/gcc/opt-problem.h b/gcc/opt-problem.h

> new file mode 100644

> index 0000000..68d7e4a

> --- /dev/null

> +++ b/gcc/opt-problem.h

> @@ -0,0 +1,289 @@

> +/* Rich information on why an optimization wasn't possible.

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

> +#define GCC_OPT_PROBLEM_H

> +

> +#include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG.  */

> +#include "optinfo.h" /* for optinfo.  */

> +

> +/* This header declares a family of wrapper classes for tracking a

> +   success/failure value, while optionally supporting propagating an

> +   opt_problem * describing any failure back up the call stack.

> +

> +   For instance, at the deepest point of the callstack where the failure

> +   happens, rather than:

> +

> +     if (!check_something ())

> +       {

> +         if (dump_enabled_p ())

> +           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +                            "foo is unsupported.\n");

> +         return false;

> +       }

> +     // [...more checks...]

> +

> +     // All checks passed:

> +     return true;

> +

> +   we can capture the cause of the failure via:

> +

> +     if (!check_something ())

> +       return opt_result::failure_at (stmt, "foo is unsupported");

> +     // [...more checks...]

> +

> +     // All checks passed:

> +     return opt_result::success ();

> +

> +   which effectively returns true or false, whilst recording any problem.

> +

> +   opt_result::success and opt_result::failure return opt_result values

> +   which "looks like" true/false respectively, via operator bool().

> +   If dump_enabled_p, then opt_result::failure also creates an opt_problem *,

> +   capturing the pertinent data (here, "foo is unsupported " and "stmt").

> +   If dumps are disabled, then opt_problem instances aren't

> +   created, and it's equivalent to just returning a bool.

> +

> +   The opt_problem can be propagated via opt_result values back up

> +   the call stack to where it makes most sense to the user.

> +   For instance, rather than:

> +

> +     bool ok = try_something_that_might_fail ();

> +     if (!ok)

> +       {

> +         if (dump_enabled_p ())

> +           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +                            "some message.\n");

> +         return false;

> +       }

> +

> +   we can replace the bool with an opt_result, so if dump_enabled_p, we

> +   assume that if try_something_that_might_fail, an opt_problem * will be

> +   created, and we can propagate it up the call chain:

> +

> +     opt_result ok = try_something_that_might_fail ();

> +     if (!ok)

> +       {

> +         if (dump_enabled_p ())

> +           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +                            "some message.\n");

> +         return ok; // propagating the opt_result

> +       }

> +

> +   opt_result is an opt_wrapper<bool>, where opt_wrapper<T> is a base

> +   class for wrapping a T, optionally propagating an opt_problem in

> +   case of failure_at (when dumps are enabled).  Similarly,

> +   opt_pointer_wrapper<T> can be used to wrap pointer types (where non-NULL

> +   signifies success, NULL signifies failure).

> +

> +   In all cases, opt_wrapper<T> acts as if the opt_problem were one of its

> +   fields, but the opt_problem is actually stored in a global, so that when

> +   compiled, an opt_wrapper<T> is effectively just a T, so that we're

> +   still just passing e.g. a bool around; the opt_wrapper<T> classes

> +   simply provide type-checking and an API to ensure that we provide

> +   error-messages deep in the callstack at the places where problems

> +   occur, and that we propagate them.  This also avoids having

> +   to manage the ownership of the opt_problem instances.

> +

> +   Using opt_result and opt_wrapper<T> documents the intent of the code

> +   for the places where we represent success values, and allows the C++ type

> +   system to track where the deepest points in the callstack are where we

> +   need to emit the failure messages from.  */

> +

> +/* A bundle of information about why an optimization failed (e.g.

> +   vectorization), and the location in both the user's code and

> +   in GCC itself where the problem occurred.

> +

> +   Instances are created by static member functions in opt_wrapper

> +   subclasses, such as opt_result::failure.

> +

> +   Instances are only created when dump_enabled_p ().  */

> +

> +class opt_problem

> +{

> + public:

> +  static opt_problem *get_singleton () { return s_the_problem; }

> +

> +  opt_problem (const dump_location_t &loc,

> +	       const char *fmt, va_list *ap)

> +    ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);

> +

> +  const dump_location_t &

> +  get_dump_location () const { return m_optinfo.get_dump_location (); }

> +

> +  const optinfo & get_optinfo () const { return m_optinfo; }

> +

> +  void emit_and_clear ();

> +

> + private:

> +  optinfo m_optinfo;

> +

> +  static opt_problem *s_the_problem;

> +};

> +

> +/* A base class for wrapper classes that track a success/failure value, while

> +   optionally supporting propagating an opt_problem * describing any

> +   failure back up the call stack.  */

> +

> +template <typename T>

> +class opt_wrapper

> +{

> + public:

> +  typedef T wrapped_t;

> +

> +  /* Be accessible as the wrapped type.  */

> +  operator wrapped_t () const { return m_result; }

> +

> +  /* No public ctor.  */

> +

> +  wrapped_t get_result () const { return m_result; }

> +  opt_problem *get_problem () const { return opt_problem::get_singleton (); }

> +

> + protected:

> +  opt_wrapper (wrapped_t result, opt_problem */*problem*/)

> +  : m_result (result)

> +  {

> +    /* "problem" is ignored: although it looks like a field, we

> +       actually just use the opt_problem singleton, so that

> +       opt_wrapper<T> in memory is just a T.  */

> +  }

> +

> + private:

> +  wrapped_t m_result;

> +};

> +

> +/* Subclass of opt_wrapper<T> for bool, where

> +   - true signifies "success", and

> +   - false signifies "failure"

> +   whilst effectively propagating an opt_problem * describing any failure

> +   back up the call stack.  */

> +

> +class opt_result : public opt_wrapper <bool>

> +{

> + public:

> +  /* Generate a "success" value: a wrapper around "true".  */

> +

> +  static opt_result success () { return opt_result (true, NULL); }

> +

> +  /* Generate a "failure" value: a wrapper around "false", and,

> +     if dump_enabled_p, an opt_problem.  */

> +

> +  static opt_result failure_at (const dump_location_t &loc,

> +				const char *fmt, ...)

> +	  ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)

> +  {

> +    opt_problem *problem = NULL;

> +    if (dump_enabled_p ())

> +      {

> +	va_list ap;

> +	va_start (ap, fmt);

> +	problem = new opt_problem (loc, fmt, &ap);

> +	va_end (ap);

> +      }

> +    return opt_result (false, problem);

> +  }

> +

> +  /* Given a failure wrapper of some other kind, make an opt_result failure

> +     object, for propagating the opt_problem up the call stack.  */

> +

> +  template <typename S>

> +  static opt_result

> +  propagate_failure (opt_wrapper <S> other)

> +  {

> +    return opt_result (false, other.get_problem ());

> +  }

> +

> + private:

> +  /* Private ctor.  Instances should be created by the success and failure

> +     static member functions.  */

> +  opt_result (wrapped_t result, opt_problem *problem)

> +  : opt_wrapper (result, problem)

> +  {}

> +};

> +

> +/* Subclass of opt_wrapper<T> where T is a pointer type, for tracking

> +   success/failure, where:

> +   - a non-NULL value signifies "success", and

> +   - a NULL value signifies "failure",

> +   whilst effectively propagating an opt_problem * describing any failure

> +   back up the call stack.  */

> +

> +template <typename PtrType_t>

> +class opt_pointer_wrapper : public opt_wrapper <PtrType_t>

> +{

> + public:

> +  typedef PtrType_t wrapped_pointer_t;

> +

> +  /* Given a non-NULL pointer, make a success object wrapping it.  */

> +

> +  static opt_pointer_wrapper <wrapped_pointer_t>

> +  success (wrapped_pointer_t ptr)

> +  {

> +    return opt_pointer_wrapper <wrapped_pointer_t> (ptr, NULL);

> +  }

> +

> +  /* Make a NULL pointer failure object, with the given message

> +     (if dump_enabled_p).  */

> +

> +  static opt_pointer_wrapper <wrapped_pointer_t>

> +  failure_at (const dump_location_t &loc,

> +	      const char *fmt, ...)

> +    ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)

> +  {

> +    opt_problem *problem = NULL;

> +    if (dump_enabled_p ())

> +      {

> +	va_list ap;

> +	va_start (ap, fmt);

> +	problem = new opt_problem (loc, fmt, &ap);

> +	va_end (ap);

> +      }

> +    return opt_pointer_wrapper <wrapped_pointer_t> (NULL, problem);

> +  }

> +

> +  /* Given a failure wrapper of some other kind, make a NULL pointer

> +     failure object, propagating the problem.  */

> +

> +  template <typename S>

> +  static opt_pointer_wrapper <wrapped_pointer_t>

> +  propagate_failure (opt_wrapper <S> other)

> +  {

> +    return opt_pointer_wrapper <wrapped_pointer_t> (NULL,

> +						    other.get_problem ());

> +  }

> +

> +  /* Support accessing the underlying pointer via ->.  */

> +

> +  wrapped_pointer_t operator-> () const { return this->get_result (); }

> +

> + private:

> +  /* Private ctor.  Instances should be built using the static member

> +     functions "success" and "failure".  */

> +  opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)

> +  : opt_wrapper<PtrType_t> (result, problem)

> +  {}

> +};

> +

> +/* A typedef for wrapping "tree" so that NULL_TREE can carry an

> +   opt_problem describing the failure (if dump_enabled_p).  */

> +

> +typedef opt_pointer_wrapper<tree> opt_tree;

> +

> +#endif /* #ifndef GCC_OPT_PROBLEM_H */

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

> index efdbdb3..31029ad 100644

> --- a/gcc/optinfo-emit-json.cc

> +++ b/gcc/optinfo-emit-json.cc

> @@ -531,7 +531,7 @@ namespace selftest {

>  static void

>  test_building_json_from_dump_calls ()

>  {

> -  temp_dump_context tmp (true, MSG_NOTE);

> +  temp_dump_context tmp (true, true, MSG_NOTE);

>    dump_location_t loc;

>    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");

>    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);

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

> index b858c3c..de781a5 100644

> --- a/gcc/optinfo.cc

> +++ b/gcc/optinfo.cc

> @@ -89,11 +89,51 @@ optinfo::add_item (optinfo_item *item)

>    m_items.safe_push (item);

>  }

>  

> +/* Get MSG_* flags corresponding to KIND.  */

> +

> +static dump_flags_t

> +optinfo_kind_to_dump_flag (enum optinfo_kind kind)

> +{

> +  switch (kind)

> +    {

> +    default:

> +      gcc_unreachable ();

> +    case OPTINFO_KIND_SUCCESS:

> +      return MSG_OPTIMIZED_LOCATIONS;

> +    case OPTINFO_KIND_FAILURE:

> +      return MSG_MISSED_OPTIMIZATION;

> +    case OPTINFO_KIND_NOTE:

> +    case OPTINFO_KIND_SCOPE:

> +      return MSG_NOTE;

> +    }

> +}

> +

> +/* Re-emit this optinfo, both to the "non-immediate" destinations,

> +   *and* to the "immediate" destinations.  */

> +

> +void

> +optinfo::emit_for_opt_problem () const

> +{

> +  dump_flags_t dump_kind = optinfo_kind_to_dump_flag (get_kind ());

> +  dump_kind |= MSG_PRIORITY_REEMITTED;

> +

> +  /* Re-emit to "immediate" destinations, without creating a new optinfo.  */

> +  dump_context::get ().dump_loc_immediate (dump_kind, get_dump_location ());

> +  unsigned i;

> +  optinfo_item *item;

> +  FOR_EACH_VEC_ELT (m_items, i, item)

> +    dump_context::get ().emit_item (item, dump_kind);

> +

> +  /* Re-emit to "non-immediate" destinations.  */

> +  emit ();

> +}

> +

>  /* Emit the optinfo to all of the "non-immediate" destinations

> -   (emission to "immediate" destinations is done by emit_item).  */

> +   (emission to "immediate" destinations is done by

> +   dump_context::emit_item).  */

>  

>  void

> -optinfo::emit ()

> +optinfo::emit () const

>  {

>    /* -fsave-optimization-record.  */

>    optimization_records_maybe_record_optinfo (this);

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

> index 8ac961c..99bd22c 100644

> --- a/gcc/optinfo.h

> +++ b/gcc/optinfo.h

> @@ -108,6 +108,9 @@ class optinfo

>    {}

>    ~optinfo ();

>  

> +  const dump_location_t &

> +  get_dump_location () const { return m_loc; }

> +

>    const dump_user_location_t &

>    get_user_location () const { return m_loc.get_user_location (); }

>  

> @@ -124,8 +127,10 @@ class optinfo

>  

>    void add_item (optinfo_item *item);

>  

> +  void emit_for_opt_problem () const;

> +

>   private:

> -  void emit ();

> +  void emit () const;

>  

>    /* Pre-canned ways of manipulating the optinfo, for use by friend class

>       dump_context.  */

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

> index 5adb033..562ada7 100644

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

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

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

>    opt_proposer_c_tests ();

>    json_cc_tests ();

>    optinfo_emit_json_cc_tests ();

> +  opt_problem_cc_tests ();

>  

>    /* Mid-level data structures.  */

>    input_c_tests ();

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

> index ede7732..8da7c4a 100644

> --- a/gcc/selftest.h

> +++ b/gcc/selftest.h

> @@ -229,6 +229,7 @@ extern void hash_map_tests_c_tests ();

>  extern void hash_set_tests_c_tests ();

>  extern void input_c_tests ();

>  extern void json_cc_tests ();

> +extern void opt_problem_cc_tests ();

>  extern void optinfo_emit_json_cc_tests ();

>  extern void predict_c_tests ();

>  extern void pretty_print_c_tests ();

> diff --git a/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c b/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c

> new file mode 100644

> index 0000000..94c55a9

> --- /dev/null

> +++ b/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c

> @@ -0,0 +1,12 @@

> +/* { dg-do compile { target vect_int } } */

> +/* { dg-additional-options "-fopt-info-vec-all -O3" } */

> +

> +extern void accumulate (int x, int *a);

> +

> +int test_missing_function_defn (int *arr, int n) /* { dg-message "vectorized 0 loops in function" } */

> +{

> +  int sum = 0;

> +  for (int i = 0; i < n; ++i) /* { dg-missed "couldn't vectorize loop" } */

> +    accumulate (arr[i], &sum); /* { dg-missed "statement clobbers memory: accumulate \\(.*\\);" } */

> +  return sum;

> +}

> diff --git a/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c b/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c

> index 1e5fc27..750193e 100644

> --- a/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c

> +++ b/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c

> @@ -1,6 +1,6 @@

>  /* { dg-do compile } */

>  /* { dg-require-effective-target vect_int } */

> -/* { dg-additional-options "--param vect-max-version-for-alias-checks=0" } */

> +/* { dg-additional-options "--param vect-max-version-for-alias-checks=0 -fopt-info-vec-all" } */

>  

>  #define N 16

>  

> @@ -12,24 +12,26 @@ union u { struct s2 f; struct s3 g; };

>  /* We allow a and b to overlap arbitrarily.  */

>  

>  void

> -f1 (int a[][N], int b[][N])

> +f1 (int a[][N], int b[][N]) /* { dg-message "vectorized 0 loops in function" } */

>  {

> -  for (int i = 0; i < N; ++i)

> +  for (int i = 0; i < N; ++i) /* { dg-missed "couldn't vectorize loop" } */

>      a[0][i] += b[0][i];

> +  /* { dg-message "will not create alias checks, as --param vect-max-version-for-alias-checks == 0" "" { target *-*-* } .-2 } */

>  }

>  

>  void

> -f2 (union u *a, union u *b)

> +f2 (union u *a, union u *b) /* { dg-message "vectorized 0 loops in function" } */

>  {

> -  for (int i = 0; i < N; ++i)

> +  for (int i = 0; i < N; ++i) /* { dg-missed "couldn't vectorize loop" } */

>      a->f.b.a[i] += b->g.e.a[i];

> +  /* { dg-message "will not create alias checks, as --param vect-max-version-for-alias-checks == 0" "" { target *-*-* } .-2 } */

>  }

>  

>  void

> -f3 (struct s1 *a, struct s1 *b)

> +f3 (struct s1 *a, struct s1 *b) /* { dg-message "vectorized 0 loops in function" } */

>  {

> -  for (int i = 0; i < N - 1; ++i)

> -    a->a[i + 1] += b->a[i];

> +  for (int i = 0; i < N - 1; ++i) /* { dg-missed "couldn't vectorize loop" } */

> +    a->a[i + 1] += b->a[i]; /* { dg-missed "possible dependence between data-refs" } */

>  }

>  

>  /* { dg-final { scan-tree-dump-not "LOOP VECTORIZED" "vect" } } */

> diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c

> index bf30a61..69c5f7b 100644

> --- a/gcc/tree-data-ref.c

> +++ b/gcc/tree-data-ref.c

> @@ -807,7 +807,8 @@ canonicalize_base_object_address (tree addr)

>    return build_fold_addr_expr (TREE_OPERAND (addr, 0));

>  }

>  

> -/* Analyze the behavior of memory reference REF.  There are two modes:

> +/* Analyze the behavior of memory reference REF within STMT.

> +   There are two modes:

>  

>     - BB analysis.  In this case we simply split the address into base,

>       init and offset components, without reference to any containing loop.

> @@ -827,9 +828,9 @@ canonicalize_base_object_address (tree addr)

>     Return true if the analysis succeeded and store the results in DRB if so.

>     BB analysis can only fail for bitfield or reversed-storage accesses.  */

>  

> -bool

> +opt_result

>  dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,

> -		      struct loop *loop)

> +		      struct loop *loop, const gimple *stmt)

>  {

>    poly_int64 pbitsize, pbitpos;

>    tree base, poffset;

> @@ -848,18 +849,12 @@ dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,

>  

>    poly_int64 pbytepos;

>    if (!multiple_p (pbitpos, BITS_PER_UNIT, &pbytepos))

> -    {

> -      if (dump_file && (dump_flags & TDF_DETAILS))

> -	fprintf (dump_file, "failed: bit offset alignment.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "failed: bit offset alignment.\n");

>  

>    if (preversep)

> -    {

> -      if (dump_file && (dump_flags & TDF_DETAILS))

> -	fprintf (dump_file, "failed: reverse storage order.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "failed: reverse storage order.\n");

>  

>    /* Calculate the alignment and misalignment for the inner reference.  */

>    unsigned int HOST_WIDE_INT bit_base_misalignment;

> @@ -895,11 +890,8 @@ dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,

>    if (in_loop)

>      {

>        if (!simple_iv (loop, loop, base, &base_iv, true))

> -        {

> -	  if (dump_file && (dump_flags & TDF_DETAILS))

> -	    fprintf (dump_file, "failed: evolution of base is not affine.\n");

> -	  return false;

> -        }

> +	return opt_result::failure_at

> +	  (stmt, "failed: evolution of base is not affine.\n");

>      }

>    else

>      {

> @@ -921,11 +913,8 @@ dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,

>            offset_iv.step = ssize_int (0);

>          }

>        else if (!simple_iv (loop, loop, poffset, &offset_iv, true))

> -        {

> -	  if (dump_file && (dump_flags & TDF_DETAILS))

> -	    fprintf (dump_file, "failed: evolution of offset is not affine.\n");

> -	  return false;

> -        }

> +	return opt_result::failure_at

> +	  (stmt, "failed: evolution of offset is not affine.\n");

>      }

>  

>    init = ssize_int (pbytepos);

> @@ -981,7 +970,7 @@ dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,

>    if (dump_file && (dump_flags & TDF_DETAILS))

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

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Return true if OP is a valid component reference for a DR access

> @@ -1205,7 +1194,7 @@ create_data_ref (edge nest, loop_p loop, tree memref, gimple *stmt,

>    DR_IS_CONDITIONAL_IN_STMT (dr) = is_conditional_in_stmt;

>  

>    dr_analyze_innermost (&DR_INNERMOST (dr), memref,

> -			nest != NULL ? loop : NULL);

> +			nest != NULL ? loop : NULL, stmt);

>    dr_analyze_indices (dr, nest, loop);

>    dr_analyze_alias (dr);

>  

> @@ -1318,7 +1307,7 @@ data_ref_compare_tree (tree t1, tree t2)

>  /* Return TRUE it's possible to resolve data dependence DDR by runtime alias

>     check.  */

>  

> -bool

> +opt_result

>  runtime_alias_check_p (ddr_p ddr, struct loop *loop, bool speed_p)

>  {

>    if (dump_enabled_p ())

> @@ -1327,25 +1316,18 @@ runtime_alias_check_p (ddr_p ddr, struct loop *loop, bool speed_p)

>  		 DR_REF (DDR_A (ddr)), DR_REF (DDR_B (ddr)));

>  

>    if (!speed_p)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf (MSG_MISSED_OPTIMIZATION,

> -		     "runtime alias check not supported when optimizing "

> -		     "for size.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (DR_STMT (DDR_A (ddr)),

> +				   "runtime alias check not supported when"

> +				   " optimizing for size.\n");

>  

>    /* FORNOW: We don't support versioning with outer-loop in either

>       vectorization or loop distribution.  */

>    if (loop != NULL && loop->inner != NULL)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf (MSG_MISSED_OPTIMIZATION,

> -		     "runtime alias check not supported for outer loop.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (DR_STMT (DDR_A (ddr)),

> +				   "runtime alias check not supported for"

> +				   " outer loop.\n");

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Operator == between two dr_with_seg_len objects.

> @@ -5043,18 +5025,18 @@ loop_nest_has_data_refs (loop_p loop)

>     reference, returns false, otherwise returns true.  NEST is the outermost

>     loop of the loop nest in which the references should be analyzed.  */

>  

> -bool

> +opt_result

>  find_data_references_in_stmt (struct loop *nest, gimple *stmt,

>  			      vec<data_reference_p> *datarefs)

>  {

>    unsigned i;

>    auto_vec<data_ref_loc, 2> references;

>    data_ref_loc *ref;

> -  bool ret = true;

>    data_reference_p dr;

>  

>    if (get_references_in_stmt (stmt, &references))

> -    return false;

> +    return opt_result::failure_at (stmt, "statement clobbers memory: %G",

> +				   stmt);

>  

>    FOR_EACH_VEC_ELT (references, i, ref)

>      {

> @@ -5065,7 +5047,7 @@ find_data_references_in_stmt (struct loop *nest, gimple *stmt,

>        datarefs->safe_push (dr);

>      }

>  

> -  return ret;

> +  return opt_result::success ();

>  }

>  

>  /* Stores the data references in STMT to DATAREFS.  If there is an

> diff --git a/gcc/tree-data-ref.h b/gcc/tree-data-ref.h

> index 8739853..525d27f 100644

> --- a/gcc/tree-data-ref.h

> +++ b/gcc/tree-data-ref.h

> @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see

>  

>  #include "graphds.h"

>  #include "tree-chrec.h"

> +#include "opt-problem.h"

>  

>  /*

>    innermost_loop_behavior describes the evolution of the address of the memory

> @@ -421,7 +422,8 @@ typedef struct data_dependence_relation *ddr_p;

>  #define DDR_COULD_BE_INDEPENDENT_P(DDR) (DDR)->could_be_independent_p

>  

>  

> -bool dr_analyze_innermost (innermost_loop_behavior *, tree, struct loop *);

> +opt_result dr_analyze_innermost (innermost_loop_behavior *, tree,

> +				 struct loop *, const gimple *);

>  extern bool compute_data_dependences_for_loop (struct loop *, bool,

>  					       vec<loop_p> *,

>  					       vec<data_reference_p> *,

> @@ -443,8 +445,8 @@ extern void free_dependence_relation (struct data_dependence_relation *);

>  extern void free_dependence_relations (vec<ddr_p> );

>  extern void free_data_ref (data_reference_p);

>  extern void free_data_refs (vec<data_reference_p> );

> -extern bool find_data_references_in_stmt (struct loop *, gimple *,

> -					  vec<data_reference_p> *);

> +extern opt_result find_data_references_in_stmt (struct loop *, gimple *,

> +						vec<data_reference_p> *);

>  extern bool graphite_find_data_references_in_stmt (edge, loop_p, gimple *,

>  						   vec<data_reference_p> *);

>  tree find_data_references_in_loop (struct loop *, vec<data_reference_p> *);

> @@ -479,7 +481,7 @@ extern bool dr_may_alias_p (const struct data_reference *,

>  extern bool dr_equal_offsets_p (struct data_reference *,

>                                  struct data_reference *);

>  

> -extern bool runtime_alias_check_p (ddr_p, struct loop *, bool);

> +extern opt_result runtime_alias_check_p (ddr_p, struct loop *, bool);

>  extern int data_ref_compare_tree (tree, tree);

>  extern void prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *,

>  					   poly_uint64);

> diff --git a/gcc/tree-predcom.c b/gcc/tree-predcom.c

> index 2bde732..1711027 100644

> --- a/gcc/tree-predcom.c

> +++ b/gcc/tree-predcom.c

> @@ -1280,7 +1280,8 @@ find_looparound_phi (struct loop *loop, dref ref, dref root)

>    memset (&init_dr, 0, sizeof (struct data_reference));

>    DR_REF (&init_dr) = init_ref;

>    DR_STMT (&init_dr) = phi;

> -  if (!dr_analyze_innermost (&DR_INNERMOST (&init_dr), init_ref, loop))

> +  if (!dr_analyze_innermost (&DR_INNERMOST (&init_dr), init_ref, loop,

> +			     init_stmt))

>      return NULL;

>  

>    if (!valid_initializer_p (&init_dr, ref->distance + 1, root->ref))

> diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c

> index 56b7968..c4805e7 100644

> --- a/gcc/tree-vect-data-refs.c

> +++ b/gcc/tree-vect-data-refs.c

> @@ -156,20 +156,25 @@ vect_get_smallest_scalar_type (stmt_vec_info stmt_info,

>     tested at run-time.  Return TRUE if DDR was successfully inserted.

>     Return false if versioning is not supported.  */

>  

> -static bool

> +static opt_result

>  vect_mark_for_runtime_alias_test (ddr_p ddr, loop_vec_info loop_vinfo)

>  {

>    struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);

>  

>    if ((unsigned) PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS) == 0)

> -    return false;

> +    return opt_result::failure_at (vect_location,

> +				   "will not create alias checks, as"

> +				   " --param vect-max-version-for-alias-checks"

> +				   " == 0\n");

>  

> -  if (!runtime_alias_check_p (ddr, loop,

> -			      optimize_loop_nest_for_speed_p (loop)))

> -    return false;

> +  opt_result res

> +    = runtime_alias_check_p (ddr, loop,

> +			     optimize_loop_nest_for_speed_p (loop));

> +  if (!res)

> +    return res;

>  

>    LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo).safe_push (ddr);

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Record that loop LOOP_VINFO needs to check that VALUE is nonzero.  */

> @@ -277,12 +282,14 @@ vect_analyze_possibly_independent_ddr (data_dependence_relation *ddr,

>  

>  /* Function vect_analyze_data_ref_dependence.

>  

> -   Return TRUE if there (might) exist a dependence between a memory-reference

> +   FIXME: I needed to change the sense of the returned flag.

> +

> +   Return FALSE if there (might) exist a dependence between a memory-reference

>     DRA and a memory-reference DRB.  When versioning for alias may check a

> -   dependence at run-time, return FALSE.  Adjust *MAX_VF according to

> +   dependence at run-time, return TRUE.  Adjust *MAX_VF according to

>     the data dependence.  */

>  

> -static bool

> +static opt_result

>  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  				  loop_vec_info loop_vinfo,

>  				  unsigned int *max_vf)

> @@ -305,11 +312,11 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  

>    /* Independent data accesses.  */

>    if (DDR_ARE_DEPENDENT (ddr) == chrec_known)

> -    return false;

> +    return opt_result::success ();

>  

>    if (dra == drb

>        || (DR_IS_READ (dra) && DR_IS_READ (drb)))

> -    return false;

> +    return opt_result::success ();

>  

>    /* We do not have to consider dependences between accesses that belong

>       to the same group, unless the stride could be smaller than the

> @@ -318,7 +325,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>        && (DR_GROUP_FIRST_ELEMENT (stmtinfo_a)

>  	  == DR_GROUP_FIRST_ELEMENT (stmtinfo_b))

>        && !STMT_VINFO_STRIDED_P (stmtinfo_a))

> -    return false;

> +    return opt_result::success ();

>  

>    /* Even if we have an anti-dependence then, as the vectorized loop covers at

>       least two scalar iterations, there is always also a true dependence.

> @@ -330,7 +337,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>         || (DR_IS_WRITE (dra) && DR_IS_READ (drb)))

>        && !alias_sets_conflict_p (get_alias_set (DR_REF (dra)),

>  				 get_alias_set (DR_REF (drb))))

> -    return false;

> +    return opt_result::success ();

>  

>    /* Unknown data dependence.  */

>    if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)

> @@ -342,28 +349,25 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  	  if ((unsigned int) loop->safelen < *max_vf)

>  	    *max_vf = loop->safelen;

>  	  LOOP_VINFO_NO_DATA_DEPENDENCIES (loop_vinfo) = false;

> -	  return false;

> +	  return opt_result::success ();

>  	}

>  

>        if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)

>  	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "versioning for alias not supported for: "

> -			     "can't determine dependence between %T and %T\n",

> -			     DR_REF (dra), DR_REF (drb));

> -	  return true;

> -	}

> +	return opt_result::failure_at

> +	  (stmtinfo_a->stmt,

> +	   "versioning for alias not supported for: "

> +	   "can't determine dependence between %T and %T\n",

> +	   DR_REF (dra), DR_REF (drb));

>  

>        if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmtinfo_a->stmt,

>  			 "versioning for alias required: "

>  			 "can't determine dependence between %T and %T\n",

>  			 DR_REF (dra), DR_REF (drb));

>  

>        /* Add to list of ddrs that need to be tested at run-time.  */

> -      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);

> +      return vect_mark_for_runtime_alias_test (ddr, loop_vinfo);

>      }

>  

>    /* Known data dependence.  */

> @@ -376,27 +380,24 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  	  if ((unsigned int) loop->safelen < *max_vf)

>  	    *max_vf = loop->safelen;

>  	  LOOP_VINFO_NO_DATA_DEPENDENCIES (loop_vinfo) = false;

> -	  return false;

> +	  return opt_result::success ();

>  	}

>  

>        if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)

>  	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "versioning for alias not supported for: "

> -			     "bad dist vector for %T and %T\n",

> -			     DR_REF (dra), DR_REF (drb));

> -	  return true;

> -	}

> +	return opt_result::failure_at

> +	  (stmtinfo_a->stmt,

> +	   "versioning for alias not supported for: "

> +	   "bad dist vector for %T and %T\n",

> +	   DR_REF (dra), DR_REF (drb));

>  

>        if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmtinfo_a->stmt,

>  			 "versioning for alias required: "

>  			 "bad dist vector for %T and %T\n",

>  			 DR_REF (dra), DR_REF (drb));

>        /* Add to list of ddrs that need to be tested at run-time.  */

> -      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);

> +      return vect_mark_for_runtime_alias_test (ddr, loop_vinfo);

>      }

>  

>    loop_depth = index_in_loop_nest (loop->num, DDR_LOOP_NEST (ddr));

> @@ -404,7 +405,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>    if (DDR_COULD_BE_INDEPENDENT_P (ddr)

>        && vect_analyze_possibly_independent_ddr (ddr, loop_vinfo,

>  						loop_depth, max_vf))

> -    return false;

> +    return opt_result::success ();

>  

>    FOR_EACH_VEC_ELT (DDR_DIST_VECTS (ddr), i, dist_v)

>      {

> @@ -440,23 +441,16 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  		a[i+1] = ...;

>  	     where loads from the group interleave with the store.  */

>  	  if (!vect_preserves_scalar_order_p (dr_info_a, dr_info_b))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "READ_WRITE dependence in interleaving.\n");

> -	      return true;

> -	    }

> +	    return opt_result::failure_at (stmtinfo_a->stmt,

> +					   "READ_WRITE dependence"

> +					   " in interleaving.\n");

>  

>  	  if (loop->safelen < 2)

>  	    {

>  	      tree indicator = dr_zero_step_indicator (dra);

>  	      if (!indicator || integer_zerop (indicator))

> -		{

> -		  if (dump_enabled_p ())

> -		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "access also has a zero step\n");

> -		  return true;

> -		}

> +		return opt_result::failure_at (stmtinfo_a->stmt,

> +					       "access also has a zero step\n");

>  	      else if (TREE_CODE (indicator) != INTEGER_CST)

>  		vect_check_nonzero_value (loop_vinfo, indicator);

>  	    }

> @@ -503,16 +497,13 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>  	  continue;

>  	}

>  

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized, possible dependence "

> -			 "between data-refs %T and %T\n",

> -			 DR_REF (dra), DR_REF (drb));

> -

> -      return true;

> +      return opt_result::failure_at (stmtinfo_a->stmt,

> +				     "not vectorized, possible dependence "

> +				     "between data-refs %T and %T\n",

> +				     DR_REF (dra), DR_REF (drb));

>      }

>  

> -  return false;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_analyze_data_ref_dependences.

> @@ -521,7 +512,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,

>     exist any data dependences between them.  Set *MAX_VF according to

>     the maximum vectorization factor the data dependences allow.  */

>  

> -bool

> +opt_result

>  vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,

>  				   unsigned int *max_vf)

>  {

> @@ -553,10 +544,14 @@ vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,

>      *max_vf = LOOP_VINFO_ORIG_MAX_VECT_FACTOR (loop_vinfo);

>    else

>      FOR_EACH_VEC_ELT (LOOP_VINFO_DDRS (loop_vinfo), i, ddr)

> -      if (vect_analyze_data_ref_dependence (ddr, loop_vinfo, max_vf))

> -	return false;

> +      {

> +	opt_result res

> +	  = vect_analyze_data_ref_dependence (ddr, loop_vinfo, max_vf);

> +	if (!res)

> +	  return res;

> +      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> @@ -1055,33 +1050,24 @@ vect_update_misalignment_for_peel (dr_vec_info *dr_info,

>  

>     Return TRUE if DR_INFO can be handled with respect to alignment.  */

>  

> -static bool

> +static opt_result

>  verify_data_ref_alignment (dr_vec_info *dr_info)

>  {

>    enum dr_alignment_support supportable_dr_alignment

>      = vect_supportable_dr_alignment (dr_info, false);

>    if (!supportable_dr_alignment)

> -    {

> -      if (dump_enabled_p ())

> -	{

> -	  if (DR_IS_READ (dr_info->dr))

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: unsupported unaligned load.");

> -	  else

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: unsupported unaligned "

> -			     "store.");

> -

> -	  dump_printf (MSG_MISSED_OPTIMIZATION, "%T\n", DR_REF (dr_info->dr));

> -	}

> -      return false;

> -    }

> +    return opt_result::failure_at

> +      (dr_info->stmt->stmt,

> +       DR_IS_READ (dr_info->dr)

> +	? "not vectorized: unsupported unaligned load: %T\n"

> +	: "not vectorized: unsupported unaligned store: %T\n",

> +       DR_REF (dr_info->dr));

>  

>    if (supportable_dr_alignment != dr_aligned && dump_enabled_p ())

>      dump_printf_loc (MSG_NOTE, vect_location,

>  		     "Vectorizing an unaligned access.\n");

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_verify_datarefs_alignment

> @@ -1089,7 +1075,7 @@ verify_data_ref_alignment (dr_vec_info *dr_info)

>     Return TRUE if all data references in the loop can be

>     handled with respect to alignment.  */

>  

> -bool

> +opt_result

>  vect_verify_datarefs_alignment (loop_vec_info vinfo)

>  {

>    vec<data_reference_p> datarefs = vinfo->shared->datarefs;

> @@ -1115,11 +1101,12 @@ vect_verify_datarefs_alignment (loop_vec_info vinfo)

>  	  && !STMT_VINFO_GROUPED_ACCESS (stmt_info))

>  	continue;

>  

> -      if (! verify_data_ref_alignment (dr_info))

> -	return false;

> +      opt_result res = verify_data_ref_alignment (dr_info);

> +      if (!res)

> +	return res;

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Given an memory reference EXP return whether its alignment is less

> @@ -1593,7 +1580,7 @@ vect_peeling_supportable (loop_vec_info loop_vinfo, dr_vec_info *dr0_info,

>       (whether to generate regular loads/stores, or with special handling for

>       misalignment).  */

>  

> -bool

> +opt_result

>  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>  {

>    vec<data_reference_p> datarefs = LOOP_VINFO_DATAREFS (loop_vinfo);

> @@ -1605,7 +1592,6 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>    unsigned int i, j;

>    bool do_peeling = false;

>    bool do_versioning = false;

> -  bool stat;

>    unsigned int npeel = 0;

>    bool one_misalignment_known = false;

>    bool one_misalignment_unknown = false;

> @@ -1992,7 +1978,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>        /* Check if all datarefs are supportable and log.  */

>        if (do_peeling && known_alignment_for_access_p (dr0_info) && npeel == 0)

>          {

> -          stat = vect_verify_datarefs_alignment (loop_vinfo);

> +          opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);

>            if (!stat)

>              do_peeling = false;

>            else

> @@ -2078,7 +2064,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>  	  /* The inside-loop cost will be accounted for in vectorizable_load

>  	     and vectorizable_store correctly with adjusted alignments.

>  	     Drop the body_cst_vec on the floor here.  */

> -	  stat = vect_verify_datarefs_alignment (loop_vinfo);

> +	  opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);

>  	  gcc_assert (stat);

>            return stat;

>          }

> @@ -2201,7 +2187,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>        /* Peeling and versioning can't be done together at this time.  */

>        gcc_assert (! (do_peeling && do_versioning));

>  

> -      stat = vect_verify_datarefs_alignment (loop_vinfo);

> +      opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);

>        gcc_assert (stat);

>        return stat;

>      }

> @@ -2209,7 +2195,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)

>    /* This point is reached if neither peeling nor versioning is being done.  */

>    gcc_assert (! (do_peeling || do_versioning));

>  

> -  stat = vect_verify_datarefs_alignment (loop_vinfo);

> +  opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);

>    return stat;

>  }

>  

> @@ -2275,7 +2261,7 @@ vect_find_same_alignment_drs (vec_info *vinfo, data_dependence_relation *ddr)

>     Analyze the alignment of the data-references in the loop.

>     Return FALSE if a data reference is found that cannot be vectorized.  */

>  

> -bool

> +opt_result

>  vect_analyze_data_refs_alignment (loop_vec_info vinfo)

>  {

>    DUMP_VECT_SCOPE ("vect_analyze_data_refs_alignment");

> @@ -2300,7 +2286,7 @@ vect_analyze_data_refs_alignment (loop_vec_info vinfo)

>  	vect_compute_data_ref_alignment (dr_info);

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> @@ -2825,7 +2811,7 @@ can_group_stmts_p (stmt_vec_info stmt1_info, stmt_vec_info stmt2_info)

>  

>     FORNOW: handle only arrays and pointer accesses.  */

>  

> -bool

> +opt_result

>  vect_analyze_data_ref_accesses (vec_info *vinfo)

>  {

>    unsigned int i;

> @@ -2835,7 +2821,7 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)

>    DUMP_VECT_SCOPE ("vect_analyze_data_ref_accesses");

>  

>    if (datarefs.is_empty ())

> -    return true;

> +    return opt_result::success ();

>  

>    /* Sort the array of datarefs to make building the interleaving chains

>       linear.  Don't modify the original vector's order, it is needed for

> @@ -2994,13 +2980,15 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)

>  	  else

>  	    {

>  	      datarefs_copy.release ();

> -	      return false;

> +	      return opt_result::failure_at (dr_info->stmt->stmt,

> +					     "not vectorized:"

> +					     " complicated access pattern.\n");

>  	    }

>  	}

>      }

>  

>    datarefs_copy.release ();

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_vfa_segment_size.

> @@ -3258,7 +3246,7 @@ vectorizable_with_step_bound_p (dr_vec_info *dr_info_a, dr_vec_info *dr_info_b,

>     Return FALSE if resulting list of ddrs is longer then allowed by

>     PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS, otherwise return TRUE.  */

>  

> -bool

> +opt_result

>  vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)

>  {

>    typedef pair_hash <tree_operand_hash, tree_operand_hash> tree_pair_hash;

> @@ -3292,7 +3280,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)

>      }

>  

>    if (may_alias_ddrs.is_empty ())

> -    return true;

> +    return opt_result::success ();

>  

>    comp_alias_ddrs.create (may_alias_ddrs.length ());

>  

> @@ -3452,12 +3440,11 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)

>  	    continue;

>  

>  	  if (res == 1)

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_NOTE, vect_location,

> -				 "not vectorized: compilation time alias.\n");

> -	      return false;

> -	    }

> +	    return opt_result::failure_at (stmt_info_b->stmt,

> +					   "not vectorized:"

> +					   " compilation time alias: %G%G",

> +					   stmt_info_a->stmt,

> +					   stmt_info_b->stmt);

>  	}

>  

>        dr_with_seg_len_pair_t dr_with_seg_len_pair

> @@ -3482,17 +3469,14 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)

>  		   "improved number of alias checks from %d to %d\n",

>  		   may_alias_ddrs.length (), count);

>    if ((int) count > PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "number of versioning for alias "

> -			 "run-time tests exceeds %d "

> -			 "(--param vect-max-version-for-alias-checks)\n",

> -			 PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));

> -      return false;

> -    }

> -

> -  return true;

> +    return opt_result::failure_at

> +      (vect_location,

> +       "number of versioning for alias "

> +       "run-time tests exceeds %d "

> +       "(--param vect-max-version-for-alias-checks)\n",

> +       PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));

> +

> +  return opt_result::success ();

>  }

>  

>  /* Check whether we can use an internal function for a gather load

> @@ -3846,7 +3830,7 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, loop_vec_info loop_vinfo,

>     append them to DATAREFS.  Return false if datarefs in this stmt cannot

>     be handled.  */

>  

> -bool

> +opt_result

>  vect_find_stmt_data_reference (loop_p loop, gimple *stmt,

>  			       vec<data_reference_p> *datarefs)

>  {

> @@ -3854,72 +3838,50 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,

>       loop vectorization and BB vectorization checks dependences with a

>       stmt walk.  */

>    if (gimple_clobber_p (stmt))

> -    return true;

> +    return opt_result::success ();

>  

>    if (gimple_has_volatile_ops (stmt))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: volatile type %G", stmt);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt, "not vectorized: volatile type: %G",

> +				   stmt);

>  

>    if (stmt_can_throw_internal (stmt))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: statement can throw an exception %G",

> -			 stmt);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized:"

> +				   " statement can throw an exception: %G",

> +				   stmt);

>  

>    auto_vec<data_reference_p, 2> refs;

> -  if (!find_data_references_in_stmt (loop, stmt, &refs))

> -    return false;

> +  opt_result res = find_data_references_in_stmt (loop, stmt, &refs);

> +  if (!res)

> +    return res;

>  

>    if (refs.is_empty ())

> -    return true;

> +    return opt_result::success ();

>  

>    if (refs.length () > 1)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: more than one data ref "

> -			 "in stmt: %G", stmt);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized:"

> +				   " more than one data ref in stmt: %G", stmt);

>  

>    if (gcall *call = dyn_cast <gcall *> (stmt))

>      if (!gimple_call_internal_p (call)

>  	|| (gimple_call_internal_fn (call) != IFN_MASK_LOAD

>  	    && gimple_call_internal_fn (call) != IFN_MASK_STORE))

> -      {

> -	if (dump_enabled_p ())

> -	  dump_printf_loc (MSG_MISSED_OPTIMIZATION,  vect_location,

> -			   "not vectorized: dr in a call %G", stmt);

> -	return false;

> -      }

> +      return opt_result::failure_at (stmt,

> +				     "not vectorized: dr in a call %G", stmt);

>  

>    data_reference_p dr = refs.pop ();

>    if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF

>        && DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1)))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: statement is bitfield "

> -			 "access %G", stmt);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized:"

> +				   " statement is bitfield access %G", stmt);

>  

>    if (DR_BASE_ADDRESS (dr)

>        && TREE_CODE (DR_BASE_ADDRESS (dr)) == INTEGER_CST)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: base addr of dr is a "

> -			 "constant\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized:"

> +				   " base addr of dr is a constant\n");

>  

>    /* Check whether this may be a SIMD lane access and adjust the

>       DR to make it easier for us to handle it.  */

> @@ -3976,7 +3938,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,

>  			  newdr->aux = (void *)-1;

>  			  free_data_ref (dr);

>  			  datarefs->safe_push (newdr);

> -			  return true;

> +			  return opt_result::success ();

>  			}

>  		    }

>  		}

> @@ -3986,7 +3948,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,

>      }

>  

>    datarefs->safe_push (dr);

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_analyze_data_refs.

> @@ -4004,7 +3966,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,

>  

>  */

>  

> -bool

> +opt_result

>  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  {

>    struct loop *loop = NULL;

> @@ -4074,7 +4036,10 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  		  STMT_VINFO_VECTORIZABLE (stmt_info) = false;

>  		  continue;

>  		}

> -	      return false;

> +	      return opt_result::failure_at (stmt_info->stmt,

> +					     "not vectorized:"

> +					     " data ref analysis failed: %G",

> +					     stmt_info->stmt);

>  	    }

>          }

>  

> @@ -4082,13 +4047,10 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>        if (dr->aux == (void *)-1)

>  	{

>  	  if (nested_in_vect_loop_p (loop, stmt_info))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: data ref analysis "

> -				 "failed %G", stmt_info->stmt);

> -	      return false;

> -	    }

> +	    return opt_result::failure_at (stmt_info->stmt,

> +					   "not vectorized:"

> +					   " data ref analysis failed: %G",

> +					   stmt_info->stmt);

>  	  STMT_VINFO_SIMD_LANE_ACCESS_P (stmt_info) = true;

>  	}

>  

> @@ -4106,7 +4068,10 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  	      STMT_VINFO_VECTORIZABLE (stmt_info) = false;

>  	      continue;

>  	    }

> -	  return false;

> +	  return opt_result::failure_at (stmt_info->stmt,

> +					 "not vectorized: base object not"

> +					 " addressable for stmt: %G",

> +					 stmt_info->stmt);

>  	}

>  

>        if (is_a <loop_vec_info> (vinfo)

> @@ -4114,13 +4079,10 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  	  && TREE_CODE (DR_STEP (dr)) != INTEGER_CST)

>  	{

>  	  if (nested_in_vect_loop_p (loop, stmt_info))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: not suitable for strided "

> -				 "load %G", stmt_info->stmt);

> -	      return false;

> -	    }

> +	    return opt_result::failure_at (stmt_info->stmt,

> +					   "not vectorized:"

> +					   "not suitable for strided load %G",

> +					   stmt_info->stmt);

>  	  STMT_VINFO_STRIDED_P (stmt_info) = true;

>  	}

>  

> @@ -4150,10 +4112,12 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  	    dump_printf_loc (MSG_NOTE, vect_location,

>  			     "analyze in outer loop: %T\n", init_ref);

>  

> -	  if (!dr_analyze_innermost (&STMT_VINFO_DR_WRT_VEC_LOOP (stmt_info),

> -				     init_ref, loop))

> +	  opt_result res

> +	    = dr_analyze_innermost (&STMT_VINFO_DR_WRT_VEC_LOOP (stmt_info),

> +				    init_ref, loop, stmt_info->stmt);

> +	  if (!res)

>  	    /* dr_analyze_innermost already explained the failure.  */

> -	    return false;

> +	    return res;

>  

>            if (dump_enabled_p ())

>  	    dump_printf_loc (MSG_NOTE, vect_location,

> @@ -4199,7 +4163,11 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  	      STMT_VINFO_VECTORIZABLE (stmt_info) = false;

>  	      continue;

>  	    }

> -	  return false;

> +	  return opt_result::failure_at (stmt_info->stmt,

> +					 "not vectorized:"

> +					 " no vectype for stmt: %G"

> +					 " scalar_type: %T\n",

> +					 stmt_info->stmt, scalar_type);

>          }

>        else

>  	{

> @@ -4221,17 +4189,12 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>  					  as_a <loop_vec_info> (vinfo),

>  					  &gs_info)

>  	      || !get_vectype_for_scalar_type (TREE_TYPE (gs_info.offset)))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 (gatherscatter == GATHER) ?

> -				 "not vectorized: not suitable for gather "

> -				 "load %G" :

> -				 "not vectorized: not suitable for scatter "

> -				 "store %G",

> -				 stmt_info->stmt);

> -	      return false;

> -	    }

> +	    return opt_result::failure_at

> +	      (stmt_info->stmt,

> +	       (gatherscatter == GATHER) ?

> +	       "not vectorized: not suitable for gather load %G" :

> +	       "not vectorized: not suitable for scatter store %G",

> +	       stmt_info->stmt);

>  	  STMT_VINFO_GATHER_SCATTER_P (stmt_info) = gatherscatter;

>  	}

>      }

> @@ -4240,7 +4203,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)

>       longer need to.  */

>    gcc_assert (i == datarefs.length ());

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c

> index fdac10b..6ea1e77 100644

> --- a/gcc/tree-vect-loop.c

> +++ b/gcc/tree-vect-loop.c

> @@ -159,7 +159,7 @@ static void vect_estimate_min_profitable_iters (loop_vec_info, int *, int *);

>     statement.  VECTYPE_MAYBE_SET_P is true if STMT_VINFO_VECTYPE

>     may already be set for general statements (not just data refs).  */

>  

> -static bool

> +static opt_result

>  vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,

>  			      bool vectype_maybe_set_p,

>  			      poly_uint64 *vf,

> @@ -173,13 +173,14 @@ vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,

>      {

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_NOTE, vect_location, "skip.\n");

> -      return true;

> +      return opt_result::success ();

>      }

>  

>    tree stmt_vectype, nunits_vectype;

> -  if (!vect_get_vector_types_for_stmt (stmt_info, &stmt_vectype,

> -				       &nunits_vectype))

> -    return false;

> +  opt_result res = vect_get_vector_types_for_stmt (stmt_info, &stmt_vectype,

> +						   &nunits_vectype);

> +  if (!res)

> +    return res;

>  

>    if (stmt_vectype)

>      {

> @@ -199,7 +200,7 @@ vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,

>    if (nunits_vectype)

>      vect_update_max_nunits (vf, nunits_vectype);

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Subroutine of vect_determine_vectorization_factor.  Set the vector

> @@ -209,7 +210,7 @@ vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,

>     add them to MASK_PRODUCERS.  Return true on success or false if

>     something prevented vectorization.  */

>  

> -static bool

> +static opt_result

>  vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,

>  			    vec<stmt_vec_info > *mask_producers)

>  {

> @@ -217,8 +218,10 @@ vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,

>    if (dump_enabled_p ())

>      dump_printf_loc (MSG_NOTE, vect_location, "==> examining statement: %G",

>  		     stmt_info->stmt);

> -  if (!vect_determine_vf_for_stmt_1 (stmt_info, false, vf, mask_producers))

> -    return false;

> +  opt_result res

> +    = vect_determine_vf_for_stmt_1 (stmt_info, false, vf, mask_producers);

> +  if (!res)

> +    return res;

>  

>    if (STMT_VINFO_IN_PATTERN_P (stmt_info)

>        && STMT_VINFO_RELATED_STMT (stmt_info))

> @@ -237,18 +240,22 @@ vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,

>  			     def_stmt_info->stmt);

>  	  if (!vect_determine_vf_for_stmt_1 (def_stmt_info, true,

>  					     vf, mask_producers))

> -	    return false;

> +	  res = vect_determine_vf_for_stmt_1 (def_stmt_info, true,

> +					      vf, mask_producers);

> +	  if (!res)

> +	    return res;

>  	}

>  

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_NOTE, vect_location,

>  			 "==> examining pattern statement: %G",

>  			 stmt_info->stmt);

> -      if (!vect_determine_vf_for_stmt_1 (stmt_info, true, vf, mask_producers))

> -	return false;

> +      res = vect_determine_vf_for_stmt_1 (stmt_info, true, vf, mask_producers);

> +      if (!res)

> +	return res;

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_determine_vectorization_factor

> @@ -276,7 +283,7 @@ vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,

>          }

>  */

>  

> -static bool

> +static opt_result

>  vect_determine_vectorization_factor (loop_vec_info loop_vinfo)

>  {

>    struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);

> @@ -320,14 +327,10 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)

>  

>  	      vectype = get_vectype_for_scalar_type (scalar_type);

>  	      if (!vectype)

> -		{

> -		  if (dump_enabled_p ())

> -		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				     "not vectorized: unsupported "

> -				     "data-type %T\n",

> -				     scalar_type);

> -		  return false;

> -		}

> +		return opt_result::failure_at (phi,

> +					       "not vectorized: unsupported "

> +					       "data-type %T\n",

> +					       scalar_type);

>  	      STMT_VINFO_VECTYPE (stmt_info) = vectype;

>  

>  	      if (dump_enabled_p ())

> @@ -349,9 +352,11 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)

>  	   gsi_next (&si))

>  	{

>  	  stmt_info = loop_vinfo->lookup_stmt (gsi_stmt (si));

> -	  if (!vect_determine_vf_for_stmt (stmt_info, &vectorization_factor,

> -					   &mask_producers))

> -	    return false;

> +	  opt_result res

> +	    = vect_determine_vf_for_stmt (stmt_info, &vectorization_factor,

> +					  &mask_producers);

> +	  if (!res)

> +	    return res;

>          }

>      }

>  

> @@ -364,24 +369,20 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)

>      }

>  

>    if (known_le (vectorization_factor, 1U))

> -    {

> -      if (dump_enabled_p ())

> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -                         "not vectorized: unsupported data-type\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (vect_location,

> +				   "not vectorized: unsupported data-type\n");

>    LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;

>  

>    for (i = 0; i < mask_producers.length (); i++)

>      {

>        stmt_info = mask_producers[i];

> -      tree mask_type = vect_get_mask_type_for_stmt (stmt_info);

> +      opt_tree mask_type = vect_get_mask_type_for_stmt (stmt_info);

>        if (!mask_type)

> -	return false;

> +	return opt_result::propagate_failure (mask_type);

>        STMT_VINFO_VECTYPE (stmt_info) = mask_type;

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> @@ -1145,7 +1146,7 @@ vect_compute_single_scalar_iteration_cost (loop_vec_info loop_vinfo)

>     - the number of iterations can be analyzed, i.e, a countable loop.  The

>       niter could be analyzed under some assumptions.  */

>  

> -bool

> +opt_result

>  vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,

>  			  tree *assumptions, tree *number_of_iterationsm1,

>  			  tree *number_of_iterations, gcond **inner_loop_cond)

> @@ -1171,20 +1172,13 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,

>                          (exit-bb)  */

>  

>        if (loop->num_nodes != 2)

> -        {

> -          if (dump_enabled_p ())

> -            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: control flow in loop.\n");

> -          return false;

> -        }

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized:"

> +				       " control flow in loop.\n");

>  

>        if (empty_block_p (loop->header))

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: empty loop.\n");

> -	  return false;

> -	}

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized: empty loop.\n");

>      }

>    else

>      {

> @@ -1209,75 +1203,60 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,

>  	 as described above.  */

>  

>        if ((loop->inner)->inner || (loop->inner)->next)

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: multiple nested loops.\n");

> -	  return false;

> -	}

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized:"

> +				       " multiple nested loops.\n");

>  

>        if (loop->num_nodes != 5)

> -        {

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: control flow in loop.\n");

> -	  return false;

> -        }

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized:"

> +				       " control flow in loop.\n");

>  

>        entryedge = loop_preheader_edge (innerloop);

>        if (entryedge->src != loop->header

>  	  || !single_exit (innerloop)

>  	  || single_exit (innerloop)->dest != EDGE_PRED (loop->latch, 0)->src)

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: unsupported outerloop form.\n");

> -	  return false;

> -	}

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized:"

> +				       " unsupported outerloop form.\n");

>  

>        /* Analyze the inner-loop.  */

>        tree inner_niterm1, inner_niter, inner_assumptions;

> -      if (! vect_analyze_loop_form_1 (loop->inner, inner_loop_cond,

> -				      &inner_assumptions, &inner_niterm1,

> -				      &inner_niter, NULL)

> -	  /* Don't support analyzing niter under assumptions for inner

> -	     loop.  */

> -	  || !integer_onep (inner_assumptions))

> +      opt_result res

> +	= vect_analyze_loop_form_1 (loop->inner, inner_loop_cond,

> +				    &inner_assumptions, &inner_niterm1,

> +				    &inner_niter, NULL);

> +      if (!res)

>  	{

>  	  if (dump_enabled_p ())

> -            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			     "not vectorized: Bad inner loop.\n");

> -	  return false;

> +	  return res;

>  	}

>  

> +      /* Don't support analyzing niter under assumptions for inner

> +	 loop.  */

> +      if (!integer_onep (inner_assumptions))

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized: Bad inner loop.\n");

> +

>        if (!expr_invariant_in_loop_p (loop, inner_niter))

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: inner-loop count not"

> -                             " invariant.\n");

> -	  return false;

> -	}

> +	return opt_result::failure_at (vect_location,

> +				       "not vectorized: inner-loop count not"

> +				       " invariant.\n");

>  

>        if (dump_enabled_p ())

>          dump_printf_loc (MSG_NOTE, vect_location,

>  			 "Considering outer-loop vectorization.\n");

>      }

>  

> -  if (!single_exit (loop)

> -      || EDGE_COUNT (loop->header->preds) != 2)

> -    {

> -      if (dump_enabled_p ())

> -        {

> -          if (!single_exit (loop))

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: multiple exits.\n");

> -          else if (EDGE_COUNT (loop->header->preds) != 2)

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: too many incoming edges.\n");

> -        }

> -      return false;

> -    }

> +  if (!single_exit (loop))

> +    return opt_result::failure_at (vect_location,

> +				   "not vectorized: multiple exits.\n");

> +  if (EDGE_COUNT (loop->header->preds) != 2)

> +    return opt_result::failure_at (vect_location,

> +				   "not vectorized:"

> +				   " too many incoming edges.\n");

>  

>    /* We assume that the loop exit condition is at the end of the loop. i.e,

>       that the loop is represented as a do-while (with a proper if-guard

> @@ -1285,67 +1264,52 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,

>       executable statements, and the latch is empty.  */

>    if (!empty_block_p (loop->latch)

>        || !gimple_seq_empty_p (phi_nodes (loop->latch)))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: latch block not empty.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (vect_location,

> +				   "not vectorized: latch block not empty.\n");

>  

>    /* Make sure the exit is not abnormal.  */

>    edge e = single_exit (loop);

>    if (e->flags & EDGE_ABNORMAL)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: abnormal loop exit edge.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (vect_location,

> +				   "not vectorized:"

> +				   " abnormal loop exit edge.\n");

>  

>    *loop_cond = vect_get_loop_niters (loop, assumptions, number_of_iterations,

>  				     number_of_iterationsm1);

>    if (!*loop_cond)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: complicated exit condition.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at

> +      (vect_location,

> +       "not vectorized: complicated exit condition.\n");

>  

>    if (integer_zerop (*assumptions)

>        || !*number_of_iterations

>        || chrec_contains_undetermined (*number_of_iterations))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: number of iterations cannot be "

> -			 "computed.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at

> +      (*loop_cond,

> +       "not vectorized: number of iterations cannot be computed.\n");

>  

>    if (integer_zerop (*number_of_iterations))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: number of iterations = 0.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at

> +      (*loop_cond,

> +       "not vectorized: number of iterations = 0.\n");

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Analyze LOOP form and return a loop_vec_info if it is of suitable form.  */

>  

> -loop_vec_info

> +opt_loop_vec_info

>  vect_analyze_loop_form (struct loop *loop, vec_info_shared *shared)

>  {

>    tree assumptions, number_of_iterations, number_of_iterationsm1;

>    gcond *loop_cond, *inner_loop_cond = NULL;

>  

> -  if (! vect_analyze_loop_form_1 (loop, &loop_cond,

> -				  &assumptions, &number_of_iterationsm1,

> -				  &number_of_iterations, &inner_loop_cond))

> -    return NULL;

> +  opt_result res

> +    = vect_analyze_loop_form_1 (loop, &loop_cond,

> +				&assumptions, &number_of_iterationsm1,

> +				&number_of_iterations, &inner_loop_cond);

> +  if (!res)

> +    return opt_loop_vec_info::propagate_failure (res);

>  

>    loop_vec_info loop_vinfo = new _loop_vec_info (loop, shared);

>    LOOP_VINFO_NITERSM1 (loop_vinfo) = number_of_iterationsm1;

> @@ -1387,7 +1351,7 @@ vect_analyze_loop_form (struct loop *loop, vec_info_shared *shared)

>  

>    gcc_assert (!loop->aux);

>    loop->aux = loop_vinfo;

> -  return loop_vinfo;

> +  return opt_loop_vec_info::success (loop_vinfo);

>  }

>  

>  

> @@ -1489,7 +1453,7 @@ vect_active_double_reduction_p (stmt_vec_info stmt_info)

>  

>     Scan the loop stmts and make sure they are all vectorizable.  */

>  

> -static bool

> +static opt_result

>  vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>  {

>    struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);

> @@ -1531,13 +1495,9 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>                   requires to actually do something here.  */

>                if (STMT_VINFO_LIVE_P (stmt_info)

>  		  && !vect_active_double_reduction_p (stmt_info))

> -                {

> -                  if (dump_enabled_p ())

> -		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				     "Unsupported loop-closed phi in "

> -				     "outer-loop.\n");

> -                  return false;

> -                }

> +		return opt_result::failure_at (phi,

> +					       "Unsupported loop-closed phi"

> +					       " in outer-loop.\n");

>  

>                /* If PHI is used in the outer loop, we check that its operand

>                   is defined in the inner loop.  */

> @@ -1546,17 +1506,17 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>                    tree phi_op;

>  

>                    if (gimple_phi_num_args (phi) != 1)

> -                    return false;

> +                    return opt_result::failure_at (phi, "unsupported phi");

>  

>                    phi_op = PHI_ARG_DEF (phi, 0);

>  		  stmt_vec_info op_def_info = loop_vinfo->lookup_def (phi_op);

>  		  if (!op_def_info)

> -                    return false;

> +		    return opt_result::failure_at (phi, "unsupported phi");

>  

>  		  if (STMT_VINFO_RELEVANT (op_def_info) != vect_used_in_outer

>  		      && (STMT_VINFO_RELEVANT (op_def_info)

>  			  != vect_used_in_outer_by_reduction))

> -		    return false;

> +		    return opt_result::failure_at (phi, "unsupported phi");

>                  }

>  

>                continue;

> @@ -1567,13 +1527,10 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>            if ((STMT_VINFO_RELEVANT (stmt_info) == vect_used_in_scope

>                 || STMT_VINFO_LIVE_P (stmt_info))

>                && STMT_VINFO_DEF_TYPE (stmt_info) != vect_induction_def)

> -            {

> -              /* A scalar-dependence cycle that we don't support.  */

> -              if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: scalar dependence cycle.\n");

> -              return false;

> -            }

> +	    /* A scalar-dependence cycle that we don't support.  */

> +	    return opt_result::failure_at (phi,

> +					   "not vectorized:"

> +					   " scalar dependence cycle.\n");

>  

>            if (STMT_VINFO_RELEVANT_P (stmt_info))

>              {

> @@ -1597,24 +1554,25 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>  					      &cost_vec);

>  

>            if (!ok)

> -            {

> -              if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: relevant phi not "

> -				 "supported: %G", phi);

> -	      return false;

> -            }

> +	    return opt_result::failure_at (phi,

> +					   "not vectorized: relevant phi not "

> +					   "supported: %G",

> +					   static_cast <gimple *> (phi));

>          }

>  

>        for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);

>  	   gsi_next (&si))

>          {

>  	  gimple *stmt = gsi_stmt (si);

> -	  if (!gimple_clobber_p (stmt)

> -	      && !vect_analyze_stmt (loop_vinfo->lookup_stmt (stmt),

> +	  if (!gimple_clobber_p (stmt))

> +	    {

> +	      opt_result res

> +		= vect_analyze_stmt (loop_vinfo->lookup_stmt (stmt),

>  				     &need_to_vectorize,

> -				     NULL, NULL, &cost_vec))

> -	    return false;

> +				     NULL, NULL, &cost_vec);

> +	      if (!res)

> +		return res;

> +	    }

>          }

>      } /* bbs */

>  

> @@ -1631,14 +1589,12 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)

>        if (dump_enabled_p ())

>          dump_printf_loc (MSG_NOTE, vect_location,

>  			 "All the computation can be taken out of the loop.\n");

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: redundant loop. no profit to "

> -			 "vectorize.\n");

> -      return false;

> +      return opt_result::failure_at

> +	(vect_location,

> +	 "not vectorized: redundant loop. no profit to vectorize.\n");

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Analyze the cost of the loop described by LOOP_VINFO.  Decide if it

> @@ -1736,7 +1692,7 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)

>    return 1;

>  }

>  

> -static bool

> +static opt_result

>  vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,

>  			   vec<data_reference_p> *datarefs,

>  			   unsigned int *n_stmts)

> @@ -1750,7 +1706,8 @@ vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,

>  	if (is_gimple_debug (stmt))

>  	  continue;

>  	++(*n_stmts);

> -	if (!vect_find_stmt_data_reference (loop, stmt, datarefs))

> +	opt_result res = vect_find_stmt_data_reference (loop, stmt, datarefs);

> +	if (!res)

>  	  {

>  	    if (is_gimple_call (stmt) && loop->safelen)

>  	      {

> @@ -1782,15 +1739,16 @@ vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,

>  		      }

>  		  }

>  	      }

> -	    return false;

> +	    return res;

>  	  }

>  	/* If dependence analysis will give up due to the limit on the

>  	   number of datarefs stop here and fail fatally.  */

>  	if (datarefs->length ()

>  	    > (unsigned)PARAM_VALUE (PARAM_LOOP_MAX_DATAREFS_FOR_DATADEPS))

> -	  return false;

> +	  return opt_result::failure_at (stmt, "exceeded param "

> +					 "loop-max-datarefs-for-datadeps\n");

>        }

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Function vect_analyze_loop_2.

> @@ -1798,10 +1756,10 @@ vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,

>     Apply a set of analyses on LOOP, and create a loop_vec_info struct

>     for it.  The different analyses will record information in the

>     loop_vec_info struct.  */

> -static bool

> +static opt_result

>  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>  {

> -  bool ok;

> +  opt_result ok = opt_result::success ();

>    int res;

>    unsigned int max_vf = MAX_VECTORIZATION_FACTOR;

>    poly_uint64 min_vf = 2;

> @@ -1817,16 +1775,18 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>    /* Gather the data references and count stmts in the loop.  */

>    if (!LOOP_VINFO_DATAREFS (loop_vinfo).exists ())

>      {

> -      if (!vect_get_datarefs_in_loop (loop, LOOP_VINFO_BBS (loop_vinfo),

> -				      &LOOP_VINFO_DATAREFS (loop_vinfo),

> -				      n_stmts))

> +      opt_result res

> +	= vect_get_datarefs_in_loop (loop, LOOP_VINFO_BBS (loop_vinfo),

> +				     &LOOP_VINFO_DATAREFS (loop_vinfo),

> +				     n_stmts);

> +      if (!res)

>  	{

>  	  if (dump_enabled_p ())

>  	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			     "not vectorized: loop contains function "

>  			     "calls or data references that cannot "

>  			     "be analyzed\n");

> -	  return false;

> +	  return res;

>  	}

>        loop_vinfo->shared->save_datarefs ();

>      }

> @@ -1842,7 +1802,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "bad data references.\n");

> -      return false;

> +      return ok;

>      }

>  

>    /* Classify all cross-iteration scalar data-flow cycles.

> @@ -1862,7 +1822,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "bad data access.\n");

> -      return false;

> +      return ok;

>      }

>  

>    /* Data-flow analysis to detect stmts that do not need to be vectorized.  */

> @@ -1873,7 +1833,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "unexpected pattern.\n");

> -      return false;

> +      return ok;

>      }

>  

>    /* While the rest of the analysis below depends on it in some way.  */

> @@ -1885,15 +1845,16 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>       FORNOW: fail at the first data dependence that we encounter.  */

>  

>    ok = vect_analyze_data_ref_dependences (loop_vinfo, &max_vf);

> -  if (!ok

> -      || (max_vf != MAX_VECTORIZATION_FACTOR

> -	  && maybe_lt (max_vf, min_vf)))

> +  if (!ok)

>      {

>        if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "bad data dependence.\n");

> -      return false;

> +	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +			 "bad data dependence.\n");

> +      return ok;

>      }

> +  if (max_vf != MAX_VECTORIZATION_FACTOR

> +      && maybe_lt (max_vf, min_vf))

> +    return opt_result::failure_at (vect_location, "bad data dependence.\n");

>    LOOP_VINFO_MAX_VECT_FACTOR (loop_vinfo) = max_vf;

>  

>    ok = vect_determine_vectorization_factor (loop_vinfo);

> @@ -1902,16 +1863,11 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "can't determine vectorization factor.\n");

> -      return false;

> +      return ok;

>      }

>    if (max_vf != MAX_VECTORIZATION_FACTOR

>        && maybe_lt (max_vf, LOOP_VINFO_VECT_FACTOR (loop_vinfo)))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "bad data dependence.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (vect_location, "bad data dependence.\n");

>  

>    /* Compute the scalar iteration cost.  */

>    vect_compute_single_scalar_iteration_cost (loop_vinfo);

> @@ -1922,7 +1878,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)

>    /* Check the SLP opportunities in the loop, analyze and build SLP trees.  */

>    ok = vect_analyze_slp (loop_vinfo, *n_stmts);

>    if (!ok)

> -    return false;

> +    return ok;

>  

>    /* If there are any SLP instances mark them as pure_slp.  */

>    bool slp = vect_make_slp_decision (loop_vinfo);

> @@ -1969,7 +1925,7 @@ start_over:

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "bad data alignment.\n");

> -      return false;

> +      return ok;

>      }

>  

>    /* Prune the list of ddrs to be tested at run-time by versioning for alias.

> @@ -1977,7 +1933,7 @@ start_over:

>       since we use grouping information gathered by interleaving analysis.  */

>    ok = vect_prune_runtime_alias_test_list (loop_vinfo);

>    if (!ok)

> -    return false;

> +    return ok;

>  

>    /* Do not invoke vect_enhance_data_refs_alignment for epilogue

>       vectorization, since we do not want to add extra peeling or

> @@ -1989,12 +1945,7 @@ start_over:

>    else

>      ok = vect_verify_datarefs_alignment (loop_vinfo);

>    if (!ok)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "bad data alignment.\n");

> -      return false;

> -    }

> +    return ok;

>  

>    if (slp)

>      {

> @@ -2004,7 +1955,11 @@ start_over:

>        unsigned old_size = LOOP_VINFO_SLP_INSTANCES (loop_vinfo).length ();

>        vect_slp_analyze_operations (loop_vinfo);

>        if (LOOP_VINFO_SLP_INSTANCES (loop_vinfo).length () != old_size)

> -	goto again;

> +	{

> +	  ok = opt_result::failure_at (vect_location,

> +				       "unsupported SLP instances\n");

> +	  goto again;

> +	}

>      }

>  

>    /* Scan all the remaining operations in the loop that are not subject

> @@ -2015,7 +1970,7 @@ start_over:

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			 "bad operation or unsupported loop bound.\n");

> -      return false;

> +      return ok;

>      }

>  

>    /* Decide whether to use a fully-masked loop for this vectorization

> @@ -2044,26 +1999,22 @@ start_over:

>        tree scalar_niters = LOOP_VINFO_NITERSM1 (loop_vinfo);

>  

>        if (known_lt (wi::to_widest (scalar_niters), vf))

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_NOTE, vect_location,

> -			     "loop has no enough iterations to support"

> -			     " peeling for gaps.\n");

> -	  return false;

> -	}

> +	return opt_result::failure_at (vect_location,

> +				       "loop has no enough iterations to"

> +				       " support peeling for gaps.\n");

>      }

>  

>    /* Check the costings of the loop make vectorizing worthwhile.  */

>    res = vect_analyze_loop_costing (loop_vinfo);

>    if (res < 0)

> -    goto again;

> -  if (!res)

>      {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "Loop costings not worthwhile.\n");

> -      return false;

> +      ok = opt_result::failure_at (vect_location,

> +				   "Loop costings may not be worthwhile.\n");

> +      goto again;

>      }

> +  if (!res)

> +    return opt_result::failure_at (vect_location,

> +				   "Loop costings not worthwhile.\n");

>  

>    /* Decide whether we need to create an epilogue loop to handle

>       remaining scalar iterations.  */

> @@ -2112,10 +2063,9 @@ start_over:

>  					   single_exit (LOOP_VINFO_LOOP

>  							 (loop_vinfo))))

>          {

> -          if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: can't create required "

> -			     "epilog loop\n");

> +	  ok = opt_result::failure_at (vect_location,

> +				       "not vectorized: can't create required "

> +				       "epilog loop\n");

>            goto again;

>          }

>      }

> @@ -2154,17 +2104,20 @@ start_over:

>  			LOOP_VINFO_VECT_FACTOR (loop_vinfo)));

>  

>    /* Ok to vectorize!  */

> -  return true;

> +  return opt_result::success ();

>  

>  again:

> +  /* Ensure that "ok" is false (with an opt_problem if dumping is enabled).  */

> +  gcc_assert (!ok);

> +

>    /* Try again with SLP forced off but if we didn't do any SLP there is

>       no point in re-trying.  */

>    if (!slp)

> -    return false;

> +    return ok;

>  

>    /* If there are reduction chains re-trying will fail anyway.  */

>    if (! LOOP_VINFO_REDUCTION_CHAINS (loop_vinfo).is_empty ())

> -    return false;

> +    return ok;

>  

>    /* Likewise if the grouped loads or stores in the SLP cannot be handled

>       via interleaving or lane instructions.  */

> @@ -2183,7 +2136,8 @@ again:

>        if (! vect_store_lanes_supported (vectype, size, false)

>  	 && ! known_eq (TYPE_VECTOR_SUBPARTS (vectype), 1U)

>  	 && ! vect_grouped_store_supported (vectype, size))

> -       return false;

> +	return opt_result::failure_at (vinfo->stmt,

> +				       "unsupported grouped store\n");

>        FOR_EACH_VEC_ELT (SLP_INSTANCE_LOADS (instance), j, node)

>  	{

>  	  vinfo = SLP_TREE_SCALAR_STMTS (node)[0];

> @@ -2194,7 +2148,8 @@ again:

>  	  if (! vect_load_lanes_supported (vectype, size, false)

>  	      && ! vect_grouped_load_supported (vectype, single_element_p,

>  						size))

> -	    return false;

> +	    return opt_result::failure_at (vinfo->stmt,

> +					   "unsupported grouped load\n");

>  	}

>      }

>  

> @@ -2263,11 +2218,10 @@ again:

>     for it.  The different analyses will record information in the

>     loop_vec_info struct.  If ORIG_LOOP_VINFO is not NULL epilogue must

>     be vectorized.  */

> -loop_vec_info

> +opt_loop_vec_info

>  vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,

>  		   vec_info_shared *shared)

>  {

> -  loop_vec_info loop_vinfo;

>    auto_vector_sizes vector_sizes;

>  

>    /* Autodetect first vector size we try.  */

> @@ -2280,35 +2234,28 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,

>    if (loop_outer (loop)

>        && loop_vec_info_for_loop (loop_outer (loop))

>        && LOOP_VINFO_VECTORIZABLE_P (loop_vec_info_for_loop (loop_outer (loop))))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_NOTE, vect_location,

> -			 "outer-loop already vectorized.\n");

> -      return NULL;

> -    }

> +    return opt_loop_vec_info::failure_at (vect_location,

> +					  "outer-loop already vectorized.\n");

>  

>    if (!find_loop_nest (loop, &shared->loop_nest))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: loop nest containing two "

> -			 "or more consecutive inner loops cannot be "

> -			 "vectorized\n");

> -      return NULL;

> -    }

> +    return opt_loop_vec_info::failure_at

> +      (vect_location,

> +       "not vectorized: loop nest containing two or more consecutive inner"

> +       " loops cannot be vectorized\n");

>  

>    unsigned n_stmts = 0;

>    poly_uint64 autodetected_vector_size = 0;

>    while (1)

>      {

>        /* Check the CFG characteristics of the loop (nesting, entry/exit).  */

> -      loop_vinfo = vect_analyze_loop_form (loop, shared);

> +      opt_loop_vec_info loop_vinfo

> +	= vect_analyze_loop_form (loop, shared);

>        if (!loop_vinfo)

>  	{

>  	  if (dump_enabled_p ())

>  	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>  			     "bad loop form.\n");

> -	  return NULL;

> +	  return loop_vinfo;

>  	}

>  

>        bool fatal = false;

> @@ -2316,7 +2263,8 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,

>        if (orig_loop_vinfo)

>  	LOOP_VINFO_ORIG_LOOP_INFO (loop_vinfo) = orig_loop_vinfo;

>  

> -      if (vect_analyze_loop_2 (loop_vinfo, fatal, &n_stmts))

> +      opt_result res = vect_analyze_loop_2 (loop_vinfo, fatal, &n_stmts);

> +      if (res)

>  	{

>  	  LOOP_VINFO_VECTORIZABLE_P (loop_vinfo) = 1;

>  

> @@ -2335,7 +2283,7 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,

>        if (fatal

>  	  || next_size == vector_sizes.length ()

>  	  || known_eq (current_vector_size, 0U))

> -	return NULL;

> +	return opt_loop_vec_info::propagate_failure (res);

>  

>        /* Try the next biggest vector size.  */

>        current_vector_size = vector_sizes[next_size++];

> diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c

> index ae1c453..f60fea0 100644

> --- a/gcc/tree-vect-slp.c

> +++ b/gcc/tree-vect-slp.c

> @@ -2071,7 +2071,7 @@ vect_analyze_slp_instance (vec_info *vinfo,

>  /* Check if there are stmts in the loop can be vectorized using SLP.  Build SLP

>     trees of packed scalar stmts if SLP is possible.  */

>  

> -bool

> +opt_result

>  vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)

>  {

>    unsigned int i;

> @@ -2111,7 +2111,7 @@ vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)

>  				   max_tree_size);

>      }

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c

> index 7a6efdb..8108d52 100644

> --- a/gcc/tree-vect-stmts.c

> +++ b/gcc/tree-vect-stmts.c

> @@ -448,7 +448,7 @@ exist_non_indexing_operands_for_use_p (tree use, stmt_vec_info stmt_info)

>  

>     Return true if everything is as expected. Return false otherwise.  */

>  

> -static bool

> +static opt_result

>  process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,

>  	     enum vect_relevant relevant, vec<stmt_vec_info> *worklist,

>  	     bool force)

> @@ -460,18 +460,15 @@ process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,

>    /* case 1: we are only interested in uses that need to be vectorized.  Uses

>       that are used for address computation are not considered relevant.  */

>    if (!force && !exist_non_indexing_operands_for_use_p (use, stmt_vinfo))

> -     return true;

> +    return opt_result::success ();

>  

>    if (!vect_is_simple_use (use, loop_vinfo, &dt, &dstmt_vinfo))

> -    {

> -      if (dump_enabled_p ())

> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -                         "not vectorized: unsupported use in stmt.\n");

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt_vinfo->stmt,

> +				   "not vectorized:"

> +				   " unsupported use in stmt.\n");

>  

>    if (!dstmt_vinfo)

> -    return true;

> +    return opt_result::success ();

>  

>    def_bb = gimple_bb (dstmt_vinfo->stmt);

>  

> @@ -493,7 +490,7 @@ process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,

>        gcc_assert (STMT_VINFO_RELEVANT (dstmt_vinfo) < vect_used_by_reduction);

>        gcc_assert (STMT_VINFO_LIVE_P (dstmt_vinfo)

>  		  || STMT_VINFO_RELEVANT (dstmt_vinfo) > vect_unused_in_scope);

> -      return true;

> +      return opt_result::success ();

>      }

>  

>    /* case 3a: outer-loop stmt defining an inner-loop stmt:

> @@ -582,12 +579,12 @@ process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,

>        if (dump_enabled_p ())

>  	dump_printf_loc (MSG_NOTE, vect_location,

>                           "induction value on backedge.\n");

> -      return true;

> +      return opt_result::success ();

>      }

>  

>  

>    vect_mark_relevant (worklist, dstmt_vinfo, relevant, false);

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> @@ -607,7 +604,7 @@ process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,

>  

>     This pass detects such stmts.  */

>  

> -bool

> +opt_result

>  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  {

>    struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);

> @@ -684,38 +681,24 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  		&& relevant != vect_used_in_scope

>  		&& relevant != vect_used_by_reduction

>  		&& relevant != vect_used_only_live)

> -	      {

> -		if (dump_enabled_p ())

> -		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				   "unsupported use of reduction.\n");

> -		return false;

> -	      }

> +	      return opt_result::failure_at

> +		(stmt_vinfo->stmt, "unsupported use of reduction.\n");

>  	    break;

>  

>            case vect_nested_cycle:

>  	    if (relevant != vect_unused_in_scope

>  		&& relevant != vect_used_in_outer_by_reduction

>  		&& relevant != vect_used_in_outer)

> -              {

> -                if (dump_enabled_p ())

> -                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -                                   "unsupported use of nested cycle.\n");

> -

> -                return false;

> -              }

> +	      return opt_result::failure_at

> +		(stmt_vinfo->stmt, "unsupported use of nested cycle.\n");

>              break;

>  

>            case vect_double_reduction_def:

>  	    if (relevant != vect_unused_in_scope

>  		&& relevant != vect_used_by_reduction

>  		&& relevant != vect_used_only_live)

> -              {

> -                if (dump_enabled_p ())

> -                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -                                   "unsupported use of double reduction.\n");

> -

> -                return false;

> -              }

> +	      return opt_result::failure_at

> +		(stmt_vinfo->stmt, "unsupported use of double reduction.\n");

>              break;

>  

>            default:

> @@ -735,20 +718,28 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  	      i = 1;

>  	      if (rhs_code == COND_EXPR && COMPARISON_CLASS_P (op))

>  		{

> -		  if (!process_use (stmt_vinfo, TREE_OPERAND (op, 0),

> -				    loop_vinfo, relevant, &worklist, false)

> -		      || !process_use (stmt_vinfo, TREE_OPERAND (op, 1),

> -				       loop_vinfo, relevant, &worklist, false))

> -		    return false;

> +		  opt_result res

> +		    = process_use (stmt_vinfo, TREE_OPERAND (op, 0),

> +				   loop_vinfo, relevant, &worklist, false);

> +		  if (!res)

> +		    return res;

> +		  res = process_use (stmt_vinfo, TREE_OPERAND (op, 1),

> +				     loop_vinfo, relevant, &worklist, false);

> +		  if (!res)

> +		    return res;

>  		  i = 2;

>  		}

>  	      for (; i < gimple_num_ops (assign); i++)

>  		{

>  		  op = gimple_op (assign, i);

> -                  if (TREE_CODE (op) == SSA_NAME

> -		      && !process_use (stmt_vinfo, op, loop_vinfo, relevant,

> -				       &worklist, false))

> -                    return false;

> +                  if (TREE_CODE (op) == SSA_NAME)

> +		    {

> +		      opt_result res

> +			= process_use (stmt_vinfo, op, loop_vinfo, relevant,

> +				       &worklist, false);

> +		      if (!res)

> +			return res;

> +		    }

>                   }

>              }

>  	  else if (gcall *call = dyn_cast <gcall *> (stmt_vinfo->stmt))

> @@ -756,9 +747,11 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  	      for (i = 0; i < gimple_call_num_args (call); i++)

>  		{

>  		  tree arg = gimple_call_arg (call, i);

> -		  if (!process_use (stmt_vinfo, arg, loop_vinfo, relevant,

> -				    &worklist, false))

> -                    return false;

> +		  opt_result res

> +		    = process_use (stmt_vinfo, arg, loop_vinfo, relevant,

> +				   &worklist, false);

> +		  if (!res)

> +		    return res;

>  		}

>  	    }

>          }

> @@ -766,9 +759,11 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  	FOR_EACH_PHI_OR_STMT_USE (use_p, stmt_vinfo->stmt, iter, SSA_OP_USE)

>            {

>              tree op = USE_FROM_PTR (use_p);

> -	    if (!process_use (stmt_vinfo, op, loop_vinfo, relevant,

> -			      &worklist, false))

> -              return false;

> +	    opt_result res

> +	      = process_use (stmt_vinfo, op, loop_vinfo, relevant,

> +			     &worklist, false);

> +	    if (!res)

> +	      return res;

>            }

>  

>        if (STMT_VINFO_GATHER_SCATTER_P (stmt_vinfo))

> @@ -776,13 +771,15 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)

>  	  gather_scatter_info gs_info;

>  	  if (!vect_check_gather_scatter (stmt_vinfo, loop_vinfo, &gs_info))

>  	    gcc_unreachable ();

> -	  if (!process_use (stmt_vinfo, gs_info.offset, loop_vinfo, relevant,

> -			    &worklist, true))

> -	    return false;

> +	  opt_result res

> +	    = process_use (stmt_vinfo, gs_info.offset, loop_vinfo, relevant,

> +			   &worklist, true);

> +	  if (!res)

> +	    return res;

>  	}

>      } /* while worklist */

>  

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Compute the prologue cost for invariant or constant operands.  */

> @@ -9382,7 +9379,7 @@ can_vectorize_live_stmts (stmt_vec_info stmt_info, gimple_stmt_iterator *gsi,

>  

>  /* Make sure the statement is vectorizable.  */

>  

> -bool

> +opt_result

>  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>  		   slp_tree node, slp_instance node_instance,

>  		   stmt_vector_for_cost *cost_vec)

> @@ -9398,13 +9395,10 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>  		     stmt_info->stmt);

>  

>    if (gimple_has_volatile_ops (stmt_info->stmt))

> -    {

> -      if (dump_enabled_p ())

> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -                         "not vectorized: stmt has volatile operands\n");

> -

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt_info->stmt,

> +				   "not vectorized:"

> +				   " stmt has volatile operands: %G\n",

> +				   stmt_info->stmt);

>  

>    if (STMT_VINFO_IN_PATTERN_P (stmt_info)

>        && node == NULL

> @@ -9425,10 +9419,12 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>  				 "==> examining pattern def statement: %G",

>  				 pattern_def_stmt_info->stmt);

>  

> -	      if (!vect_analyze_stmt (pattern_def_stmt_info,

> -				      need_to_vectorize, node, node_instance,

> -				      cost_vec))

> -		return false;

> +	      opt_result res

> +		= vect_analyze_stmt (pattern_def_stmt_info,

> +				     need_to_vectorize, node, node_instance,

> +				     cost_vec);

> +	      if (!res)

> +		return res;

>  	    }

>  	}

>      }

> @@ -9468,7 +9464,7 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>            if (dump_enabled_p ())

>              dump_printf_loc (MSG_NOTE, vect_location, "irrelevant.\n");

>  

> -          return true;

> +          return opt_result::success ();

>          }

>      }

>    else if (STMT_VINFO_IN_PATTERN_P (stmt_info)

> @@ -9483,9 +9479,11 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>  			 "==> examining pattern statement: %G",

>  			 pattern_stmt_info->stmt);

>  

> -      if (!vect_analyze_stmt (pattern_stmt_info, need_to_vectorize, node,

> -			      node_instance, cost_vec))

> -        return false;

> +      opt_result res

> +	= vect_analyze_stmt (pattern_stmt_info, need_to_vectorize, node,

> +			     node_instance, cost_vec);

> +      if (!res)

> +	return res;

>     }

>  

>    switch (STMT_VINFO_DEF_TYPE (stmt_info))

> @@ -9528,7 +9526,7 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>      {

>        dump_printf_loc (MSG_NOTE, vect_location,

>  		       "handled only by SLP analysis\n");

> -      return true;

> +      return opt_result::success ();

>      }

>  

>    ok = true;

> @@ -9573,30 +9571,22 @@ vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,

>      }

>  

>    if (!ok)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: relevant stmt not supported: %G",

> -			 stmt_info->stmt);

> -

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt_info->stmt,

> +				   "not vectorized:"

> +				   " relevant stmt not supported: %G",

> +				   stmt_info->stmt);

>  

>    /* Stmts that are (also) "live" (i.e. - that are used out of the loop)

>        need extra handling, except for vectorizable reductions.  */

>    if (!bb_vinfo

>        && STMT_VINFO_TYPE (stmt_info) != reduc_vec_info_type

>        && !can_vectorize_live_stmts (stmt_info, NULL, node, NULL, cost_vec))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: live stmt not supported: %G",

> -			 stmt_info->stmt);

> +    return opt_result::failure_at (stmt_info->stmt,

> +				   "not vectorized:"

> +				   " live stmt not supported: %G",

> +				   stmt_info->stmt);

>  

> -       return false;

> -    }

> -

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  

> @@ -10537,7 +10527,7 @@ vect_gen_while_not (gimple_seq *seq, tree mask_type, tree start_index,

>       number of units needed to vectorize STMT_INFO, or NULL_TREE if the

>       statement does not help to determine the overall number of units.  */

>  

> -bool

> +opt_result

>  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>  				tree *stmt_vectype_out,

>  				tree *nunits_vectype_out)

> @@ -10560,22 +10550,17 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>  	  if (dump_enabled_p ())

>  	    dump_printf_loc (MSG_NOTE, vect_location,

>  			     "defer to SIMD clone analysis.\n");

> -	  return true;

> +	  return opt_result::success ();

>  	}

>  

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: irregular stmt.%G", stmt);

> -      return false;

> +      return opt_result::failure_at (stmt,

> +				     "not vectorized: irregular stmt.%G", stmt);

>      }

>  

>    if (VECTOR_MODE_P (TYPE_MODE (gimple_expr_type (stmt))))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: vector stmt in loop:%G", stmt);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized: vector stmt in loop:%G",

> +				   stmt);

>  

>    tree vectype;

>    tree scalar_type = NULL_TREE;

> @@ -10606,7 +10591,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>  	      if (dump_enabled_p ())

>  		dump_printf_loc (MSG_NOTE, vect_location,

>  				 "pure bool operation.\n");

> -	      return true;

> +	      return opt_result::success ();

>  	    }

>  	}

>  

> @@ -10615,13 +10600,10 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>  			 "get vectype for scalar type:  %T\n", scalar_type);

>        vectype = get_vectype_for_scalar_type (scalar_type);

>        if (!vectype)

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: unsupported data-type %T\n",

> -			     scalar_type);

> -	  return false;

> -	}

> +	return opt_result::failure_at (stmt,

> +				       "not vectorized:"

> +				       " unsupported data-type %T\n",

> +				       scalar_type);

>  

>        if (!*stmt_vectype_out)

>  	*stmt_vectype_out = vectype;

> @@ -10652,24 +10634,16 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>        nunits_vectype = get_vectype_for_scalar_type (scalar_type);

>      }

>    if (!nunits_vectype)

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: unsupported data-type %T\n",

> -			 scalar_type);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized: unsupported data-type %T\n",

> +				   scalar_type);

>  

>    if (maybe_ne (GET_MODE_SIZE (TYPE_MODE (vectype)),

>  		GET_MODE_SIZE (TYPE_MODE (nunits_vectype))))

> -    {

> -      if (dump_enabled_p ())

> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			 "not vectorized: different sized vector "

> -			 "types in statement, %T and %T\n",

> -			 vectype, nunits_vectype);

> -      return false;

> -    }

> +    return opt_result::failure_at (stmt,

> +				   "not vectorized: different sized vector "

> +				   "types in statement, %T and %T\n",

> +				   vectype, nunits_vectype);

>  

>    if (dump_enabled_p ())

>      {

> @@ -10682,14 +10656,14 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,

>      }

>  

>    *nunits_vectype_out = nunits_vectype;

> -  return true;

> +  return opt_result::success ();

>  }

>  

>  /* Try to determine the correct vector type for STMT_INFO, which is a

>     statement that produces a scalar boolean result.  Return the vector

>     type on success, otherwise return NULL_TREE.  */

>  

> -tree

> +opt_tree

>  vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)

>  {

>    gimple *stmt = stmt_info->stmt;

> @@ -10704,12 +10678,8 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)

>        mask_type = get_mask_type_for_scalar_type (scalar_type);

>  

>        if (!mask_type)

> -	{

> -	  if (dump_enabled_p ())

> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -			     "not vectorized: unsupported mask\n");

> -	  return NULL_TREE;

> -	}

> +	return opt_tree::failure_at (stmt,

> +				     "not vectorized: unsupported mask\n");

>      }

>    else

>      {

> @@ -10720,13 +10690,9 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)

>        FOR_EACH_SSA_TREE_OPERAND (rhs, stmt, iter, SSA_OP_USE)

>  	{

>  	  if (!vect_is_simple_use (rhs, stmt_info->vinfo, &dt, &vectype))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: can't compute mask type "

> -				 "for statement, %G", stmt);

> -	      return NULL_TREE;

> -	    }

> +	    return opt_tree::failure_at (stmt,

> +					 "not vectorized:can't compute mask"

> +					 " type for statement, %G", stmt);

>  

>  	  /* No vectype probably means external definition.

>  	     Allow it in case there is another operand which

> @@ -10738,25 +10704,17 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)

>  	    mask_type = vectype;

>  	  else if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_type),

>  			     TYPE_VECTOR_SUBPARTS (vectype)))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: different sized masks "

> -				 "types in statement, %T and %T\n",

> -				 mask_type, vectype);

> -	      return NULL_TREE;

> -	    }

> +	    return opt_tree::failure_at (stmt,

> +					 "not vectorized: different sized mask"

> +					 " types in statement, %T and %T\n",

> +					 mask_type, vectype);

>  	  else if (VECTOR_BOOLEAN_TYPE_P (mask_type)

>  		   != VECTOR_BOOLEAN_TYPE_P (vectype))

> -	    {

> -	      if (dump_enabled_p ())

> -		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -				 "not vectorized: mixed mask and "

> -				 "nonmask vector types in statement, "

> -				 "%T and %T\n",

> -				 mask_type, vectype);

> -	      return NULL_TREE;

> -	    }

> +	    return opt_tree::failure_at (stmt,

> +					 "not vectorized: mixed mask and "

> +					 "nonmask vector types in statement, "

> +					 "%T and %T\n",

> +					 mask_type, vectype);

>  	}

>  

>        /* We may compare boolean value loaded as vector of integers.

> @@ -10770,9 +10728,10 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)

>  

>    /* No mask_type should mean loop invariant predicate.

>       This is probably a subject for optimization in if-conversion.  */

> -  if (!mask_type && dump_enabled_p ())

> -    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> -		     "not vectorized: can't compute mask type "

> -		     "for statement, %G", stmt);

> -  return mask_type;

> +  if (!mask_type)

> +    return opt_tree::failure_at (stmt,

> +				 "not vectorized: can't compute mask type "

> +				 "for statement: %G", stmt);

> +

> +  return opt_tree::success (mask_type);

>  }

> diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c

> index 23bddf3..747fb67 100644

> --- a/gcc/tree-vectorizer.c

> +++ b/gcc/tree-vectorizer.c

> @@ -79,6 +79,7 @@ along with GCC; see the file COPYING3.  If not see

>  #include "stringpool.h"

>  #include "attribs.h"

>  #include "gimple-pretty-print.h"

> +#include "opt-problem.h"

>  

>  

>  /* Loop or bb location, with hotness information.  */

> @@ -860,13 +861,25 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,

>    vect_location = find_loop_location (loop);

>    if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION

>        && dump_enabled_p ())

> -    dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",

> +    dump_printf (MSG_NOTE | MSG_PRIORITY_INTERNALS,

> +		 "\nAnalyzing loop at %s:%d\n",

>  		 LOCATION_FILE (vect_location.get_location_t ()),

>  		 LOCATION_LINE (vect_location.get_location_t ()));

>  

> -  loop_vec_info loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

> +  /* Try to analyze the loop, retaining an opt_problem if dump_enabled_p.  */

> +  opt_loop_vec_info loop_vinfo

> +    = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

>    loop->aux = loop_vinfo;

>  

> +  if (!loop_vinfo)

> +    if (dump_enabled_p ())

> +      if (opt_problem *problem = loop_vinfo.get_problem ())

> +	{

> +	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

> +			   "couldn't vectorize loop\n");

> +	  problem->emit_and_clear ();

> +	}

> +

>    if (!loop_vinfo || !LOOP_VINFO_VECTORIZABLE_P (loop_vinfo))

>      {

>        /* Free existing information if loop is analyzed with some

> diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h

> index af5d5bf..63cff79 100644

> --- a/gcc/tree-vectorizer.h

> +++ b/gcc/tree-vectorizer.h

> @@ -612,6 +612,12 @@ typedef struct _loop_vec_info : public vec_info {

>  #define LOOP_VINFO_ORIG_MAX_VECT_FACTOR(L) \

>    (LOOP_VINFO_MAX_VECT_FACTOR (LOOP_VINFO_ORIG_LOOP_INFO (L)))

>  

> +/* Wrapper for loop_vec_info, for tracking success/failure, where a non-NULL

> +   value signifies success, and a NULL value signifies failure, supporting

> +   propagating an opt_problem * describing the failure back up the call

> +   stack.  */

> +typedef opt_pointer_wrapper <loop_vec_info> opt_loop_vec_info;

> +

>  static inline loop_vec_info

>  loop_vec_info_for_loop (struct loop *loop)

>  {

> @@ -1473,7 +1479,7 @@ extern unsigned record_stmt_cost (stmt_vector_for_cost *, int,

>  extern stmt_vec_info vect_finish_replace_stmt (stmt_vec_info, gimple *);

>  extern stmt_vec_info vect_finish_stmt_generation (stmt_vec_info, gimple *,

>  						  gimple_stmt_iterator *);

> -extern bool vect_mark_stmts_to_be_vectorized (loop_vec_info);

> +extern opt_result vect_mark_stmts_to_be_vectorized (loop_vec_info);

>  extern tree vect_get_store_rhs (stmt_vec_info);

>  extern tree vect_get_vec_def_for_operand_1 (stmt_vec_info, enum vect_def_type);

>  extern tree vect_get_vec_def_for_operand (tree, stmt_vec_info, tree = NULL);

> @@ -1487,8 +1493,8 @@ extern tree vect_get_vec_def_for_stmt_copy (vec_info *, tree);

>  extern bool vect_transform_stmt (stmt_vec_info, gimple_stmt_iterator *,

>  				 slp_tree, slp_instance);

>  extern void vect_remove_stores (stmt_vec_info);

> -extern bool vect_analyze_stmt (stmt_vec_info, bool *, slp_tree, slp_instance,

> -			       stmt_vector_for_cost *);

> +extern opt_result vect_analyze_stmt (stmt_vec_info, bool *, slp_tree,

> +				     slp_instance, stmt_vector_for_cost *);

>  extern bool vectorizable_condition (stmt_vec_info, gimple_stmt_iterator *,

>  				    stmt_vec_info *, tree, int, slp_tree,

>  				    stmt_vector_for_cost *);

> @@ -1504,8 +1510,9 @@ extern tree vect_gen_perm_mask_checked (tree, const vec_perm_indices &);

>  extern void optimize_mask_stores (struct loop*);

>  extern gcall *vect_gen_while (tree, tree, tree);

>  extern tree vect_gen_while_not (gimple_seq *, tree, tree, tree);

> -extern bool vect_get_vector_types_for_stmt (stmt_vec_info, tree *, tree *);

> -extern tree vect_get_mask_type_for_stmt (stmt_vec_info);

> +extern opt_result vect_get_vector_types_for_stmt (stmt_vec_info, tree *,

> +						  tree *);

> +extern opt_tree vect_get_mask_type_for_stmt (stmt_vec_info);

>  

>  /* In tree-vect-data-refs.c.  */

>  extern bool vect_can_force_dr_alignment_p (const_tree, unsigned int);

> @@ -1513,21 +1520,21 @@ extern enum dr_alignment_support vect_supportable_dr_alignment

>                                             (dr_vec_info *, bool);

>  extern tree vect_get_smallest_scalar_type (stmt_vec_info, HOST_WIDE_INT *,

>                                             HOST_WIDE_INT *);

> -extern bool vect_analyze_data_ref_dependences (loop_vec_info, unsigned int *);

> +extern opt_result vect_analyze_data_ref_dependences (loop_vec_info, unsigned int *);

>  extern bool vect_slp_analyze_instance_dependence (slp_instance);

> -extern bool vect_enhance_data_refs_alignment (loop_vec_info);

> -extern bool vect_analyze_data_refs_alignment (loop_vec_info);

> -extern bool vect_verify_datarefs_alignment (loop_vec_info);

> +extern opt_result vect_enhance_data_refs_alignment (loop_vec_info);

> +extern opt_result vect_analyze_data_refs_alignment (loop_vec_info);

> +extern opt_result vect_verify_datarefs_alignment (loop_vec_info);

>  extern bool vect_slp_analyze_and_verify_instance_alignment (slp_instance);

> -extern bool vect_analyze_data_ref_accesses (vec_info *);

> -extern bool vect_prune_runtime_alias_test_list (loop_vec_info);

> +extern opt_result vect_analyze_data_ref_accesses (vec_info *);

> +extern opt_result vect_prune_runtime_alias_test_list (loop_vec_info);

>  extern bool vect_gather_scatter_fn_p (bool, bool, tree, tree, unsigned int,

>  				      signop, int, internal_fn *, tree *);

>  extern bool vect_check_gather_scatter (stmt_vec_info, loop_vec_info,

>  				       gather_scatter_info *);

> -extern bool vect_find_stmt_data_reference (loop_p, gimple *,

> -					   vec<data_reference_p> *);

> -extern bool vect_analyze_data_refs (vec_info *, poly_uint64 *);

> +extern opt_result vect_find_stmt_data_reference (loop_p, gimple *,

> +						 vec<data_reference_p> *);

> +extern opt_result vect_analyze_data_refs (vec_info *, poly_uint64 *);

>  extern void vect_record_base_alignments (vec_info *);

>  extern tree vect_create_data_ref_ptr (stmt_vec_info, tree, struct loop *, tree,

>  				      tree *, gimple_stmt_iterator *,

> @@ -1563,8 +1570,9 @@ extern stmt_vec_info vect_force_simple_reduction (loop_vec_info, stmt_vec_info,

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

> -					vec_info_shared *);

> +extern opt_loop_vec_info vect_analyze_loop (struct loop *,

> +					    loop_vec_info,

> +					    vec_info_shared *);

>  extern tree vect_build_loop_niters (loop_vec_info, bool * = NULL);

>  extern void vect_gen_vector_loop_niters (loop_vec_info, tree, tree *,

>  					 tree *, bool);

> @@ -1577,7 +1585,8 @@ extern tree vect_get_loop_mask (gimple_stmt_iterator *, vec_loop_masks *,

>  

>  /* Drive for loop transformation stage.  */

>  extern struct loop *vect_transform_loop (loop_vec_info);

> -extern loop_vec_info vect_analyze_loop_form (struct loop *, vec_info_shared *);

> +extern opt_loop_vec_info vect_analyze_loop_form (struct loop *,

> +						 vec_info_shared *);

>  extern bool vectorizable_live_operation (stmt_vec_info, gimple_stmt_iterator *,

>  					 slp_tree, int, stmt_vec_info *,

>  					 stmt_vector_for_cost *);

> @@ -1602,7 +1611,7 @@ extern bool vect_transform_slp_perm_load (slp_tree, vec<tree> ,

>  					  slp_instance, bool, unsigned *);

>  extern bool vect_slp_analyze_operations (vec_info *);

>  extern void vect_schedule_slp (vec_info *);

> -extern bool vect_analyze_slp (vec_info *, unsigned);

> +extern opt_result vect_analyze_slp (vec_info *, unsigned);

>  extern bool vect_make_slp_decision (loop_vec_info);

>  extern void vect_detect_hybrid_slp (loop_vec_info);

>  extern void vect_get_slp_defs (vec<tree> , slp_tree, vec<vec<tree> > *);

> 


-- 
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)
Richard Sandiford Oct. 4, 2018, 5:12 p.m. | #2
Richard Biener <rguenther@suse.de> writes:
> On Fri, 28 Sep 2018, David Malcolm wrote:

>> This is v3 of the patch; previous versions were:

>>   v2: https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html

>>   v1: https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html

>> 

>> This patch introduces a class opt_problem, along with wrapper

>> classes for bool (opt_result) and for pointers (e.g. opt_loop_vec_info

>> for loop_vec_info).

>> 

>> opt_problem instances are created when an optimization problem

>> is encountered, but only if dump_enabled_p.  They are manually

>> propagated up the callstack, and are manually reported at the

>> "top level" of an optimization if dumping is enabled, to give the user

>> a concise summary of the problem *after* the failure is reported.

>> In particular, the location of the problematic statement is

>> captured and emitted, rather than just the loop's location.

>> 

>> For example:

>> 

>> no-vfa-vect-102.c:24:3: missed: couldn't vectorize loop

>> no-vfa-vect-102.c:27:7: missed: statement clobbers memory: __asm__

> __volatile__("" : : : "memory");

>> 

>> Changed in v3:

>> * This version bootstraps and passes regression testing (on

>>   x86_64-pc-linux-gnu).

>> * added selftests, to exercise the opt_problem machinery

>> * removed the "bool to opt_result" ctor, so that attempts to

>>   use e.g. return a bool from an opt_result-returning function

>>   will fail at compile time

>> * use formatted printing within opt_problem ctor to replace the

>>   various dump_printf_loc calls

>> * dropped i18n

>> * changed the sense of vect_analyze_data_ref_dependence's return

>>   value (see the ChangeLog)

>> * add MSG_PRIORITY_REEMITTED, so that -fopt-info can show the

>>   messages, without them messing up the counts in scan-tree-dump-times

>>   in DejaGnu tests

>> 

>> Re Richard Sandiford's feedback on the v2 patch:

>>   https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00560.html

>> > Since the creation of the opt_problem depends on dump_enabled_p, would

>> > it make sense for the dump_printf_loc to happen automatically on

>> > opt_result::failure, rather than have both?

>> 

>> Yes; this v3 patch does that: opt_result::failure_at is passed a format

>> string with variadic args.  If dumping is enabled, it performs the

>> equivalent of dump_printf_loc in a form that will reach dumpfiles

>> (and -fopt-info-internals), stashing the dumped items in the opt_problem.

>> When the opt_problem is emitted at the top-level, the message is re-emitted

>> (but only for -fopt-info, not for dumpfiles, to avoid duplicates that mess

>> up scan-tree-dump-times in DejaGnu tests)

>> 

>> > I guess this is bike-shedding, but personally I'd prefer an explicit

>> > test for success rather than operator bool, so that:

>> >

>> >    opt_result foo = ...;

>> >    bool bar = foo;

>> >

>> > is ill-formed.  The danger otherwise might be that we drop a useful

>> > opt_problem and replace it with something more generic.  E.g. the

>> > opt_result form of:

>> >   if (!ok)

>> >     {

>> >       if (dump_enabled_p ())

>> >         {

>> >           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,

>> >                            "not vectorized: relevant stmt not ");

>> >           dump_printf (MSG_MISSED_OPTIMIZATION, "supported: ");

>> >           dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);

>> >         }

>> >

>> >       return false;

>> >     }

>> >

>> > in vect_analyze_stmt could silently drop the information provided by

>> > the subroutine if we forgot to change "ok" from "bool" to "opt_result".

>> 

>> I didn't make that change in v3: if the function returns an opt_result, then

>> the "return false;" will be a compile-time failure, alerting us to the

>> problem.

>> 

>> I guess this is a matter of style, whether explicit is better than

>> implicit.  Dropping the operator bool would require an explicit approach,

>> with something like:

>> 

>>     // Explicit style:

>>     opt_result res = ...;

>>     if (res.failed_p ())

>>        return res;

>> 

>> and:

>> 

>>     // Explicit style:

>>     // It's often currently called "ok":

>>     opt_result ok = ...;

>>     if (ok.failed_p ())

>>        return ok;

>> 

>> as opposed to:

>> 

>>     // Implicit style:

>>     opt_result res = ...;

>>     if (!res)

>>        return res;

>> 

>> and:

>> 

>>     // Implicit style:

>>     opt_result ok = ...;

>>     if (!ok)

>>        return ok;

>> 

>> I think I went with the implicit style to minimize the lines touched by

>> the patch, but I'm happy with either approach.  [If we're bikeshedding,

>> would renaming those "ok" to "res" be acceptable also?  "ok" reads to

>> me like a "success" value for a status variable, rather than the status

>> variable itself; it's presumably meant to be pronounced with a rising

>> interrogative as it were a question - "ok?" - but that's not visible in

>> the code when reading the usage sites].

>> 

>> Similarly, the pointer wrappers use an implicit style:

>> 

>>   // Implicit style:

>> 

>>   opt_loop_vec_info loop_vinfo

>>     = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

>>   loop->aux = loop_vinfo;

>> 

>>   if (!loop_vinfo)

>>     if (dump_enabled_p ())

>>       if (opt_problem *problem = loop_vinfo.get_problem ())

>> 	{

>> 

>> but maybe an explicit style is more readable:

>> 

>>   // Explicit style:

>> 

>>   opt_loop_vec_info opt_loop_vinfo

>>     = vect_analyze_loop (loop, orig_loop_vinfo, &shared);

>>   loop_vec_info loop_vinfo = loop_vinfo.get_pointer ();

>>   loop->aux = loop_vinfo

>> 

>>   if (opt_loop_vinfo.failed_p ())

>>     if (dump_enabled_p ())

>>       if (opt_problem *problem = loop_vinfo.get_problem ())

>> 	{

>> 

>> 

>> How would you want the code to look?

>> 

>> Richi: do you have an opinion here?

>

> I wouldn't mind about the explicit variant for the bool but

> the explicit for the pointer looks odd.

>

> In general I agree that

>

>  opt_result foo = x;

>  bool bar = x;

>

> is confusing but I also like the brevity that is possible

> with the automatic conversions so I am biased towards that.

>

>> (or is that style in the patch OK as-is?)

>

> For me it is OK as-is.

>

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

>> 

>> OK for trunk?

>

> OK.  My question on 2/3 leaves Richard time to comment.


No objection from me.

Thanks,
Richard
David Malcolm Oct. 4, 2018, 6:21 p.m. | #3
On Thu, 2018-10-04 at 12:52 +0200, Richard Biener wrote:
> On Fri, 28 Sep 2018, David Malcolm wrote:


[...snip...]

> > 

> > OK for trunk?

> 

> OK.  My question on 2/3 leaves Richard time to comment.

> 

> Maybe you can tackle the issue that -fopt-info-inline does

> nothing at the moment and see if opt-problems are a good fit

> there as well (reporting the CIF strings).


Thanks.  I've committed the patch kit.

[CCing Honza]

I'm looking at -fopt-info-inline now (PR ipa/86395).  I might look at
loop optimizations also (there are some fprintfs there also).

Presumably the user-experience for -fopt-info-inline should be
something like:

  <CALLSITE-LOCATION>: missed: can't inline foo into bar
  <REASON-LOCATION>: missed: because of <REASON>

or:

  <CALLSITE-LOCATION>: missed: not inlining foo into bar
  <REASON-LOCATION>: missed: because of <REASON>

and:

  <CALLSITE-LOCATION>: optimized: inlined foo into bar

(gathering all pertinent data into the -fsave-optimization-record
output).

I suspect that the opt_problem class from the above patch kit [1] isn't
a good fit for inlining: the loop vectorizer runs one outer loop at a
time, with a deep callstack, needing to bubble up a single "problem" at
at time.

In contrast, assuming I'm reading the code right, the inliner stores a
status in each cgraph_edge, and walks the cgraph to decide what to do
with all of the edges, with a fairly shallow callstack: I believe
decisions made for one edge can affect other edges etc.

It might still be possible to store and/or emit a bit more per-edge
detail other than the plain enum cgraph_inline_failed_t, if that's
desirable.

Brainstorming:

(a) replace the "inline_failed" enum with a pointer to a class that can
contain more details (perhaps an opt_problem subclass): in a normal
dump-disabled setting, these would point to global constant singletons
(so no allocation is needed); in a dump-enabled setting, these could be
allocated objects containing additional details describing *exactly*
why an edge wasn't inlined (but this adds lots of indirections, ugh)

(b) add a map of cgraph_edge to opt_problem instances; this would only
be populated if dumping is enabled.

(c) if dumping is enabled, generate an opt_problem when an edge
transitions to CIF_FINAL_ERROR, and emit it... somewhere.

I'm not sure if the above are good ideas or not.  (b) and (c) feel more
credible than (a).


Dave

[1] https://gcc.gnu.org/ml/gcc-patches/2018-09/msg01747.html
Richard Biener Oct. 5, 2018, 6:48 a.m. | #4
On Thu, 4 Oct 2018, David Malcolm wrote:

> On Thu, 2018-10-04 at 12:52 +0200, Richard Biener wrote:

> > On Fri, 28 Sep 2018, David Malcolm wrote:

> 

> [...snip...]

> 

> > > 

> > > OK for trunk?

> > 

> > OK.  My question on 2/3 leaves Richard time to comment.

> > 

> > Maybe you can tackle the issue that -fopt-info-inline does

> > nothing at the moment and see if opt-problems are a good fit

> > there as well (reporting the CIF strings).

> 

> Thanks.  I've committed the patch kit.

> 

> [CCing Honza]

> 

> I'm looking at -fopt-info-inline now (PR ipa/86395).  I might look at

> loop optimizations also (there are some fprintfs there also).

> 

> Presumably the user-experience for -fopt-info-inline should be

> something like:

> 

>   <CALLSITE-LOCATION>: missed: can't inline foo into bar

>   <REASON-LOCATION>: missed: because of <REASON>

> 

> or:

> 

>   <CALLSITE-LOCATION>: missed: not inlining foo into bar

>   <REASON-LOCATION>: missed: because of <REASON>

> 

> and:

> 

>   <CALLSITE-LOCATION>: optimized: inlined foo into bar

> 

> (gathering all pertinent data into the -fsave-optimization-record

> output).

> 

> I suspect that the opt_problem class from the above patch kit [1] isn't

> a good fit for inlining: the loop vectorizer runs one outer loop at a

> time, with a deep callstack, needing to bubble up a single "problem" at

> at time.

> 

> In contrast, assuming I'm reading the code right, the inliner stores a

> status in each cgraph_edge, and walks the cgraph to decide what to do

> with all of the edges, with a fairly shallow callstack: I believe

> decisions made for one edge can affect other edges etc.

> 

> It might still be possible to store and/or emit a bit more per-edge

> detail other than the plain enum cgraph_inline_failed_t, if that's

> desirable.

> 

> Brainstorming:

> 

> (a) replace the "inline_failed" enum with a pointer to a class that can

> contain more details (perhaps an opt_problem subclass): in a normal

> dump-disabled setting, these would point to global constant singletons

> (so no allocation is needed); in a dump-enabled setting, these could be

> allocated objects containing additional details describing *exactly*

> why an edge wasn't inlined (but this adds lots of indirections, ugh)

> 

> (b) add a map of cgraph_edge to opt_problem instances; this would only

> be populated if dumping is enabled.

> 

> (c) if dumping is enabled, generate an opt_problem when an edge

> transitions to CIF_FINAL_ERROR, and emit it... somewhere.

> 

> I'm not sure if the above are good ideas or not.  (b) and (c) feel more

> credible than (a).


I guess I'd try a classical top-down approach - start by simply
adding some dump_printf to where inline failed/succeeded and see
if that's enough to do the job.  Back to square one if it isn't ;)

Richard.

> 

> Dave

> 

> [1] https://gcc.gnu.org/ml/gcc-patches/2018-09/msg01747.html

> 

> 


-- 
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 4b7cec8..116ed6e 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1423,6 +1423,7 @@  OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	opt-problem.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index 20b94a7..3a45f23 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -24,6 +24,9 @@  along with GCC; see the file COPYING3.  If not see
 
 #include "dumpfile.h"
 #include "pretty-print.h"
+#include "selftest.h"
+
+namespace selftest { class temp_dump_context; }
 
 /* A class for handling the various dump_* calls.
 
@@ -36,7 +39,8 @@  along with GCC; see the file COPYING3.  If not see
 
 class dump_context
 {
-  friend class temp_dump_context;
+  friend class selftest::temp_dump_context;
+
  public:
   static dump_context &get () { return *s_current; }
 
@@ -45,6 +49,7 @@  class dump_context
   void refresh_dumps_are_enabled ();
 
   void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);
+  void dump_loc_immediate (dump_flags_t dump_kind, const dump_location_t &loc);
 
   void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
 			 gimple *gs, int spc);
@@ -129,8 +134,53 @@  class dump_context
   static dump_context s_default;
 };
 
+/* A subclass of pretty_printer for implementing dump_context::dump_printf_va.
+   In particular, the formatted chunks are captured as optinfo_item instances,
+   thus retaining metadata about the entities being dumped (e.g. source
+   locations), rather than just as plain text.  */
+
+class dump_pretty_printer : public pretty_printer
+{
+public:
+  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);
+
+  void emit_items (optinfo *dest);
+
+private:
+  /* Information on an optinfo_item that was generated during phase 2 of
+     formatting.  */
+  struct stashed_item
+  {
+    stashed_item (const char **buffer_ptr_, optinfo_item *item_)
+      : buffer_ptr (buffer_ptr_), item (item_) {}
+    const char **buffer_ptr;
+    optinfo_item *item;
+  };
+
+  static bool format_decoder_cb (pretty_printer *pp, text_info *text,
+				 const char *spec, int /*precision*/,
+				 bool /*wide*/, bool /*set_locus*/,
+				 bool /*verbose*/, bool */*quoted*/,
+				 const char **buffer_ptr);
+
+  bool decode_format (text_info *text, const char *spec,
+		      const char **buffer_ptr);
+
+  void stash_item (const char **buffer_ptr, optinfo_item *item);
+
+  void emit_any_pending_textual_chunks (optinfo *dest);
+
+  void emit_item (optinfo_item *item, optinfo *dest);
+
+  dump_context *m_context;
+  dump_flags_t m_dump_kind;
+  auto_vec<stashed_item> m_stashed_items;
+};
+
 #if CHECKING_P
 
+namespace selftest {
+
 /* An RAII-style class for use in selftests for temporarily using a different
    dump_context.  */
 
@@ -138,6 +188,7 @@  class temp_dump_context
 {
  public:
   temp_dump_context (bool forcibly_enable_optinfo,
+		     bool forcibly_enable_dumping,
 		     dump_flags_t test_pp_flags);
   ~temp_dump_context ();
 
@@ -151,6 +202,57 @@  class temp_dump_context
   dump_context *m_saved;
 };
 
+/* Implementation detail of ASSERT_DUMPED_TEXT_EQ.  */
+
+extern void verify_dumped_text (const location &loc,
+				temp_dump_context *context,
+				const char *expected_text);
+
+/* Verify that the text dumped so far in CONTEXT equals
+   EXPECTED_TEXT.
+   As a side-effect, the internal buffer is 0-terminated.  */
+
+#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)			\
+  SELFTEST_BEGIN_STMT							\
+    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \
+  SELFTEST_END_STMT
+
+
+/* Verify that ITEM has the expected values.  */
+
+void
+verify_item (const location &loc,
+	     const optinfo_item *item,
+	     enum optinfo_item_kind expected_kind,
+	     location_t expected_location,
+	     const char *expected_text);
+
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
+		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+} // namespace selftest
+
 #endif /* CHECKING_P */
 
 #endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index e15edc7..0b140ff 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -562,6 +562,21 @@  dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
 {
   end_any_optinfo ();
 
+  dump_loc_immediate (dump_kind, loc);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+    }
+}
+
+/* As dump_loc above, but without starting a new optinfo. */
+
+void
+dump_context::dump_loc_immediate (dump_flags_t dump_kind,
+				  const dump_location_t &loc)
+{
   location_t srcloc = loc.get_location_t ();
 
   if (dump_file && apply_dump_filter_p (dump_kind, pflags))
@@ -573,12 +588,6 @@  dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
   /* Support for temp_dump_context in selftests.  */
   if (m_test_pp && apply_dump_filter_p (dump_kind, m_test_pp_flags))
     ::dump_loc (dump_kind, m_test_pp, srcloc);
-
-  if (optinfo_enabled_p ())
-    {
-      optinfo &info = begin_next_optinfo (loc);
-      info.handle_dump_file_kind (dump_kind);
-    }
 }
 
 /* Make an item for the given dump call, equivalent to print_gimple_stmt.  */
@@ -739,49 +748,6 @@  dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
-/* A subclass of pretty_printer for implementing dump_context::dump_printf_va.
-   In particular, the formatted chunks are captured as optinfo_item instances,
-   thus retaining metadata about the entities being dumped (e.g. source
-   locations), rather than just as plain text.  */
-
-class dump_pretty_printer : public pretty_printer
-{
-public:
-  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);
-
-  void emit_items (optinfo *dest);
-
-private:
-  /* Information on an optinfo_item that was generated during phase 2 of
-     formatting.  */
-  struct stashed_item
-  {
-    stashed_item (const char **buffer_ptr_, optinfo_item *item_)
-      : buffer_ptr (buffer_ptr_), item (item_) {}
-    const char **buffer_ptr;
-    optinfo_item *item;
-  };
-
-  static bool format_decoder_cb (pretty_printer *pp, text_info *text,
-				 const char *spec, int /*precision*/,
-				 bool /*wide*/, bool /*set_locus*/,
-				 bool /*verbose*/, bool */*quoted*/,
-				 const char **buffer_ptr);
-
-  bool decode_format (text_info *text, const char *spec,
-		      const char **buffer_ptr);
-
-  void stash_item (const char **buffer_ptr, optinfo_item *item);
-
-  void emit_any_pending_textual_chunks (optinfo *dest);
-
-  void emit_item (optinfo_item *item, optinfo *dest);
-
-  dump_context *m_context;
-  dump_flags_t m_dump_kind;
-  auto_vec<stashed_item> m_stashed_items;
-};
-
 /* dump_pretty_printer's ctor.  */
 
 dump_pretty_printer::dump_pretty_printer (dump_context *context,
@@ -1732,7 +1698,12 @@  dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob)
     return 0;
 
   ptr = option_value;
-  flags = MSG_ALL_PRIORITIES;
+
+  /* Retain "user-facing" and "internals" messages, but filter out
+     those from an opt_problem being re-emitted at the top level
+     (MSG_PRIORITY_REEMITTED), so as to avoid duplicate messages
+     messing up scan-tree-dump-times" in DejaGnu tests.  */
+  flags = MSG_PRIORITY_USER_FACING | MSG_PRIORITY_INTERNALS;
 
   while (*ptr)
     {
@@ -1830,8 +1801,9 @@  opt_info_switch_p_1 (const char *arg, dump_flags_t *flags,
   *filename = NULL;
 
   /* Default to filtering out "internals" messages, and retaining
-     "user-facing" messages.  */
-  *flags = MSG_PRIORITY_USER_FACING;
+     "user-facing" messages, and those from an opt_problem being
+     re-emitted at the top level.  */
+  *flags = MSG_PRIORITY_USER_FACING | MSG_PRIORITY_REEMITTED;
 
   *optgroup_flags = OPTGROUP_NONE;
 
@@ -1981,19 +1953,26 @@  enable_rtl_dump_file (void)
 
 #if CHECKING_P
 
+namespace selftest {
+
 /* temp_dump_context's ctor.  Temporarily override the dump_context
    (to forcibly enable optinfo-generation).  */
 
 temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo,
+				      bool forcibly_enable_dumping,
 				      dump_flags_t test_pp_flags)
-
 : m_context (),
   m_saved (&dump_context ().get ())
 {
   dump_context::s_current = &m_context;
   m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
-  m_context.m_test_pp = &m_pp;
-  m_context.m_test_pp_flags = test_pp_flags;
+  /* Conditionally enable the test dump, so that we can verify both the
+     dump_enabled_p and the !dump_enabled_p cases in selftests.  */
+  if (forcibly_enable_dumping)
+    {
+      m_context.m_test_pp = &m_pp;
+      m_context.m_test_pp_flags = test_pp_flags;
+    }
 
   dump_context::get ().refresh_dumps_are_enabled ();
 }
@@ -2015,8 +1994,6 @@  temp_dump_context::get_dumped_text ()
   return pp_formatted_text (&m_pp);
 }
 
-namespace selftest {
-
 /* Verify that the dump_location_t constructors capture the source location
    at which they were called (provided that the build compiler is sufficiently
    recent).  */
@@ -2055,7 +2032,7 @@  test_impl_location ()
    EXPECTED_TEXT, using LOC for the location of any failure.
    As a side-effect, the internal buffer is 0-terminated.  */
 
-static void
+void
 verify_dumped_text (const location &loc,
 		    temp_dump_context *context,
 		    const char *expected_text)
@@ -2065,18 +2042,9 @@  verify_dumped_text (const location &loc,
 		   expected_text);
 }
 
-/* Verify that the text dumped so far in CONTEXT equals
-   EXPECTED_TEXT.
-   As a side-effect, the internal buffer is 0-terminated.  */
-
-#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)			\
-  SELFTEST_BEGIN_STMT							\
-    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \
-  SELFTEST_END_STMT
-
 /* Verify that ITEM has the expected values.  */
 
-static void
+void
 verify_item (const location &loc,
 	     const optinfo_item *item,
 	     enum optinfo_item_kind expected_kind,
@@ -2088,30 +2056,6 @@  verify_item (const location &loc,
   ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
 }
 
-/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
-
-#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
-  SELFTEST_BEGIN_STMT						    \
-    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
-		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \
-  SELFTEST_END_STMT
-
-/* Verify that ITEM is a tree item, with the expected values.  */
-
-#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
-  SELFTEST_BEGIN_STMT						    \
-    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
-		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
-  SELFTEST_END_STMT
-
-/* Verify that ITEM is a gimple item, with the expected values.  */
-
-#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
-  SELFTEST_BEGIN_STMT						    \
-    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
-		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
-  SELFTEST_END_STMT
-
 /* Verify that calls to the dump_* API are captured and consolidated into
    optimization records. */
 
@@ -2144,7 +2088,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Test of dump_printf.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
 
@@ -2161,7 +2105,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Test of dump_printf with %T.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
 
@@ -2179,7 +2123,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Test of dump_printf with %E.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf (MSG_NOTE, "gimple: %E", stmt);
 
@@ -2197,7 +2141,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Test of dump_printf with %G.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf (MSG_NOTE, "gimple: %G", stmt);
 
@@ -2220,7 +2164,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 	 - multiple dump-specific format codes: some consecutive, others
 	 separated by text, trailing text after the final one.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
 			 " %i consecutive %E%E after\n",
@@ -2248,7 +2192,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Tree, via dump_generic_expr.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
 	dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
@@ -2268,7 +2212,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* Tree, via dump_generic_expr_loc.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
 
@@ -2288,7 +2232,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
       {
 	/* dump_gimple_stmt_loc.  */
 	{
-	  temp_dump_context tmp (with_optinfo,
+	  temp_dump_context tmp (with_optinfo, true,
 				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	  dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
 
@@ -2304,7 +2248,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
 	/* dump_gimple_stmt.  */
 	{
-	  temp_dump_context tmp (with_optinfo,
+	  temp_dump_context tmp (with_optinfo, true,
 				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
 
@@ -2320,7 +2264,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
 	/* dump_gimple_expr_loc.  */
 	{
-	  temp_dump_context tmp (with_optinfo,
+	  temp_dump_context tmp (with_optinfo, true,
 				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	  dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
 
@@ -2336,7 +2280,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
 	/* dump_gimple_expr.  */
 	{
-	  temp_dump_context tmp (with_optinfo,
+	  temp_dump_context tmp (with_optinfo, true,
 				 MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	  dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
 
@@ -2353,7 +2297,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
       /* poly_int.  */
       {
-	temp_dump_context tmp (with_optinfo,
+	temp_dump_context tmp (with_optinfo, true,
 			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
 	dump_dec (MSG_NOTE, poly_int64 (42));
 
@@ -2378,7 +2322,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 	  if (j / 2)
 	    dump_filter |= MSG_PRIORITY_INTERNALS;
 
-	  temp_dump_context tmp (with_optinfo, dump_filter);
+	  temp_dump_context tmp (with_optinfo, true, dump_filter);
 	  /* Emit various messages, mostly with implicit priority.  */
 	  dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
 	  dump_printf_loc (MSG_NOTE | MSG_PRIORITY_INTERNALS, stmt,
@@ -2460,7 +2404,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
   {
     /* MSG_OPTIMIZED_LOCATIONS.  */
     {
-      temp_dump_context tmp (true, MSG_ALL_KINDS);
+      temp_dump_context tmp (true, true, MSG_ALL_KINDS);
       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
       ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
 		 OPTINFO_KIND_SUCCESS);
@@ -2468,7 +2412,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
     /* MSG_MISSED_OPTIMIZATION.  */
     {
-      temp_dump_context tmp (true, MSG_ALL_KINDS);
+      temp_dump_context tmp (true, true, MSG_ALL_KINDS);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
       ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
 		 OPTINFO_KIND_FAILURE);
@@ -2477,7 +2421,7 @@  test_capture_of_dump_calls (const line_table_case &case_)
 
   /* Verify that MSG_* affect AUTO_DUMP_SCOPE and the dump calls.  */
   {
-    temp_dump_context tmp (false,
+    temp_dump_context tmp (false, true,
 			   MSG_OPTIMIZED_LOCATIONS | MSG_ALL_PRIORITIES);
     dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
     {
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 5933905..c82157d 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -179,15 +179,22 @@  enum dump_flag
   /* Implicitly supplied for messages within nested dump scopes.  */
   MSG_PRIORITY_INTERNALS = (1 << 26),
 
+  /* Supplied when an opt_problem generated in a nested scope is re-emitted
+     at the top-level.   We want to default to showing these in -fopt-info
+     output, but to *not* show them in dump files, as the message would be
+     shown twice, messing up "scan-tree-dump-times" in DejaGnu tests.  */
+  MSG_PRIORITY_REEMITTED = (1 << 27),
+
   /* Mask for selecting MSG_PRIORITY_* flags.  */
   MSG_ALL_PRIORITIES = (MSG_PRIORITY_USER_FACING
-			| MSG_PRIORITY_INTERNALS),
+			| MSG_PRIORITY_INTERNALS
+			| MSG_PRIORITY_REEMITTED),
 
   /* Dumping for -fcompare-debug.  */
-  TDF_COMPARE_DEBUG = (1 << 27),
+  TDF_COMPARE_DEBUG = (1 << 28),
 
   /* All values.  */
-  TDF_ALL_VALUES = (1 << 28) - 1
+  TDF_ALL_VALUES = (1 << 29) - 1
 };
 
 /* Dump flags type.  */
diff --git a/gcc/opt-problem.cc b/gcc/opt-problem.cc
new file mode 100644
index 0000000..dad3a8c
--- /dev/null
+++ b/gcc/opt-problem.cc
@@ -0,0 +1,335 @@ 
+/* Rich optional information on why an optimization wasn't possible.
+   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 "pretty-print.h"
+#include "opt-problem.h"
+#include "dump-context.h"
+#include "tree-pass.h"
+#include "selftest.h"
+
+/* opt_problem's ctor.
+
+   Use FMT and AP to emit a message to the "immediate" dump destinations
+   as if via:
+     dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, ...)
+
+   The optinfo_item instances are not emitted yet.  Instead, they
+   are retained internally so that the message can be replayed and
+   emitted when this problem is handled, higher up the call stack.  */
+
+opt_problem::opt_problem (const dump_location_t &loc,
+			  const char *fmt, va_list *ap)
+: m_optinfo (loc, OPTINFO_KIND_FAILURE, current_pass)
+{
+  /* We shouldn't be bothering to construct these objects if
+     dumping isn't enabled.  */
+  gcc_assert (dump_enabled_p ());
+
+  /* Update the singleton.  */
+  delete s_the_problem;
+  s_the_problem = this;
+
+  /* Print the location to the "immediate" dump destinations.  */
+  dump_context &dc = dump_context::get ();
+  dc.dump_loc (MSG_MISSED_OPTIMIZATION, loc);
+
+  /* Print the formatted string to this opt_problem's optinfo, dumping
+     the items to the "immediate" dump destinations, and storing items
+     for later retrieval.  */
+  {
+    dump_pretty_printer pp (&dump_context::get (), MSG_MISSED_OPTIMIZATION);
+
+    text_info text;
+    text.err_no = errno;
+    text.args_ptr = ap;
+    text.format_spec = fmt; /* No i18n is performed.  */
+
+    /* Phases 1 and 2, using pp_format.  */
+    pp_format (&pp, &text);
+
+    /* Phase 3: dump the items to the "immediate" dump destinations,
+       and storing them into m_optinfo for later retrieval.  */
+    pp.emit_items (&m_optinfo);
+  }
+}
+
+/* Emit this problem and delete it, clearing the current opt_problem.  */
+
+void
+opt_problem::emit_and_clear ()
+{
+  gcc_assert (this == s_the_problem);
+
+  m_optinfo.emit_for_opt_problem ();
+
+  delete this;
+  s_the_problem = NULL;
+}
+
+/* The singleton opt_problem *.  */
+
+opt_problem *opt_problem::s_the_problem;
+
+#if CHECKING_P
+
+namespace selftest {
+
+static opt_result
+function_that_succeeds ()
+{
+  return opt_result::success ();
+}
+
+/* Verify that opt_result::success works.  */
+
+static void
+test_opt_result_success ()
+{
+  /* Run all tests twice, with and then without dumping enabled.  */
+  for (int i = 0 ; i < 2; i++)
+    {
+      bool with_dumping = (i == 0);
+
+      temp_dump_context tmp (with_dumping, with_dumping,
+			     MSG_ALL_KINDS | MSG_ALL_PRIORITIES);
+
+      if (with_dumping)
+	gcc_assert (dump_enabled_p ());
+      else
+	gcc_assert (!dump_enabled_p ());
+
+      opt_result res = function_that_succeeds ();
+
+      /* Verify that "success" can be used as a "true" boolean.  */
+      ASSERT_TRUE (res);
+
+      /* Verify the underlying opt_wrapper<bool>.  */
+      ASSERT_TRUE (res.get_result ());
+      ASSERT_EQ (res.get_problem (), NULL);
+
+      /* Nothing should have been dumped.  */
+      ASSERT_DUMPED_TEXT_EQ (tmp, "");
+      optinfo *info = tmp.get_pending_optinfo ();
+      ASSERT_EQ (info, NULL);
+    }
+}
+
+/* Example of a function that fails, with a non-trivial
+   pre-canned error message.  */
+
+static opt_result
+function_that_fails (const greturn *stmt)
+{
+  gcc_assert (stmt);
+  gcc_assert (gimple_return_retval (stmt));
+
+  AUTO_DUMP_SCOPE ("function_that_fails", stmt);
+
+  return opt_result::failure_at (stmt,
+				 "can't handle return type: %T for stmt: %G",
+				 TREE_TYPE (gimple_return_retval (stmt)),
+				 static_cast <const gimple *> (stmt));
+}
+
+/* Example of a function that indirectly fails.  */
+
+static opt_result
+function_that_indirectly_fails (const greturn *stmt)
+{
+  AUTO_DUMP_SCOPE ("function_that_indirectly_fails", stmt);
+
+  opt_result res = function_that_fails (stmt);
+  if (!res)
+    return res;
+  return opt_result::success ();
+}
+
+/* Verify that opt_result::failure_at works.
+   Simulate a failure handling a stmt at one location whilst considering
+   an optimization that's notionally at another location (as a microcosm
+   of e.g. a problematic statement within a loop that prevents loop
+   vectorization).  */
+
+static void
+test_opt_result_failure_at (const line_table_case &case_)
+{
+  /* Generate a location_t for testing.  */
+  line_table_test ltt (case_);
+  const line_map_ordinary *ord_map
+    = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
+					   "test.c", 0));
+  linemap_line_start (line_table, 5, 100);
+
+  /* A test location: "test.c:5:10".  */
+  const location_t line_5 = linemap_position_for_column (line_table, 10);
+
+  /* Another test location: "test.c:6:12".  */
+  const location_t line_6
+    = linemap_position_for_line_and_column (line_table, ord_map, 6, 12);
+
+  if (line_6 > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  /* Generate statements using "line_5" and "line_6" for testing.  */
+  greturn *stmt_at_5 = gimple_build_return (integer_one_node);
+  gimple_set_location (stmt_at_5, line_5);
+
+  greturn *stmt_at_6 = gimple_build_return (integer_zero_node);
+  gimple_set_location (stmt_at_6, line_6);
+
+  /* Run with and then without dumping enabled.  */
+  for (int i = 0; i < 2; i++)
+    {
+      bool with_dumping = (i == 0);
+
+      /* Run with all 4 combinations of
+	 with and without MSG_PRIORITY_INTERNALS and
+	 with and without MSG_PRIORITY_REEMITTED.  */
+      for (int j = 0; j < 4; j++)
+	{
+	  dump_flags_t filter = MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING;
+	  if (j / 2)
+	    filter |= MSG_PRIORITY_INTERNALS;
+	  if (j % 2)
+	    filter |= MSG_PRIORITY_REEMITTED;
+
+	  temp_dump_context tmp (with_dumping, with_dumping, filter);
+
+	  if (with_dumping)
+	    gcc_assert (dump_enabled_p ());
+	  else
+	    gcc_assert (!dump_enabled_p ());
+
+	  /* Simulate attempting to optimize "stmt_at_6".  */
+	  opt_result res = function_that_indirectly_fails (stmt_at_6);
+
+	  /* Verify that "failure" can be used as a "false" boolean.  */
+	  ASSERT_FALSE (res);
+
+	  /* Verify the underlying opt_wrapper<bool>.  */
+	  ASSERT_FALSE (res.get_result ());
+	  opt_problem *problem = res.get_problem ();
+
+	  if (with_dumping)
+	    {
+	      ASSERT_NE (problem, NULL);
+	      ASSERT_EQ (problem->get_dump_location ().get_location_t (),
+			 line_6);
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+	      /* Verify that the problem captures the implementation location
+		 it was emitted from.  */
+	      const dump_impl_location_t &impl_location
+		= problem->get_dump_location ().get_impl_location ();
+	      ASSERT_STR_CONTAINS (impl_location.m_function,
+				   "function_that_fails");
+#endif
+
+	      /* Verify that the underlying dump items are retained in the
+		 opt_problem.  */
+	      const optinfo &info = problem->get_optinfo ();
+	      ASSERT_EQ (info.get_dump_location ().get_location_t (), line_6);
+	      ASSERT_EQ (info.num_items (), 4);
+	      ASSERT_IS_TEXT (info.get_item (0), "can't handle return type: ");
+	      ASSERT_IS_TREE (info.get_item (1), UNKNOWN_LOCATION, "int");
+	      ASSERT_IS_TEXT (info.get_item (2), " for stmt: ");
+	      ASSERT_IS_GIMPLE (info.get_item (3), line_6, "return 0;\n");
+
+	      /* ...but not in the dump_context's pending_optinfo.  */
+	      ASSERT_EQ (tmp.get_pending_optinfo (), NULL);
+
+	      /* Simulate emitting a high-level summary message, followed
+		 by the problem.  */
+	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt_at_5,
+			       "can't optimize loop\n");
+	      problem->emit_and_clear ();
+	      ASSERT_EQ (res.get_problem (), NULL);
+
+	      /* Verify that the error message was dumped (when the failure
+		 occurred).  We can't use a switch here as not all of the
+		 values are const expressions (using C++98).  */
+	      dump_flags_t effective_filter
+		= filter & (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED);
+	      if (effective_filter
+		  == (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED))
+		/* The -fopt-info-internals case.  */
+		ASSERT_DUMPED_TEXT_EQ
+		  (tmp,
+		   "test.c:6:12: note:  === function_that_indirectly_fails"
+		   " ===\n"
+		   "test.c:6:12: note:   === function_that_fails ===\n"
+		   "test.c:6:12: missed:   can't handle return type: int"
+		   " for stmt: return 0;\n"
+		   "test.c:5:10: missed: can't optimize loop\n"
+		   "test.c:6:12: missed: can't handle return type: int"
+		   " for stmt: return 0;\n");
+	      else if (effective_filter == MSG_PRIORITY_INTERNALS)
+		/* The default for dump files.  */
+		ASSERT_DUMPED_TEXT_EQ
+		  (tmp,
+		   "test.c:6:12: note:  === function_that_indirectly_fails"
+		   " ===\n"
+		   "test.c:6:12: note:   === function_that_fails ===\n"
+		   "test.c:6:12: missed:   can't handle return type: int"
+		     " for stmt: return 0;\n"
+		   "test.c:5:10: missed: can't optimize loop\n");
+	      else if (effective_filter == MSG_PRIORITY_REEMITTED)
+		/* The default when -fopt-info is enabled.  */
+		ASSERT_DUMPED_TEXT_EQ
+		  (tmp,
+		   "test.c:5:10: missed: can't optimize loop\n"
+		   "test.c:6:12: missed: can't handle return type: int"
+		   " for stmt: return 0;\n");
+	      else
+		{
+		  gcc_assert (effective_filter == 0);
+		  ASSERT_DUMPED_TEXT_EQ
+		    (tmp,
+		     "test.c:5:10: missed: can't optimize loop\n");
+		}
+	    }
+	  else
+	    {
+	      /* If dumping was disabled, then no problem should have been
+		 created, and nothing should have been dumped.  */
+	      ASSERT_EQ (problem, NULL);
+	      ASSERT_DUMPED_TEXT_EQ (tmp, "");
+	    }
+	}
+    }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+opt_problem_cc_tests ()
+{
+  test_opt_result_success ();
+  for_each_line_table_case (test_opt_result_failure_at);
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/opt-problem.h b/gcc/opt-problem.h
new file mode 100644
index 0000000..68d7e4a
--- /dev/null
+++ b/gcc/opt-problem.h
@@ -0,0 +1,289 @@ 
+/* Rich information on why an optimization wasn't possible.
+   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_OPT_PROBLEM_H
+#define GCC_OPT_PROBLEM_H
+
+#include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG.  */
+#include "optinfo.h" /* for optinfo.  */
+
+/* This header declares a family of wrapper classes for tracking a
+   success/failure value, while optionally supporting propagating an
+   opt_problem * describing any failure back up the call stack.
+
+   For instance, at the deepest point of the callstack where the failure
+   happens, rather than:
+
+     if (!check_something ())
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "foo is unsupported.\n");
+         return false;
+       }
+     // [...more checks...]
+
+     // All checks passed:
+     return true;
+
+   we can capture the cause of the failure via:
+
+     if (!check_something ())
+       return opt_result::failure_at (stmt, "foo is unsupported");
+     // [...more checks...]
+
+     // All checks passed:
+     return opt_result::success ();
+
+   which effectively returns true or false, whilst recording any problem.
+
+   opt_result::success and opt_result::failure return opt_result values
+   which "looks like" true/false respectively, via operator bool().
+   If dump_enabled_p, then opt_result::failure also creates an opt_problem *,
+   capturing the pertinent data (here, "foo is unsupported " and "stmt").
+   If dumps are disabled, then opt_problem instances aren't
+   created, and it's equivalent to just returning a bool.
+
+   The opt_problem can be propagated via opt_result values back up
+   the call stack to where it makes most sense to the user.
+   For instance, rather than:
+
+     bool ok = try_something_that_might_fail ();
+     if (!ok)
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "some message.\n");
+         return false;
+       }
+
+   we can replace the bool with an opt_result, so if dump_enabled_p, we
+   assume that if try_something_that_might_fail, an opt_problem * will be
+   created, and we can propagate it up the call chain:
+
+     opt_result ok = try_something_that_might_fail ();
+     if (!ok)
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "some message.\n");
+         return ok; // propagating the opt_result
+       }
+
+   opt_result is an opt_wrapper<bool>, where opt_wrapper<T> is a base
+   class for wrapping a T, optionally propagating an opt_problem in
+   case of failure_at (when dumps are enabled).  Similarly,
+   opt_pointer_wrapper<T> can be used to wrap pointer types (where non-NULL
+   signifies success, NULL signifies failure).
+
+   In all cases, opt_wrapper<T> acts as if the opt_problem were one of its
+   fields, but the opt_problem is actually stored in a global, so that when
+   compiled, an opt_wrapper<T> is effectively just a T, so that we're
+   still just passing e.g. a bool around; the opt_wrapper<T> classes
+   simply provide type-checking and an API to ensure that we provide
+   error-messages deep in the callstack at the places where problems
+   occur, and that we propagate them.  This also avoids having
+   to manage the ownership of the opt_problem instances.
+
+   Using opt_result and opt_wrapper<T> documents the intent of the code
+   for the places where we represent success values, and allows the C++ type
+   system to track where the deepest points in the callstack are where we
+   need to emit the failure messages from.  */
+
+/* A bundle of information about why an optimization failed (e.g.
+   vectorization), and the location in both the user's code and
+   in GCC itself where the problem occurred.
+
+   Instances are created by static member functions in opt_wrapper
+   subclasses, such as opt_result::failure.
+
+   Instances are only created when dump_enabled_p ().  */
+
+class opt_problem
+{
+ public:
+  static opt_problem *get_singleton () { return s_the_problem; }
+
+  opt_problem (const dump_location_t &loc,
+	       const char *fmt, va_list *ap)
+    ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
+
+  const dump_location_t &
+  get_dump_location () const { return m_optinfo.get_dump_location (); }
+
+  const optinfo & get_optinfo () const { return m_optinfo; }
+
+  void emit_and_clear ();
+
+ private:
+  optinfo m_optinfo;
+
+  static opt_problem *s_the_problem;
+};
+
+/* A base class for wrapper classes that track a success/failure value, while
+   optionally supporting propagating an opt_problem * describing any
+   failure back up the call stack.  */
+
+template <typename T>
+class opt_wrapper
+{
+ public:
+  typedef T wrapped_t;
+
+  /* Be accessible as the wrapped type.  */
+  operator wrapped_t () const { return m_result; }
+
+  /* No public ctor.  */
+
+  wrapped_t get_result () const { return m_result; }
+  opt_problem *get_problem () const { return opt_problem::get_singleton (); }
+
+ protected:
+  opt_wrapper (wrapped_t result, opt_problem */*problem*/)
+  : m_result (result)
+  {
+    /* "problem" is ignored: although it looks like a field, we
+       actually just use the opt_problem singleton, so that
+       opt_wrapper<T> in memory is just a T.  */
+  }
+
+ private:
+  wrapped_t m_result;
+};
+
+/* Subclass of opt_wrapper<T> for bool, where
+   - true signifies "success", and
+   - false signifies "failure"
+   whilst effectively propagating an opt_problem * describing any failure
+   back up the call stack.  */
+
+class opt_result : public opt_wrapper <bool>
+{
+ public:
+  /* Generate a "success" value: a wrapper around "true".  */
+
+  static opt_result success () { return opt_result (true, NULL); }
+
+  /* Generate a "failure" value: a wrapper around "false", and,
+     if dump_enabled_p, an opt_problem.  */
+
+  static opt_result failure_at (const dump_location_t &loc,
+				const char *fmt, ...)
+	  ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
+  {
+    opt_problem *problem = NULL;
+    if (dump_enabled_p ())
+      {
+	va_list ap;
+	va_start (ap, fmt);
+	problem = new opt_problem (loc, fmt, &ap);
+	va_end (ap);
+      }
+    return opt_result (false, problem);
+  }
+
+  /* Given a failure wrapper of some other kind, make an opt_result failure
+     object, for propagating the opt_problem up the call stack.  */
+
+  template <typename S>
+  static opt_result
+  propagate_failure (opt_wrapper <S> other)
+  {
+    return opt_result (false, other.get_problem ());
+  }
+
+ private:
+  /* Private ctor.  Instances should be created by the success and failure
+     static member functions.  */
+  opt_result (wrapped_t result, opt_problem *problem)
+  : opt_wrapper (result, problem)
+  {}
+};
+
+/* Subclass of opt_wrapper<T> where T is a pointer type, for tracking
+   success/failure, where:
+   - a non-NULL value signifies "success", and
+   - a NULL value signifies "failure",
+   whilst effectively propagating an opt_problem * describing any failure
+   back up the call stack.  */
+
+template <typename PtrType_t>
+class opt_pointer_wrapper : public opt_wrapper <PtrType_t>
+{
+ public:
+  typedef PtrType_t wrapped_pointer_t;
+
+  /* Given a non-NULL pointer, make a success object wrapping it.  */
+
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  success (wrapped_pointer_t ptr)
+  {
+    return opt_pointer_wrapper <wrapped_pointer_t> (ptr, NULL);
+  }
+
+  /* Make a NULL pointer failure object, with the given message
+     (if dump_enabled_p).  */
+
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  failure_at (const dump_location_t &loc,
+	      const char *fmt, ...)
+    ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
+  {
+    opt_problem *problem = NULL;
+    if (dump_enabled_p ())
+      {
+	va_list ap;
+	va_start (ap, fmt);
+	problem = new opt_problem (loc, fmt, &ap);
+	va_end (ap);
+      }
+    return opt_pointer_wrapper <wrapped_pointer_t> (NULL, problem);
+  }
+
+  /* Given a failure wrapper of some other kind, make a NULL pointer
+     failure object, propagating the problem.  */
+
+  template <typename S>
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  propagate_failure (opt_wrapper <S> other)
+  {
+    return opt_pointer_wrapper <wrapped_pointer_t> (NULL,
+						    other.get_problem ());
+  }
+
+  /* Support accessing the underlying pointer via ->.  */
+
+  wrapped_pointer_t operator-> () const { return this->get_result (); }
+
+ private:
+  /* Private ctor.  Instances should be built using the static member
+     functions "success" and "failure".  */
+  opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)
+  : opt_wrapper<PtrType_t> (result, problem)
+  {}
+};
+
+/* A typedef for wrapping "tree" so that NULL_TREE can carry an
+   opt_problem describing the failure (if dump_enabled_p).  */
+
+typedef opt_pointer_wrapper<tree> opt_tree;
+
+#endif /* #ifndef GCC_OPT_PROBLEM_H */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index efdbdb3..31029ad 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -531,7 +531,7 @@  namespace selftest {
 static void
 test_building_json_from_dump_calls ()
 {
-  temp_dump_context tmp (true, MSG_NOTE);
+  temp_dump_context tmp (true, true, MSG_NOTE);
   dump_location_t loc;
   dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
   dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index b858c3c..de781a5 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -89,11 +89,51 @@  optinfo::add_item (optinfo_item *item)
   m_items.safe_push (item);
 }
 
+/* Get MSG_* flags corresponding to KIND.  */
+
+static dump_flags_t
+optinfo_kind_to_dump_flag (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return MSG_OPTIMIZED_LOCATIONS;
+    case OPTINFO_KIND_FAILURE:
+      return MSG_MISSED_OPTIMIZATION;
+    case OPTINFO_KIND_NOTE:
+    case OPTINFO_KIND_SCOPE:
+      return MSG_NOTE;
+    }
+}
+
+/* Re-emit this optinfo, both to the "non-immediate" destinations,
+   *and* to the "immediate" destinations.  */
+
+void
+optinfo::emit_for_opt_problem () const
+{
+  dump_flags_t dump_kind = optinfo_kind_to_dump_flag (get_kind ());
+  dump_kind |= MSG_PRIORITY_REEMITTED;
+
+  /* Re-emit to "immediate" destinations, without creating a new optinfo.  */
+  dump_context::get ().dump_loc_immediate (dump_kind, get_dump_location ());
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    dump_context::get ().emit_item (item, dump_kind);
+
+  /* Re-emit to "non-immediate" destinations.  */
+  emit ();
+}
+
 /* Emit the optinfo to all of the "non-immediate" destinations
-   (emission to "immediate" destinations is done by emit_item).  */
+   (emission to "immediate" destinations is done by
+   dump_context::emit_item).  */
 
 void
-optinfo::emit ()
+optinfo::emit () const
 {
   /* -fsave-optimization-record.  */
   optimization_records_maybe_record_optinfo (this);
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index 8ac961c..99bd22c 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -108,6 +108,9 @@  class optinfo
   {}
   ~optinfo ();
 
+  const dump_location_t &
+  get_dump_location () const { return m_loc; }
+
   const dump_user_location_t &
   get_user_location () const { return m_loc.get_user_location (); }
 
@@ -124,8 +127,10 @@  class optinfo
 
   void add_item (optinfo_item *item);
 
+  void emit_for_opt_problem () const;
+
  private:
-  void emit ();
+  void emit () const;
 
   /* Pre-canned ways of manipulating the optinfo, for use by friend class
      dump_context.  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 5adb033..562ada7 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -74,6 +74,7 @@  selftest::run_tests ()
   opt_proposer_c_tests ();
   json_cc_tests ();
   optinfo_emit_json_cc_tests ();
+  opt_problem_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index ede7732..8da7c4a 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -229,6 +229,7 @@  extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void json_cc_tests ();
+extern void opt_problem_cc_tests ();
 extern void optinfo_emit_json_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
diff --git a/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c b/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c
new file mode 100644
index 0000000..94c55a9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/nodump-vect-opt-info-2.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target vect_int } } */
+/* { dg-additional-options "-fopt-info-vec-all -O3" } */
+
+extern void accumulate (int x, int *a);
+
+int test_missing_function_defn (int *arr, int n) /* { dg-message "vectorized 0 loops in function" } */
+{
+  int sum = 0;
+  for (int i = 0; i < n; ++i) /* { dg-missed "couldn't vectorize loop" } */
+    accumulate (arr[i], &sum); /* { dg-missed "statement clobbers memory: accumulate \\(.*\\);" } */
+  return sum;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c b/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c
index 1e5fc27..750193e 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-alias-check-4.c
@@ -1,6 +1,6 @@ 
 /* { dg-do compile } */
 /* { dg-require-effective-target vect_int } */
-/* { dg-additional-options "--param vect-max-version-for-alias-checks=0" } */
+/* { dg-additional-options "--param vect-max-version-for-alias-checks=0 -fopt-info-vec-all" } */
 
 #define N 16
 
@@ -12,24 +12,26 @@  union u { struct s2 f; struct s3 g; };
 /* We allow a and b to overlap arbitrarily.  */
 
 void
-f1 (int a[][N], int b[][N])
+f1 (int a[][N], int b[][N]) /* { dg-message "vectorized 0 loops in function" } */
 {
-  for (int i = 0; i < N; ++i)
+  for (int i = 0; i < N; ++i) /* { dg-missed "couldn't vectorize loop" } */
     a[0][i] += b[0][i];
+  /* { dg-message "will not create alias checks, as --param vect-max-version-for-alias-checks == 0" "" { target *-*-* } .-2 } */
 }
 
 void
-f2 (union u *a, union u *b)
+f2 (union u *a, union u *b) /* { dg-message "vectorized 0 loops in function" } */
 {
-  for (int i = 0; i < N; ++i)
+  for (int i = 0; i < N; ++i) /* { dg-missed "couldn't vectorize loop" } */
     a->f.b.a[i] += b->g.e.a[i];
+  /* { dg-message "will not create alias checks, as --param vect-max-version-for-alias-checks == 0" "" { target *-*-* } .-2 } */
 }
 
 void
-f3 (struct s1 *a, struct s1 *b)
+f3 (struct s1 *a, struct s1 *b) /* { dg-message "vectorized 0 loops in function" } */
 {
-  for (int i = 0; i < N - 1; ++i)
-    a->a[i + 1] += b->a[i];
+  for (int i = 0; i < N - 1; ++i) /* { dg-missed "couldn't vectorize loop" } */
+    a->a[i + 1] += b->a[i]; /* { dg-missed "possible dependence between data-refs" } */
 }
 
 /* { dg-final { scan-tree-dump-not "LOOP VECTORIZED" "vect" } } */
diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c
index bf30a61..69c5f7b 100644
--- a/gcc/tree-data-ref.c
+++ b/gcc/tree-data-ref.c
@@ -807,7 +807,8 @@  canonicalize_base_object_address (tree addr)
   return build_fold_addr_expr (TREE_OPERAND (addr, 0));
 }
 
-/* Analyze the behavior of memory reference REF.  There are two modes:
+/* Analyze the behavior of memory reference REF within STMT.
+   There are two modes:
 
    - BB analysis.  In this case we simply split the address into base,
      init and offset components, without reference to any containing loop.
@@ -827,9 +828,9 @@  canonicalize_base_object_address (tree addr)
    Return true if the analysis succeeded and store the results in DRB if so.
    BB analysis can only fail for bitfield or reversed-storage accesses.  */
 
-bool
+opt_result
 dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,
-		      struct loop *loop)
+		      struct loop *loop, const gimple *stmt)
 {
   poly_int64 pbitsize, pbitpos;
   tree base, poffset;
@@ -848,18 +849,12 @@  dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,
 
   poly_int64 pbytepos;
   if (!multiple_p (pbitpos, BITS_PER_UNIT, &pbytepos))
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	fprintf (dump_file, "failed: bit offset alignment.\n");
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "failed: bit offset alignment.\n");
 
   if (preversep)
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	fprintf (dump_file, "failed: reverse storage order.\n");
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "failed: reverse storage order.\n");
 
   /* Calculate the alignment and misalignment for the inner reference.  */
   unsigned int HOST_WIDE_INT bit_base_misalignment;
@@ -895,11 +890,8 @@  dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,
   if (in_loop)
     {
       if (!simple_iv (loop, loop, base, &base_iv, true))
-        {
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, "failed: evolution of base is not affine.\n");
-	  return false;
-        }
+	return opt_result::failure_at
+	  (stmt, "failed: evolution of base is not affine.\n");
     }
   else
     {
@@ -921,11 +913,8 @@  dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,
           offset_iv.step = ssize_int (0);
         }
       else if (!simple_iv (loop, loop, poffset, &offset_iv, true))
-        {
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, "failed: evolution of offset is not affine.\n");
-	  return false;
-        }
+	return opt_result::failure_at
+	  (stmt, "failed: evolution of offset is not affine.\n");
     }
 
   init = ssize_int (pbytepos);
@@ -981,7 +970,7 @@  dr_analyze_innermost (innermost_loop_behavior *drb, tree ref,
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file, "success.\n");
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Return true if OP is a valid component reference for a DR access
@@ -1205,7 +1194,7 @@  create_data_ref (edge nest, loop_p loop, tree memref, gimple *stmt,
   DR_IS_CONDITIONAL_IN_STMT (dr) = is_conditional_in_stmt;
 
   dr_analyze_innermost (&DR_INNERMOST (dr), memref,
-			nest != NULL ? loop : NULL);
+			nest != NULL ? loop : NULL, stmt);
   dr_analyze_indices (dr, nest, loop);
   dr_analyze_alias (dr);
 
@@ -1318,7 +1307,7 @@  data_ref_compare_tree (tree t1, tree t2)
 /* Return TRUE it's possible to resolve data dependence DDR by runtime alias
    check.  */
 
-bool
+opt_result
 runtime_alias_check_p (ddr_p ddr, struct loop *loop, bool speed_p)
 {
   if (dump_enabled_p ())
@@ -1327,25 +1316,18 @@  runtime_alias_check_p (ddr_p ddr, struct loop *loop, bool speed_p)
 		 DR_REF (DDR_A (ddr)), DR_REF (DDR_B (ddr)));
 
   if (!speed_p)
-    {
-      if (dump_enabled_p ())
-	dump_printf (MSG_MISSED_OPTIMIZATION,
-		     "runtime alias check not supported when optimizing "
-		     "for size.\n");
-      return false;
-    }
+    return opt_result::failure_at (DR_STMT (DDR_A (ddr)),
+				   "runtime alias check not supported when"
+				   " optimizing for size.\n");
 
   /* FORNOW: We don't support versioning with outer-loop in either
      vectorization or loop distribution.  */
   if (loop != NULL && loop->inner != NULL)
-    {
-      if (dump_enabled_p ())
-	dump_printf (MSG_MISSED_OPTIMIZATION,
-		     "runtime alias check not supported for outer loop.\n");
-      return false;
-    }
+    return opt_result::failure_at (DR_STMT (DDR_A (ddr)),
+				   "runtime alias check not supported for"
+				   " outer loop.\n");
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Operator == between two dr_with_seg_len objects.
@@ -5043,18 +5025,18 @@  loop_nest_has_data_refs (loop_p loop)
    reference, returns false, otherwise returns true.  NEST is the outermost
    loop of the loop nest in which the references should be analyzed.  */
 
-bool
+opt_result
 find_data_references_in_stmt (struct loop *nest, gimple *stmt,
 			      vec<data_reference_p> *datarefs)
 {
   unsigned i;
   auto_vec<data_ref_loc, 2> references;
   data_ref_loc *ref;
-  bool ret = true;
   data_reference_p dr;
 
   if (get_references_in_stmt (stmt, &references))
-    return false;
+    return opt_result::failure_at (stmt, "statement clobbers memory: %G",
+				   stmt);
 
   FOR_EACH_VEC_ELT (references, i, ref)
     {
@@ -5065,7 +5047,7 @@  find_data_references_in_stmt (struct loop *nest, gimple *stmt,
       datarefs->safe_push (dr);
     }
 
-  return ret;
+  return opt_result::success ();
 }
 
 /* Stores the data references in STMT to DATAREFS.  If there is an
diff --git a/gcc/tree-data-ref.h b/gcc/tree-data-ref.h
index 8739853..525d27f 100644
--- a/gcc/tree-data-ref.h
+++ b/gcc/tree-data-ref.h
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 
 #include "graphds.h"
 #include "tree-chrec.h"
+#include "opt-problem.h"
 
 /*
   innermost_loop_behavior describes the evolution of the address of the memory
@@ -421,7 +422,8 @@  typedef struct data_dependence_relation *ddr_p;
 #define DDR_COULD_BE_INDEPENDENT_P(DDR) (DDR)->could_be_independent_p
 
 
-bool dr_analyze_innermost (innermost_loop_behavior *, tree, struct loop *);
+opt_result dr_analyze_innermost (innermost_loop_behavior *, tree,
+				 struct loop *, const gimple *);
 extern bool compute_data_dependences_for_loop (struct loop *, bool,
 					       vec<loop_p> *,
 					       vec<data_reference_p> *,
@@ -443,8 +445,8 @@  extern void free_dependence_relation (struct data_dependence_relation *);
 extern void free_dependence_relations (vec<ddr_p> );
 extern void free_data_ref (data_reference_p);
 extern void free_data_refs (vec<data_reference_p> );
-extern bool find_data_references_in_stmt (struct loop *, gimple *,
-					  vec<data_reference_p> *);
+extern opt_result find_data_references_in_stmt (struct loop *, gimple *,
+						vec<data_reference_p> *);
 extern bool graphite_find_data_references_in_stmt (edge, loop_p, gimple *,
 						   vec<data_reference_p> *);
 tree find_data_references_in_loop (struct loop *, vec<data_reference_p> *);
@@ -479,7 +481,7 @@  extern bool dr_may_alias_p (const struct data_reference *,
 extern bool dr_equal_offsets_p (struct data_reference *,
                                 struct data_reference *);
 
-extern bool runtime_alias_check_p (ddr_p, struct loop *, bool);
+extern opt_result runtime_alias_check_p (ddr_p, struct loop *, bool);
 extern int data_ref_compare_tree (tree, tree);
 extern void prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *,
 					   poly_uint64);
diff --git a/gcc/tree-predcom.c b/gcc/tree-predcom.c
index 2bde732..1711027 100644
--- a/gcc/tree-predcom.c
+++ b/gcc/tree-predcom.c
@@ -1280,7 +1280,8 @@  find_looparound_phi (struct loop *loop, dref ref, dref root)
   memset (&init_dr, 0, sizeof (struct data_reference));
   DR_REF (&init_dr) = init_ref;
   DR_STMT (&init_dr) = phi;
-  if (!dr_analyze_innermost (&DR_INNERMOST (&init_dr), init_ref, loop))
+  if (!dr_analyze_innermost (&DR_INNERMOST (&init_dr), init_ref, loop,
+			     init_stmt))
     return NULL;
 
   if (!valid_initializer_p (&init_dr, ref->distance + 1, root->ref))
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 56b7968..c4805e7 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -156,20 +156,25 @@  vect_get_smallest_scalar_type (stmt_vec_info stmt_info,
    tested at run-time.  Return TRUE if DDR was successfully inserted.
    Return false if versioning is not supported.  */
 
-static bool
+static opt_result
 vect_mark_for_runtime_alias_test (ddr_p ddr, loop_vec_info loop_vinfo)
 {
   struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
 
   if ((unsigned) PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS) == 0)
-    return false;
+    return opt_result::failure_at (vect_location,
+				   "will not create alias checks, as"
+				   " --param vect-max-version-for-alias-checks"
+				   " == 0\n");
 
-  if (!runtime_alias_check_p (ddr, loop,
-			      optimize_loop_nest_for_speed_p (loop)))
-    return false;
+  opt_result res
+    = runtime_alias_check_p (ddr, loop,
+			     optimize_loop_nest_for_speed_p (loop));
+  if (!res)
+    return res;
 
   LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo).safe_push (ddr);
-  return true;
+  return opt_result::success ();
 }
 
 /* Record that loop LOOP_VINFO needs to check that VALUE is nonzero.  */
@@ -277,12 +282,14 @@  vect_analyze_possibly_independent_ddr (data_dependence_relation *ddr,
 
 /* Function vect_analyze_data_ref_dependence.
 
-   Return TRUE if there (might) exist a dependence between a memory-reference
+   FIXME: I needed to change the sense of the returned flag.
+
+   Return FALSE if there (might) exist a dependence between a memory-reference
    DRA and a memory-reference DRB.  When versioning for alias may check a
-   dependence at run-time, return FALSE.  Adjust *MAX_VF according to
+   dependence at run-time, return TRUE.  Adjust *MAX_VF according to
    the data dependence.  */
 
-static bool
+static opt_result
 vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 				  loop_vec_info loop_vinfo,
 				  unsigned int *max_vf)
@@ -305,11 +312,11 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 
   /* Independent data accesses.  */
   if (DDR_ARE_DEPENDENT (ddr) == chrec_known)
-    return false;
+    return opt_result::success ();
 
   if (dra == drb
       || (DR_IS_READ (dra) && DR_IS_READ (drb)))
-    return false;
+    return opt_result::success ();
 
   /* We do not have to consider dependences between accesses that belong
      to the same group, unless the stride could be smaller than the
@@ -318,7 +325,7 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
       && (DR_GROUP_FIRST_ELEMENT (stmtinfo_a)
 	  == DR_GROUP_FIRST_ELEMENT (stmtinfo_b))
       && !STMT_VINFO_STRIDED_P (stmtinfo_a))
-    return false;
+    return opt_result::success ();
 
   /* Even if we have an anti-dependence then, as the vectorized loop covers at
      least two scalar iterations, there is always also a true dependence.
@@ -330,7 +337,7 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
        || (DR_IS_WRITE (dra) && DR_IS_READ (drb)))
       && !alias_sets_conflict_p (get_alias_set (DR_REF (dra)),
 				 get_alias_set (DR_REF (drb))))
-    return false;
+    return opt_result::success ();
 
   /* Unknown data dependence.  */
   if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)
@@ -342,28 +349,25 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  if ((unsigned int) loop->safelen < *max_vf)
 	    *max_vf = loop->safelen;
 	  LOOP_VINFO_NO_DATA_DEPENDENCIES (loop_vinfo) = false;
-	  return false;
+	  return opt_result::success ();
 	}
 
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "versioning for alias not supported for: "
-			     "can't determine dependence between %T and %T\n",
-			     DR_REF (dra), DR_REF (drb));
-	  return true;
-	}
+	return opt_result::failure_at
+	  (stmtinfo_a->stmt,
+	   "versioning for alias not supported for: "
+	   "can't determine dependence between %T and %T\n",
+	   DR_REF (dra), DR_REF (drb));
 
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmtinfo_a->stmt,
 			 "versioning for alias required: "
 			 "can't determine dependence between %T and %T\n",
 			 DR_REF (dra), DR_REF (drb));
 
       /* Add to list of ddrs that need to be tested at run-time.  */
-      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
+      return vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
     }
 
   /* Known data dependence.  */
@@ -376,27 +380,24 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  if ((unsigned int) loop->safelen < *max_vf)
 	    *max_vf = loop->safelen;
 	  LOOP_VINFO_NO_DATA_DEPENDENCIES (loop_vinfo) = false;
-	  return false;
+	  return opt_result::success ();
 	}
 
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "versioning for alias not supported for: "
-			     "bad dist vector for %T and %T\n",
-			     DR_REF (dra), DR_REF (drb));
-	  return true;
-	}
+	return opt_result::failure_at
+	  (stmtinfo_a->stmt,
+	   "versioning for alias not supported for: "
+	   "bad dist vector for %T and %T\n",
+	   DR_REF (dra), DR_REF (drb));
 
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmtinfo_a->stmt,
 			 "versioning for alias required: "
 			 "bad dist vector for %T and %T\n",
 			 DR_REF (dra), DR_REF (drb));
       /* Add to list of ddrs that need to be tested at run-time.  */
-      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
+      return vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
     }
 
   loop_depth = index_in_loop_nest (loop->num, DDR_LOOP_NEST (ddr));
@@ -404,7 +405,7 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
   if (DDR_COULD_BE_INDEPENDENT_P (ddr)
       && vect_analyze_possibly_independent_ddr (ddr, loop_vinfo,
 						loop_depth, max_vf))
-    return false;
+    return opt_result::success ();
 
   FOR_EACH_VEC_ELT (DDR_DIST_VECTS (ddr), i, dist_v)
     {
@@ -440,23 +441,16 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 		a[i+1] = ...;
 	     where loads from the group interleave with the store.  */
 	  if (!vect_preserves_scalar_order_p (dr_info_a, dr_info_b))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "READ_WRITE dependence in interleaving.\n");
-	      return true;
-	    }
+	    return opt_result::failure_at (stmtinfo_a->stmt,
+					   "READ_WRITE dependence"
+					   " in interleaving.\n");
 
 	  if (loop->safelen < 2)
 	    {
 	      tree indicator = dr_zero_step_indicator (dra);
 	      if (!indicator || integer_zerop (indicator))
-		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "access also has a zero step\n");
-		  return true;
-		}
+		return opt_result::failure_at (stmtinfo_a->stmt,
+					       "access also has a zero step\n");
 	      else if (TREE_CODE (indicator) != INTEGER_CST)
 		vect_check_nonzero_value (loop_vinfo, indicator);
 	    }
@@ -503,16 +497,13 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  continue;
 	}
 
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized, possible dependence "
-			 "between data-refs %T and %T\n",
-			 DR_REF (dra), DR_REF (drb));
-
-      return true;
+      return opt_result::failure_at (stmtinfo_a->stmt,
+				     "not vectorized, possible dependence "
+				     "between data-refs %T and %T\n",
+				     DR_REF (dra), DR_REF (drb));
     }
 
-  return false;
+  return opt_result::success ();
 }
 
 /* Function vect_analyze_data_ref_dependences.
@@ -521,7 +512,7 @@  vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
    exist any data dependences between them.  Set *MAX_VF according to
    the maximum vectorization factor the data dependences allow.  */
 
-bool
+opt_result
 vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,
 				   unsigned int *max_vf)
 {
@@ -553,10 +544,14 @@  vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,
     *max_vf = LOOP_VINFO_ORIG_MAX_VECT_FACTOR (loop_vinfo);
   else
     FOR_EACH_VEC_ELT (LOOP_VINFO_DDRS (loop_vinfo), i, ddr)
-      if (vect_analyze_data_ref_dependence (ddr, loop_vinfo, max_vf))
-	return false;
+      {
+	opt_result res
+	  = vect_analyze_data_ref_dependence (ddr, loop_vinfo, max_vf);
+	if (!res)
+	  return res;
+      }
 
-  return true;
+  return opt_result::success ();
 }
 
 
@@ -1055,33 +1050,24 @@  vect_update_misalignment_for_peel (dr_vec_info *dr_info,
 
    Return TRUE if DR_INFO can be handled with respect to alignment.  */
 
-static bool
+static opt_result
 verify_data_ref_alignment (dr_vec_info *dr_info)
 {
   enum dr_alignment_support supportable_dr_alignment
     = vect_supportable_dr_alignment (dr_info, false);
   if (!supportable_dr_alignment)
-    {
-      if (dump_enabled_p ())
-	{
-	  if (DR_IS_READ (dr_info->dr))
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported unaligned load.");
-	  else
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported unaligned "
-			     "store.");
-
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "%T\n", DR_REF (dr_info->dr));
-	}
-      return false;
-    }
+    return opt_result::failure_at
+      (dr_info->stmt->stmt,
+       DR_IS_READ (dr_info->dr)
+	? "not vectorized: unsupported unaligned load: %T\n"
+	: "not vectorized: unsupported unaligned store: %T\n",
+       DR_REF (dr_info->dr));
 
   if (supportable_dr_alignment != dr_aligned && dump_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "Vectorizing an unaligned access.\n");
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Function vect_verify_datarefs_alignment
@@ -1089,7 +1075,7 @@  verify_data_ref_alignment (dr_vec_info *dr_info)
    Return TRUE if all data references in the loop can be
    handled with respect to alignment.  */
 
-bool
+opt_result
 vect_verify_datarefs_alignment (loop_vec_info vinfo)
 {
   vec<data_reference_p> datarefs = vinfo->shared->datarefs;
@@ -1115,11 +1101,12 @@  vect_verify_datarefs_alignment (loop_vec_info vinfo)
 	  && !STMT_VINFO_GROUPED_ACCESS (stmt_info))
 	continue;
 
-      if (! verify_data_ref_alignment (dr_info))
-	return false;
+      opt_result res = verify_data_ref_alignment (dr_info);
+      if (!res)
+	return res;
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Given an memory reference EXP return whether its alignment is less
@@ -1593,7 +1580,7 @@  vect_peeling_supportable (loop_vec_info loop_vinfo, dr_vec_info *dr0_info,
      (whether to generate regular loads/stores, or with special handling for
      misalignment).  */
 
-bool
+opt_result
 vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
 {
   vec<data_reference_p> datarefs = LOOP_VINFO_DATAREFS (loop_vinfo);
@@ -1605,7 +1592,6 @@  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
   unsigned int i, j;
   bool do_peeling = false;
   bool do_versioning = false;
-  bool stat;
   unsigned int npeel = 0;
   bool one_misalignment_known = false;
   bool one_misalignment_unknown = false;
@@ -1992,7 +1978,7 @@  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
       /* Check if all datarefs are supportable and log.  */
       if (do_peeling && known_alignment_for_access_p (dr0_info) && npeel == 0)
         {
-          stat = vect_verify_datarefs_alignment (loop_vinfo);
+          opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);
           if (!stat)
             do_peeling = false;
           else
@@ -2078,7 +2064,7 @@  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
 	  /* The inside-loop cost will be accounted for in vectorizable_load
 	     and vectorizable_store correctly with adjusted alignments.
 	     Drop the body_cst_vec on the floor here.  */
-	  stat = vect_verify_datarefs_alignment (loop_vinfo);
+	  opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);
 	  gcc_assert (stat);
           return stat;
         }
@@ -2201,7 +2187,7 @@  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
       /* Peeling and versioning can't be done together at this time.  */
       gcc_assert (! (do_peeling && do_versioning));
 
-      stat = vect_verify_datarefs_alignment (loop_vinfo);
+      opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);
       gcc_assert (stat);
       return stat;
     }
@@ -2209,7 +2195,7 @@  vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
   /* This point is reached if neither peeling nor versioning is being done.  */
   gcc_assert (! (do_peeling || do_versioning));
 
-  stat = vect_verify_datarefs_alignment (loop_vinfo);
+  opt_result stat = vect_verify_datarefs_alignment (loop_vinfo);
   return stat;
 }
 
@@ -2275,7 +2261,7 @@  vect_find_same_alignment_drs (vec_info *vinfo, data_dependence_relation *ddr)
    Analyze the alignment of the data-references in the loop.
    Return FALSE if a data reference is found that cannot be vectorized.  */
 
-bool
+opt_result
 vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 {
   DUMP_VECT_SCOPE ("vect_analyze_data_refs_alignment");
@@ -2300,7 +2286,7 @@  vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 	vect_compute_data_ref_alignment (dr_info);
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 
@@ -2825,7 +2811,7 @@  can_group_stmts_p (stmt_vec_info stmt1_info, stmt_vec_info stmt2_info)
 
    FORNOW: handle only arrays and pointer accesses.  */
 
-bool
+opt_result
 vect_analyze_data_ref_accesses (vec_info *vinfo)
 {
   unsigned int i;
@@ -2835,7 +2821,7 @@  vect_analyze_data_ref_accesses (vec_info *vinfo)
   DUMP_VECT_SCOPE ("vect_analyze_data_ref_accesses");
 
   if (datarefs.is_empty ())
-    return true;
+    return opt_result::success ();
 
   /* Sort the array of datarefs to make building the interleaving chains
      linear.  Don't modify the original vector's order, it is needed for
@@ -2994,13 +2980,15 @@  vect_analyze_data_ref_accesses (vec_info *vinfo)
 	  else
 	    {
 	      datarefs_copy.release ();
-	      return false;
+	      return opt_result::failure_at (dr_info->stmt->stmt,
+					     "not vectorized:"
+					     " complicated access pattern.\n");
 	    }
 	}
     }
 
   datarefs_copy.release ();
-  return true;
+  return opt_result::success ();
 }
 
 /* Function vect_vfa_segment_size.
@@ -3258,7 +3246,7 @@  vectorizable_with_step_bound_p (dr_vec_info *dr_info_a, dr_vec_info *dr_info_b,
    Return FALSE if resulting list of ddrs is longer then allowed by
    PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS, otherwise return TRUE.  */
 
-bool
+opt_result
 vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 {
   typedef pair_hash <tree_operand_hash, tree_operand_hash> tree_pair_hash;
@@ -3292,7 +3280,7 @@  vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
     }
 
   if (may_alias_ddrs.is_empty ())
-    return true;
+    return opt_result::success ();
 
   comp_alias_ddrs.create (may_alias_ddrs.length ());
 
@@ -3452,12 +3440,11 @@  vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	    continue;
 
 	  if (res == 1)
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_NOTE, vect_location,
-				 "not vectorized: compilation time alias.\n");
-	      return false;
-	    }
+	    return opt_result::failure_at (stmt_info_b->stmt,
+					   "not vectorized:"
+					   " compilation time alias: %G%G",
+					   stmt_info_a->stmt,
+					   stmt_info_b->stmt);
 	}
 
       dr_with_seg_len_pair_t dr_with_seg_len_pair
@@ -3482,17 +3469,14 @@  vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 		   "improved number of alias checks from %d to %d\n",
 		   may_alias_ddrs.length (), count);
   if ((int) count > PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "number of versioning for alias "
-			 "run-time tests exceeds %d "
-			 "(--param vect-max-version-for-alias-checks)\n",
-			 PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));
-      return false;
-    }
-
-  return true;
+    return opt_result::failure_at
+      (vect_location,
+       "number of versioning for alias "
+       "run-time tests exceeds %d "
+       "(--param vect-max-version-for-alias-checks)\n",
+       PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));
+
+  return opt_result::success ();
 }
 
 /* Check whether we can use an internal function for a gather load
@@ -3846,7 +3830,7 @@  vect_check_gather_scatter (stmt_vec_info stmt_info, loop_vec_info loop_vinfo,
    append them to DATAREFS.  Return false if datarefs in this stmt cannot
    be handled.  */
 
-bool
+opt_result
 vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 			       vec<data_reference_p> *datarefs)
 {
@@ -3854,72 +3838,50 @@  vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
      loop vectorization and BB vectorization checks dependences with a
      stmt walk.  */
   if (gimple_clobber_p (stmt))
-    return true;
+    return opt_result::success ();
 
   if (gimple_has_volatile_ops (stmt))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: volatile type %G", stmt);
-      return false;
-    }
+    return opt_result::failure_at (stmt, "not vectorized: volatile type: %G",
+				   stmt);
 
   if (stmt_can_throw_internal (stmt))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: statement can throw an exception %G",
-			 stmt);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized:"
+				   " statement can throw an exception: %G",
+				   stmt);
 
   auto_vec<data_reference_p, 2> refs;
-  if (!find_data_references_in_stmt (loop, stmt, &refs))
-    return false;
+  opt_result res = find_data_references_in_stmt (loop, stmt, &refs);
+  if (!res)
+    return res;
 
   if (refs.is_empty ())
-    return true;
+    return opt_result::success ();
 
   if (refs.length () > 1)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: more than one data ref "
-			 "in stmt: %G", stmt);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized:"
+				   " more than one data ref in stmt: %G", stmt);
 
   if (gcall *call = dyn_cast <gcall *> (stmt))
     if (!gimple_call_internal_p (call)
 	|| (gimple_call_internal_fn (call) != IFN_MASK_LOAD
 	    && gimple_call_internal_fn (call) != IFN_MASK_STORE))
-      {
-	if (dump_enabled_p ())
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION,  vect_location,
-			   "not vectorized: dr in a call %G", stmt);
-	return false;
-      }
+      return opt_result::failure_at (stmt,
+				     "not vectorized: dr in a call %G", stmt);
 
   data_reference_p dr = refs.pop ();
   if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF
       && DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1)))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: statement is bitfield "
-			 "access %G", stmt);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized:"
+				   " statement is bitfield access %G", stmt);
 
   if (DR_BASE_ADDRESS (dr)
       && TREE_CODE (DR_BASE_ADDRESS (dr)) == INTEGER_CST)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: base addr of dr is a "
-			 "constant\n");
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized:"
+				   " base addr of dr is a constant\n");
 
   /* Check whether this may be a SIMD lane access and adjust the
      DR to make it easier for us to handle it.  */
@@ -3976,7 +3938,7 @@  vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 			  newdr->aux = (void *)-1;
 			  free_data_ref (dr);
 			  datarefs->safe_push (newdr);
-			  return true;
+			  return opt_result::success ();
 			}
 		    }
 		}
@@ -3986,7 +3948,7 @@  vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
     }
 
   datarefs->safe_push (dr);
-  return true;
+  return opt_result::success ();
 }
 
 /* Function vect_analyze_data_refs.
@@ -4004,7 +3966,7 @@  vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 
 */
 
-bool
+opt_result
 vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 {
   struct loop *loop = NULL;
@@ -4074,7 +4036,10 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 		  STMT_VINFO_VECTORIZABLE (stmt_info) = false;
 		  continue;
 		}
-	      return false;
+	      return opt_result::failure_at (stmt_info->stmt,
+					     "not vectorized:"
+					     " data ref analysis failed: %G",
+					     stmt_info->stmt);
 	    }
         }
 
@@ -4082,13 +4047,10 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
       if (dr->aux == (void *)-1)
 	{
 	  if (nested_in_vect_loop_p (loop, stmt_info))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: data ref analysis "
-				 "failed %G", stmt_info->stmt);
-	      return false;
-	    }
+	    return opt_result::failure_at (stmt_info->stmt,
+					   "not vectorized:"
+					   " data ref analysis failed: %G",
+					   stmt_info->stmt);
 	  STMT_VINFO_SIMD_LANE_ACCESS_P (stmt_info) = true;
 	}
 
@@ -4106,7 +4068,10 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	      STMT_VINFO_VECTORIZABLE (stmt_info) = false;
 	      continue;
 	    }
-	  return false;
+	  return opt_result::failure_at (stmt_info->stmt,
+					 "not vectorized: base object not"
+					 " addressable for stmt: %G",
+					 stmt_info->stmt);
 	}
 
       if (is_a <loop_vec_info> (vinfo)
@@ -4114,13 +4079,10 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	  && TREE_CODE (DR_STEP (dr)) != INTEGER_CST)
 	{
 	  if (nested_in_vect_loop_p (loop, stmt_info))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: not suitable for strided "
-				 "load %G", stmt_info->stmt);
-	      return false;
-	    }
+	    return opt_result::failure_at (stmt_info->stmt,
+					   "not vectorized:"
+					   "not suitable for strided load %G",
+					   stmt_info->stmt);
 	  STMT_VINFO_STRIDED_P (stmt_info) = true;
 	}
 
@@ -4150,10 +4112,12 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "analyze in outer loop: %T\n", init_ref);
 
-	  if (!dr_analyze_innermost (&STMT_VINFO_DR_WRT_VEC_LOOP (stmt_info),
-				     init_ref, loop))
+	  opt_result res
+	    = dr_analyze_innermost (&STMT_VINFO_DR_WRT_VEC_LOOP (stmt_info),
+				    init_ref, loop, stmt_info->stmt);
+	  if (!res)
 	    /* dr_analyze_innermost already explained the failure.  */
-	    return false;
+	    return res;
 
           if (dump_enabled_p ())
 	    dump_printf_loc (MSG_NOTE, vect_location,
@@ -4199,7 +4163,11 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	      STMT_VINFO_VECTORIZABLE (stmt_info) = false;
 	      continue;
 	    }
-	  return false;
+	  return opt_result::failure_at (stmt_info->stmt,
+					 "not vectorized:"
+					 " no vectype for stmt: %G"
+					 " scalar_type: %T\n",
+					 stmt_info->stmt, scalar_type);
         }
       else
 	{
@@ -4221,17 +4189,12 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 					  as_a <loop_vec_info> (vinfo),
 					  &gs_info)
 	      || !get_vectype_for_scalar_type (TREE_TYPE (gs_info.offset)))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 (gatherscatter == GATHER) ?
-				 "not vectorized: not suitable for gather "
-				 "load %G" :
-				 "not vectorized: not suitable for scatter "
-				 "store %G",
-				 stmt_info->stmt);
-	      return false;
-	    }
+	    return opt_result::failure_at
+	      (stmt_info->stmt,
+	       (gatherscatter == GATHER) ?
+	       "not vectorized: not suitable for gather load %G" :
+	       "not vectorized: not suitable for scatter store %G",
+	       stmt_info->stmt);
 	  STMT_VINFO_GATHER_SCATTER_P (stmt_info) = gatherscatter;
 	}
     }
@@ -4240,7 +4203,7 @@  vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
      longer need to.  */
   gcc_assert (i == datarefs.length ());
 
-  return true;
+  return opt_result::success ();
 }
 
 
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index fdac10b..6ea1e77 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -159,7 +159,7 @@  static void vect_estimate_min_profitable_iters (loop_vec_info, int *, int *);
    statement.  VECTYPE_MAYBE_SET_P is true if STMT_VINFO_VECTYPE
    may already be set for general statements (not just data refs).  */
 
-static bool
+static opt_result
 vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,
 			      bool vectype_maybe_set_p,
 			      poly_uint64 *vf,
@@ -173,13 +173,14 @@  vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,
     {
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location, "skip.\n");
-      return true;
+      return opt_result::success ();
     }
 
   tree stmt_vectype, nunits_vectype;
-  if (!vect_get_vector_types_for_stmt (stmt_info, &stmt_vectype,
-				       &nunits_vectype))
-    return false;
+  opt_result res = vect_get_vector_types_for_stmt (stmt_info, &stmt_vectype,
+						   &nunits_vectype);
+  if (!res)
+    return res;
 
   if (stmt_vectype)
     {
@@ -199,7 +200,7 @@  vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,
   if (nunits_vectype)
     vect_update_max_nunits (vf, nunits_vectype);
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Subroutine of vect_determine_vectorization_factor.  Set the vector
@@ -209,7 +210,7 @@  vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,
    add them to MASK_PRODUCERS.  Return true on success or false if
    something prevented vectorization.  */
 
-static bool
+static opt_result
 vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
 			    vec<stmt_vec_info > *mask_producers)
 {
@@ -217,8 +218,10 @@  vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
   if (dump_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location, "==> examining statement: %G",
 		     stmt_info->stmt);
-  if (!vect_determine_vf_for_stmt_1 (stmt_info, false, vf, mask_producers))
-    return false;
+  opt_result res
+    = vect_determine_vf_for_stmt_1 (stmt_info, false, vf, mask_producers);
+  if (!res)
+    return res;
 
   if (STMT_VINFO_IN_PATTERN_P (stmt_info)
       && STMT_VINFO_RELATED_STMT (stmt_info))
@@ -237,18 +240,22 @@  vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
 			     def_stmt_info->stmt);
 	  if (!vect_determine_vf_for_stmt_1 (def_stmt_info, true,
 					     vf, mask_producers))
-	    return false;
+	  res = vect_determine_vf_for_stmt_1 (def_stmt_info, true,
+					      vf, mask_producers);
+	  if (!res)
+	    return res;
 	}
 
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "==> examining pattern statement: %G",
 			 stmt_info->stmt);
-      if (!vect_determine_vf_for_stmt_1 (stmt_info, true, vf, mask_producers))
-	return false;
+      res = vect_determine_vf_for_stmt_1 (stmt_info, true, vf, mask_producers);
+      if (!res)
+	return res;
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Function vect_determine_vectorization_factor
@@ -276,7 +283,7 @@  vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
         }
 */
 
-static bool
+static opt_result
 vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 {
   struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
@@ -320,14 +327,10 @@  vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 
 	      vectype = get_vectype_for_scalar_type (scalar_type);
 	      if (!vectype)
-		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				     "not vectorized: unsupported "
-				     "data-type %T\n",
-				     scalar_type);
-		  return false;
-		}
+		return opt_result::failure_at (phi,
+					       "not vectorized: unsupported "
+					       "data-type %T\n",
+					       scalar_type);
 	      STMT_VINFO_VECTYPE (stmt_info) = vectype;
 
 	      if (dump_enabled_p ())
@@ -349,9 +352,11 @@  vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	   gsi_next (&si))
 	{
 	  stmt_info = loop_vinfo->lookup_stmt (gsi_stmt (si));
-	  if (!vect_determine_vf_for_stmt (stmt_info, &vectorization_factor,
-					   &mask_producers))
-	    return false;
+	  opt_result res
+	    = vect_determine_vf_for_stmt (stmt_info, &vectorization_factor,
+					  &mask_producers);
+	  if (!res)
+	    return res;
         }
     }
 
@@ -364,24 +369,20 @@  vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
     }
 
   if (known_le (vectorization_factor, 1U))
-    {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "not vectorized: unsupported data-type\n");
-      return false;
-    }
+    return opt_result::failure_at (vect_location,
+				   "not vectorized: unsupported data-type\n");
   LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;
 
   for (i = 0; i < mask_producers.length (); i++)
     {
       stmt_info = mask_producers[i];
-      tree mask_type = vect_get_mask_type_for_stmt (stmt_info);
+      opt_tree mask_type = vect_get_mask_type_for_stmt (stmt_info);
       if (!mask_type)
-	return false;
+	return opt_result::propagate_failure (mask_type);
       STMT_VINFO_VECTYPE (stmt_info) = mask_type;
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 
@@ -1145,7 +1146,7 @@  vect_compute_single_scalar_iteration_cost (loop_vec_info loop_vinfo)
    - the number of iterations can be analyzed, i.e, a countable loop.  The
      niter could be analyzed under some assumptions.  */
 
-bool
+opt_result
 vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 			  tree *assumptions, tree *number_of_iterationsm1,
 			  tree *number_of_iterations, gcond **inner_loop_cond)
@@ -1171,20 +1172,13 @@  vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
                         (exit-bb)  */
 
       if (loop->num_nodes != 2)
-        {
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: control flow in loop.\n");
-          return false;
-        }
+	return opt_result::failure_at (vect_location,
+				       "not vectorized:"
+				       " control flow in loop.\n");
 
       if (empty_block_p (loop->header))
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: empty loop.\n");
-	  return false;
-	}
+	return opt_result::failure_at (vect_location,
+				       "not vectorized: empty loop.\n");
     }
   else
     {
@@ -1209,75 +1203,60 @@  vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 	 as described above.  */
 
       if ((loop->inner)->inner || (loop->inner)->next)
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: multiple nested loops.\n");
-	  return false;
-	}
+	return opt_result::failure_at (vect_location,
+				       "not vectorized:"
+				       " multiple nested loops.\n");
 
       if (loop->num_nodes != 5)
-        {
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: control flow in loop.\n");
-	  return false;
-        }
+	return opt_result::failure_at (vect_location,
+				       "not vectorized:"
+				       " control flow in loop.\n");
 
       entryedge = loop_preheader_edge (innerloop);
       if (entryedge->src != loop->header
 	  || !single_exit (innerloop)
 	  || single_exit (innerloop)->dest != EDGE_PRED (loop->latch, 0)->src)
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported outerloop form.\n");
-	  return false;
-	}
+	return opt_result::failure_at (vect_location,
+				       "not vectorized:"
+				       " unsupported outerloop form.\n");
 
       /* Analyze the inner-loop.  */
       tree inner_niterm1, inner_niter, inner_assumptions;
-      if (! vect_analyze_loop_form_1 (loop->inner, inner_loop_cond,
-				      &inner_assumptions, &inner_niterm1,
-				      &inner_niter, NULL)
-	  /* Don't support analyzing niter under assumptions for inner
-	     loop.  */
-	  || !integer_onep (inner_assumptions))
+      opt_result res
+	= vect_analyze_loop_form_1 (loop->inner, inner_loop_cond,
+				    &inner_assumptions, &inner_niterm1,
+				    &inner_niter, NULL);
+      if (!res)
 	{
 	  if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: Bad inner loop.\n");
-	  return false;
+	  return res;
 	}
 
+      /* Don't support analyzing niter under assumptions for inner
+	 loop.  */
+      if (!integer_onep (inner_assumptions))
+	return opt_result::failure_at (vect_location,
+				       "not vectorized: Bad inner loop.\n");
+
       if (!expr_invariant_in_loop_p (loop, inner_niter))
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: inner-loop count not"
-                             " invariant.\n");
-	  return false;
-	}
+	return opt_result::failure_at (vect_location,
+				       "not vectorized: inner-loop count not"
+				       " invariant.\n");
 
       if (dump_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "Considering outer-loop vectorization.\n");
     }
 
-  if (!single_exit (loop)
-      || EDGE_COUNT (loop->header->preds) != 2)
-    {
-      if (dump_enabled_p ())
-        {
-          if (!single_exit (loop))
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: multiple exits.\n");
-          else if (EDGE_COUNT (loop->header->preds) != 2)
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: too many incoming edges.\n");
-        }
-      return false;
-    }
+  if (!single_exit (loop))
+    return opt_result::failure_at (vect_location,
+				   "not vectorized: multiple exits.\n");
+  if (EDGE_COUNT (loop->header->preds) != 2)
+    return opt_result::failure_at (vect_location,
+				   "not vectorized:"
+				   " too many incoming edges.\n");
 
   /* We assume that the loop exit condition is at the end of the loop. i.e,
      that the loop is represented as a do-while (with a proper if-guard
@@ -1285,67 +1264,52 @@  vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
      executable statements, and the latch is empty.  */
   if (!empty_block_p (loop->latch)
       || !gimple_seq_empty_p (phi_nodes (loop->latch)))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: latch block not empty.\n");
-      return false;
-    }
+    return opt_result::failure_at (vect_location,
+				   "not vectorized: latch block not empty.\n");
 
   /* Make sure the exit is not abnormal.  */
   edge e = single_exit (loop);
   if (e->flags & EDGE_ABNORMAL)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: abnormal loop exit edge.\n");
-      return false;
-    }
+    return opt_result::failure_at (vect_location,
+				   "not vectorized:"
+				   " abnormal loop exit edge.\n");
 
   *loop_cond = vect_get_loop_niters (loop, assumptions, number_of_iterations,
 				     number_of_iterationsm1);
   if (!*loop_cond)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: complicated exit condition.\n");
-      return false;
-    }
+    return opt_result::failure_at
+      (vect_location,
+       "not vectorized: complicated exit condition.\n");
 
   if (integer_zerop (*assumptions)
       || !*number_of_iterations
       || chrec_contains_undetermined (*number_of_iterations))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: number of iterations cannot be "
-			 "computed.\n");
-      return false;
-    }
+    return opt_result::failure_at
+      (*loop_cond,
+       "not vectorized: number of iterations cannot be computed.\n");
 
   if (integer_zerop (*number_of_iterations))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: number of iterations = 0.\n");
-      return false;
-    }
+    return opt_result::failure_at
+      (*loop_cond,
+       "not vectorized: number of iterations = 0.\n");
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Analyze LOOP form and return a loop_vec_info if it is of suitable form.  */
 
-loop_vec_info
+opt_loop_vec_info
 vect_analyze_loop_form (struct loop *loop, vec_info_shared *shared)
 {
   tree assumptions, number_of_iterations, number_of_iterationsm1;
   gcond *loop_cond, *inner_loop_cond = NULL;
 
-  if (! vect_analyze_loop_form_1 (loop, &loop_cond,
-				  &assumptions, &number_of_iterationsm1,
-				  &number_of_iterations, &inner_loop_cond))
-    return NULL;
+  opt_result res
+    = vect_analyze_loop_form_1 (loop, &loop_cond,
+				&assumptions, &number_of_iterationsm1,
+				&number_of_iterations, &inner_loop_cond);
+  if (!res)
+    return opt_loop_vec_info::propagate_failure (res);
 
   loop_vec_info loop_vinfo = new _loop_vec_info (loop, shared);
   LOOP_VINFO_NITERSM1 (loop_vinfo) = number_of_iterationsm1;
@@ -1387,7 +1351,7 @@  vect_analyze_loop_form (struct loop *loop, vec_info_shared *shared)
 
   gcc_assert (!loop->aux);
   loop->aux = loop_vinfo;
-  return loop_vinfo;
+  return opt_loop_vec_info::success (loop_vinfo);
 }
 
 
@@ -1489,7 +1453,7 @@  vect_active_double_reduction_p (stmt_vec_info stmt_info)
 
    Scan the loop stmts and make sure they are all vectorizable.  */
 
-static bool
+static opt_result
 vect_analyze_loop_operations (loop_vec_info loop_vinfo)
 {
   struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
@@ -1531,13 +1495,9 @@  vect_analyze_loop_operations (loop_vec_info loop_vinfo)
                  requires to actually do something here.  */
               if (STMT_VINFO_LIVE_P (stmt_info)
 		  && !vect_active_double_reduction_p (stmt_info))
-                {
-                  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				     "Unsupported loop-closed phi in "
-				     "outer-loop.\n");
-                  return false;
-                }
+		return opt_result::failure_at (phi,
+					       "Unsupported loop-closed phi"
+					       " in outer-loop.\n");
 
               /* If PHI is used in the outer loop, we check that its operand
                  is defined in the inner loop.  */
@@ -1546,17 +1506,17 @@  vect_analyze_loop_operations (loop_vec_info loop_vinfo)
                   tree phi_op;
 
                   if (gimple_phi_num_args (phi) != 1)
-                    return false;
+                    return opt_result::failure_at (phi, "unsupported phi");
 
                   phi_op = PHI_ARG_DEF (phi, 0);
 		  stmt_vec_info op_def_info = loop_vinfo->lookup_def (phi_op);
 		  if (!op_def_info)
-                    return false;
+		    return opt_result::failure_at (phi, "unsupported phi");
 
 		  if (STMT_VINFO_RELEVANT (op_def_info) != vect_used_in_outer
 		      && (STMT_VINFO_RELEVANT (op_def_info)
 			  != vect_used_in_outer_by_reduction))
-		    return false;
+		    return opt_result::failure_at (phi, "unsupported phi");
                 }
 
               continue;
@@ -1567,13 +1527,10 @@  vect_analyze_loop_operations (loop_vec_info loop_vinfo)
           if ((STMT_VINFO_RELEVANT (stmt_info) == vect_used_in_scope
                || STMT_VINFO_LIVE_P (stmt_info))
               && STMT_VINFO_DEF_TYPE (stmt_info) != vect_induction_def)
-            {
-              /* A scalar-dependence cycle that we don't support.  */
-              if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: scalar dependence cycle.\n");
-              return false;
-            }
+	    /* A scalar-dependence cycle that we don't support.  */
+	    return opt_result::failure_at (phi,
+					   "not vectorized:"
+					   " scalar dependence cycle.\n");
 
           if (STMT_VINFO_RELEVANT_P (stmt_info))
             {
@@ -1597,24 +1554,25 @@  vect_analyze_loop_operations (loop_vec_info loop_vinfo)
 					      &cost_vec);
 
           if (!ok)
-            {
-              if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: relevant phi not "
-				 "supported: %G", phi);
-	      return false;
-            }
+	    return opt_result::failure_at (phi,
+					   "not vectorized: relevant phi not "
+					   "supported: %G",
+					   static_cast <gimple *> (phi));
         }
 
       for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
 	   gsi_next (&si))
         {
 	  gimple *stmt = gsi_stmt (si);
-	  if (!gimple_clobber_p (stmt)
-	      && !vect_analyze_stmt (loop_vinfo->lookup_stmt (stmt),
+	  if (!gimple_clobber_p (stmt))
+	    {
+	      opt_result res
+		= vect_analyze_stmt (loop_vinfo->lookup_stmt (stmt),
 				     &need_to_vectorize,
-				     NULL, NULL, &cost_vec))
-	    return false;
+				     NULL, NULL, &cost_vec);
+	      if (!res)
+		return res;
+	    }
         }
     } /* bbs */
 
@@ -1631,14 +1589,12 @@  vect_analyze_loop_operations (loop_vec_info loop_vinfo)
       if (dump_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "All the computation can be taken out of the loop.\n");
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: redundant loop. no profit to "
-			 "vectorize.\n");
-      return false;
+      return opt_result::failure_at
+	(vect_location,
+	 "not vectorized: redundant loop. no profit to vectorize.\n");
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Analyze the cost of the loop described by LOOP_VINFO.  Decide if it
@@ -1736,7 +1692,7 @@  vect_analyze_loop_costing (loop_vec_info loop_vinfo)
   return 1;
 }
 
-static bool
+static opt_result
 vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,
 			   vec<data_reference_p> *datarefs,
 			   unsigned int *n_stmts)
@@ -1750,7 +1706,8 @@  vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,
 	if (is_gimple_debug (stmt))
 	  continue;
 	++(*n_stmts);
-	if (!vect_find_stmt_data_reference (loop, stmt, datarefs))
+	opt_result res = vect_find_stmt_data_reference (loop, stmt, datarefs);
+	if (!res)
 	  {
 	    if (is_gimple_call (stmt) && loop->safelen)
 	      {
@@ -1782,15 +1739,16 @@  vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,
 		      }
 		  }
 	      }
-	    return false;
+	    return res;
 	  }
 	/* If dependence analysis will give up due to the limit on the
 	   number of datarefs stop here and fail fatally.  */
 	if (datarefs->length ()
 	    > (unsigned)PARAM_VALUE (PARAM_LOOP_MAX_DATAREFS_FOR_DATADEPS))
-	  return false;
+	  return opt_result::failure_at (stmt, "exceeded param "
+					 "loop-max-datarefs-for-datadeps\n");
       }
-  return true;
+  return opt_result::success ();
 }
 
 /* Function vect_analyze_loop_2.
@@ -1798,10 +1756,10 @@  vect_get_datarefs_in_loop (loop_p loop, basic_block *bbs,
    Apply a set of analyses on LOOP, and create a loop_vec_info struct
    for it.  The different analyses will record information in the
    loop_vec_info struct.  */
-static bool
+static opt_result
 vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
 {
-  bool ok;
+  opt_result ok = opt_result::success ();
   int res;
   unsigned int max_vf = MAX_VECTORIZATION_FACTOR;
   poly_uint64 min_vf = 2;
@@ -1817,16 +1775,18 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
   /* Gather the data references and count stmts in the loop.  */
   if (!LOOP_VINFO_DATAREFS (loop_vinfo).exists ())
     {
-      if (!vect_get_datarefs_in_loop (loop, LOOP_VINFO_BBS (loop_vinfo),
-				      &LOOP_VINFO_DATAREFS (loop_vinfo),
-				      n_stmts))
+      opt_result res
+	= vect_get_datarefs_in_loop (loop, LOOP_VINFO_BBS (loop_vinfo),
+				     &LOOP_VINFO_DATAREFS (loop_vinfo),
+				     n_stmts);
+      if (!res)
 	{
 	  if (dump_enabled_p ())
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: loop contains function "
 			     "calls or data references that cannot "
 			     "be analyzed\n");
-	  return false;
+	  return res;
 	}
       loop_vinfo->shared->save_datarefs ();
     }
@@ -1842,7 +1802,7 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data references.\n");
-      return false;
+      return ok;
     }
 
   /* Classify all cross-iteration scalar data-flow cycles.
@@ -1862,7 +1822,7 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data access.\n");
-      return false;
+      return ok;
     }
 
   /* Data-flow analysis to detect stmts that do not need to be vectorized.  */
@@ -1873,7 +1833,7 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "unexpected pattern.\n");
-      return false;
+      return ok;
     }
 
   /* While the rest of the analysis below depends on it in some way.  */
@@ -1885,15 +1845,16 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
      FORNOW: fail at the first data dependence that we encounter.  */
 
   ok = vect_analyze_data_ref_dependences (loop_vinfo, &max_vf);
-  if (!ok
-      || (max_vf != MAX_VECTORIZATION_FACTOR
-	  && maybe_lt (max_vf, min_vf)))
+  if (!ok)
     {
       if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "bad data dependence.\n");
-      return false;
+	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+			 "bad data dependence.\n");
+      return ok;
     }
+  if (max_vf != MAX_VECTORIZATION_FACTOR
+      && maybe_lt (max_vf, min_vf))
+    return opt_result::failure_at (vect_location, "bad data dependence.\n");
   LOOP_VINFO_MAX_VECT_FACTOR (loop_vinfo) = max_vf;
 
   ok = vect_determine_vectorization_factor (loop_vinfo);
@@ -1902,16 +1863,11 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "can't determine vectorization factor.\n");
-      return false;
+      return ok;
     }
   if (max_vf != MAX_VECTORIZATION_FACTOR
       && maybe_lt (max_vf, LOOP_VINFO_VECT_FACTOR (loop_vinfo)))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data dependence.\n");
-      return false;
-    }
+    return opt_result::failure_at (vect_location, "bad data dependence.\n");
 
   /* Compute the scalar iteration cost.  */
   vect_compute_single_scalar_iteration_cost (loop_vinfo);
@@ -1922,7 +1878,7 @@  vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal, unsigned *n_stmts)
   /* Check the SLP opportunities in the loop, analyze and build SLP trees.  */
   ok = vect_analyze_slp (loop_vinfo, *n_stmts);
   if (!ok)
-    return false;
+    return ok;
 
   /* If there are any SLP instances mark them as pure_slp.  */
   bool slp = vect_make_slp_decision (loop_vinfo);
@@ -1969,7 +1925,7 @@  start_over:
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data alignment.\n");
-      return false;
+      return ok;
     }
 
   /* Prune the list of ddrs to be tested at run-time by versioning for alias.
@@ -1977,7 +1933,7 @@  start_over:
      since we use grouping information gathered by interleaving analysis.  */
   ok = vect_prune_runtime_alias_test_list (loop_vinfo);
   if (!ok)
-    return false;
+    return ok;
 
   /* Do not invoke vect_enhance_data_refs_alignment for epilogue
      vectorization, since we do not want to add extra peeling or
@@ -1989,12 +1945,7 @@  start_over:
   else
     ok = vect_verify_datarefs_alignment (loop_vinfo);
   if (!ok)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data alignment.\n");
-      return false;
-    }
+    return ok;
 
   if (slp)
     {
@@ -2004,7 +1955,11 @@  start_over:
       unsigned old_size = LOOP_VINFO_SLP_INSTANCES (loop_vinfo).length ();
       vect_slp_analyze_operations (loop_vinfo);
       if (LOOP_VINFO_SLP_INSTANCES (loop_vinfo).length () != old_size)
-	goto again;
+	{
+	  ok = opt_result::failure_at (vect_location,
+				       "unsupported SLP instances\n");
+	  goto again;
+	}
     }
 
   /* Scan all the remaining operations in the loop that are not subject
@@ -2015,7 +1970,7 @@  start_over:
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad operation or unsupported loop bound.\n");
-      return false;
+      return ok;
     }
 
   /* Decide whether to use a fully-masked loop for this vectorization
@@ -2044,26 +1999,22 @@  start_over:
       tree scalar_niters = LOOP_VINFO_NITERSM1 (loop_vinfo);
 
       if (known_lt (wi::to_widest (scalar_niters), vf))
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "loop has no enough iterations to support"
-			     " peeling for gaps.\n");
-	  return false;
-	}
+	return opt_result::failure_at (vect_location,
+				       "loop has no enough iterations to"
+				       " support peeling for gaps.\n");
     }
 
   /* Check the costings of the loop make vectorizing worthwhile.  */
   res = vect_analyze_loop_costing (loop_vinfo);
   if (res < 0)
-    goto again;
-  if (!res)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "Loop costings not worthwhile.\n");
-      return false;
+      ok = opt_result::failure_at (vect_location,
+				   "Loop costings may not be worthwhile.\n");
+      goto again;
     }
+  if (!res)
+    return opt_result::failure_at (vect_location,
+				   "Loop costings not worthwhile.\n");
 
   /* Decide whether we need to create an epilogue loop to handle
      remaining scalar iterations.  */
@@ -2112,10 +2063,9 @@  start_over:
 					   single_exit (LOOP_VINFO_LOOP
 							 (loop_vinfo))))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: can't create required "
-			     "epilog loop\n");
+	  ok = opt_result::failure_at (vect_location,
+				       "not vectorized: can't create required "
+				       "epilog loop\n");
           goto again;
         }
     }
@@ -2154,17 +2104,20 @@  start_over:
 			LOOP_VINFO_VECT_FACTOR (loop_vinfo)));
 
   /* Ok to vectorize!  */
-  return true;
+  return opt_result::success ();
 
 again:
+  /* Ensure that "ok" is false (with an opt_problem if dumping is enabled).  */
+  gcc_assert (!ok);
+
   /* Try again with SLP forced off but if we didn't do any SLP there is
      no point in re-trying.  */
   if (!slp)
-    return false;
+    return ok;
 
   /* If there are reduction chains re-trying will fail anyway.  */
   if (! LOOP_VINFO_REDUCTION_CHAINS (loop_vinfo).is_empty ())
-    return false;
+    return ok;
 
   /* Likewise if the grouped loads or stores in the SLP cannot be handled
      via interleaving or lane instructions.  */
@@ -2183,7 +2136,8 @@  again:
       if (! vect_store_lanes_supported (vectype, size, false)
 	 && ! known_eq (TYPE_VECTOR_SUBPARTS (vectype), 1U)
 	 && ! vect_grouped_store_supported (vectype, size))
-       return false;
+	return opt_result::failure_at (vinfo->stmt,
+				       "unsupported grouped store\n");
       FOR_EACH_VEC_ELT (SLP_INSTANCE_LOADS (instance), j, node)
 	{
 	  vinfo = SLP_TREE_SCALAR_STMTS (node)[0];
@@ -2194,7 +2148,8 @@  again:
 	  if (! vect_load_lanes_supported (vectype, size, false)
 	      && ! vect_grouped_load_supported (vectype, single_element_p,
 						size))
-	    return false;
+	    return opt_result::failure_at (vinfo->stmt,
+					   "unsupported grouped load\n");
 	}
     }
 
@@ -2263,11 +2218,10 @@  again:
    for it.  The different analyses will record information in the
    loop_vec_info struct.  If ORIG_LOOP_VINFO is not NULL epilogue must
    be vectorized.  */
-loop_vec_info
+opt_loop_vec_info
 vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,
 		   vec_info_shared *shared)
 {
-  loop_vec_info loop_vinfo;
   auto_vector_sizes vector_sizes;
 
   /* Autodetect first vector size we try.  */
@@ -2280,35 +2234,28 @@  vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,
   if (loop_outer (loop)
       && loop_vec_info_for_loop (loop_outer (loop))
       && LOOP_VINFO_VECTORIZABLE_P (loop_vec_info_for_loop (loop_outer (loop))))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "outer-loop already vectorized.\n");
-      return NULL;
-    }
+    return opt_loop_vec_info::failure_at (vect_location,
+					  "outer-loop already vectorized.\n");
 
   if (!find_loop_nest (loop, &shared->loop_nest))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: loop nest containing two "
-			 "or more consecutive inner loops cannot be "
-			 "vectorized\n");
-      return NULL;
-    }
+    return opt_loop_vec_info::failure_at
+      (vect_location,
+       "not vectorized: loop nest containing two or more consecutive inner"
+       " loops cannot be vectorized\n");
 
   unsigned n_stmts = 0;
   poly_uint64 autodetected_vector_size = 0;
   while (1)
     {
       /* Check the CFG characteristics of the loop (nesting, entry/exit).  */
-      loop_vinfo = vect_analyze_loop_form (loop, shared);
+      opt_loop_vec_info loop_vinfo
+	= vect_analyze_loop_form (loop, shared);
       if (!loop_vinfo)
 	{
 	  if (dump_enabled_p ())
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "bad loop form.\n");
-	  return NULL;
+	  return loop_vinfo;
 	}
 
       bool fatal = false;
@@ -2316,7 +2263,8 @@  vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,
       if (orig_loop_vinfo)
 	LOOP_VINFO_ORIG_LOOP_INFO (loop_vinfo) = orig_loop_vinfo;
 
-      if (vect_analyze_loop_2 (loop_vinfo, fatal, &n_stmts))
+      opt_result res = vect_analyze_loop_2 (loop_vinfo, fatal, &n_stmts);
+      if (res)
 	{
 	  LOOP_VINFO_VECTORIZABLE_P (loop_vinfo) = 1;
 
@@ -2335,7 +2283,7 @@  vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo,
       if (fatal
 	  || next_size == vector_sizes.length ()
 	  || known_eq (current_vector_size, 0U))
-	return NULL;
+	return opt_loop_vec_info::propagate_failure (res);
 
       /* Try the next biggest vector size.  */
       current_vector_size = vector_sizes[next_size++];
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index ae1c453..f60fea0 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -2071,7 +2071,7 @@  vect_analyze_slp_instance (vec_info *vinfo,
 /* Check if there are stmts in the loop can be vectorized using SLP.  Build SLP
    trees of packed scalar stmts if SLP is possible.  */
 
-bool
+opt_result
 vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)
 {
   unsigned int i;
@@ -2111,7 +2111,7 @@  vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)
 				   max_tree_size);
     }
 
-  return true;
+  return opt_result::success ();
 }
 
 
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index 7a6efdb..8108d52 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -448,7 +448,7 @@  exist_non_indexing_operands_for_use_p (tree use, stmt_vec_info stmt_info)
 
    Return true if everything is as expected. Return false otherwise.  */
 
-static bool
+static opt_result
 process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,
 	     enum vect_relevant relevant, vec<stmt_vec_info> *worklist,
 	     bool force)
@@ -460,18 +460,15 @@  process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,
   /* case 1: we are only interested in uses that need to be vectorized.  Uses
      that are used for address computation are not considered relevant.  */
   if (!force && !exist_non_indexing_operands_for_use_p (use, stmt_vinfo))
-     return true;
+    return opt_result::success ();
 
   if (!vect_is_simple_use (use, loop_vinfo, &dt, &dstmt_vinfo))
-    {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "not vectorized: unsupported use in stmt.\n");
-      return false;
-    }
+    return opt_result::failure_at (stmt_vinfo->stmt,
+				   "not vectorized:"
+				   " unsupported use in stmt.\n");
 
   if (!dstmt_vinfo)
-    return true;
+    return opt_result::success ();
 
   def_bb = gimple_bb (dstmt_vinfo->stmt);
 
@@ -493,7 +490,7 @@  process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,
       gcc_assert (STMT_VINFO_RELEVANT (dstmt_vinfo) < vect_used_by_reduction);
       gcc_assert (STMT_VINFO_LIVE_P (dstmt_vinfo)
 		  || STMT_VINFO_RELEVANT (dstmt_vinfo) > vect_unused_in_scope);
-      return true;
+      return opt_result::success ();
     }
 
   /* case 3a: outer-loop stmt defining an inner-loop stmt:
@@ -582,12 +579,12 @@  process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "induction value on backedge.\n");
-      return true;
+      return opt_result::success ();
     }
 
 
   vect_mark_relevant (worklist, dstmt_vinfo, relevant, false);
-  return true;
+  return opt_result::success ();
 }
 
 
@@ -607,7 +604,7 @@  process_use (stmt_vec_info stmt_vinfo, tree use, loop_vec_info loop_vinfo,
 
    This pass detects such stmts.  */
 
-bool
+opt_result
 vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 {
   struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
@@ -684,38 +681,24 @@  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_in_scope
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
-	      {
-		if (dump_enabled_p ())
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "unsupported use of reduction.\n");
-		return false;
-	      }
+	      return opt_result::failure_at
+		(stmt_vinfo->stmt, "unsupported use of reduction.\n");
 	    break;
 
           case vect_nested_cycle:
 	    if (relevant != vect_unused_in_scope
 		&& relevant != vect_used_in_outer_by_reduction
 		&& relevant != vect_used_in_outer)
-              {
-                if (dump_enabled_p ())
-                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "unsupported use of nested cycle.\n");
-
-                return false;
-              }
+	      return opt_result::failure_at
+		(stmt_vinfo->stmt, "unsupported use of nested cycle.\n");
             break;
 
           case vect_double_reduction_def:
 	    if (relevant != vect_unused_in_scope
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
-              {
-                if (dump_enabled_p ())
-                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "unsupported use of double reduction.\n");
-
-                return false;
-              }
+	      return opt_result::failure_at
+		(stmt_vinfo->stmt, "unsupported use of double reduction.\n");
             break;
 
           default:
@@ -735,20 +718,28 @@  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 	      i = 1;
 	      if (rhs_code == COND_EXPR && COMPARISON_CLASS_P (op))
 		{
-		  if (!process_use (stmt_vinfo, TREE_OPERAND (op, 0),
-				    loop_vinfo, relevant, &worklist, false)
-		      || !process_use (stmt_vinfo, TREE_OPERAND (op, 1),
-				       loop_vinfo, relevant, &worklist, false))
-		    return false;
+		  opt_result res
+		    = process_use (stmt_vinfo, TREE_OPERAND (op, 0),
+				   loop_vinfo, relevant, &worklist, false);
+		  if (!res)
+		    return res;
+		  res = process_use (stmt_vinfo, TREE_OPERAND (op, 1),
+				     loop_vinfo, relevant, &worklist, false);
+		  if (!res)
+		    return res;
 		  i = 2;
 		}
 	      for (; i < gimple_num_ops (assign); i++)
 		{
 		  op = gimple_op (assign, i);
-                  if (TREE_CODE (op) == SSA_NAME
-		      && !process_use (stmt_vinfo, op, loop_vinfo, relevant,
-				       &worklist, false))
-                    return false;
+                  if (TREE_CODE (op) == SSA_NAME)
+		    {
+		      opt_result res
+			= process_use (stmt_vinfo, op, loop_vinfo, relevant,
+				       &worklist, false);
+		      if (!res)
+			return res;
+		    }
                  }
             }
 	  else if (gcall *call = dyn_cast <gcall *> (stmt_vinfo->stmt))
@@ -756,9 +747,11 @@  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 	      for (i = 0; i < gimple_call_num_args (call); i++)
 		{
 		  tree arg = gimple_call_arg (call, i);
-		  if (!process_use (stmt_vinfo, arg, loop_vinfo, relevant,
-				    &worklist, false))
-                    return false;
+		  opt_result res
+		    = process_use (stmt_vinfo, arg, loop_vinfo, relevant,
+				   &worklist, false);
+		  if (!res)
+		    return res;
 		}
 	    }
         }
@@ -766,9 +759,11 @@  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 	FOR_EACH_PHI_OR_STMT_USE (use_p, stmt_vinfo->stmt, iter, SSA_OP_USE)
           {
             tree op = USE_FROM_PTR (use_p);
-	    if (!process_use (stmt_vinfo, op, loop_vinfo, relevant,
-			      &worklist, false))
-              return false;
+	    opt_result res
+	      = process_use (stmt_vinfo, op, loop_vinfo, relevant,
+			     &worklist, false);
+	    if (!res)
+	      return res;
           }
 
       if (STMT_VINFO_GATHER_SCATTER_P (stmt_vinfo))
@@ -776,13 +771,15 @@  vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 	  gather_scatter_info gs_info;
 	  if (!vect_check_gather_scatter (stmt_vinfo, loop_vinfo, &gs_info))
 	    gcc_unreachable ();
-	  if (!process_use (stmt_vinfo, gs_info.offset, loop_vinfo, relevant,
-			    &worklist, true))
-	    return false;
+	  opt_result res
+	    = process_use (stmt_vinfo, gs_info.offset, loop_vinfo, relevant,
+			   &worklist, true);
+	  if (!res)
+	    return res;
 	}
     } /* while worklist */
 
-  return true;
+  return opt_result::success ();
 }
 
 /* Compute the prologue cost for invariant or constant operands.  */
@@ -9382,7 +9379,7 @@  can_vectorize_live_stmts (stmt_vec_info stmt_info, gimple_stmt_iterator *gsi,
 
 /* Make sure the statement is vectorizable.  */
 
-bool
+opt_result
 vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
 		   slp_tree node, slp_instance node_instance,
 		   stmt_vector_for_cost *cost_vec)
@@ -9398,13 +9395,10 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
 		     stmt_info->stmt);
 
   if (gimple_has_volatile_ops (stmt_info->stmt))
-    {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "not vectorized: stmt has volatile operands\n");
-
-      return false;
-    }
+    return opt_result::failure_at (stmt_info->stmt,
+				   "not vectorized:"
+				   " stmt has volatile operands: %G\n",
+				   stmt_info->stmt);
 
   if (STMT_VINFO_IN_PATTERN_P (stmt_info)
       && node == NULL
@@ -9425,10 +9419,12 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
 				 "==> examining pattern def statement: %G",
 				 pattern_def_stmt_info->stmt);
 
-	      if (!vect_analyze_stmt (pattern_def_stmt_info,
-				      need_to_vectorize, node, node_instance,
-				      cost_vec))
-		return false;
+	      opt_result res
+		= vect_analyze_stmt (pattern_def_stmt_info,
+				     need_to_vectorize, node, node_instance,
+				     cost_vec);
+	      if (!res)
+		return res;
 	    }
 	}
     }
@@ -9468,7 +9464,7 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
           if (dump_enabled_p ())
             dump_printf_loc (MSG_NOTE, vect_location, "irrelevant.\n");
 
-          return true;
+          return opt_result::success ();
         }
     }
   else if (STMT_VINFO_IN_PATTERN_P (stmt_info)
@@ -9483,9 +9479,11 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
 			 "==> examining pattern statement: %G",
 			 pattern_stmt_info->stmt);
 
-      if (!vect_analyze_stmt (pattern_stmt_info, need_to_vectorize, node,
-			      node_instance, cost_vec))
-        return false;
+      opt_result res
+	= vect_analyze_stmt (pattern_stmt_info, need_to_vectorize, node,
+			     node_instance, cost_vec);
+      if (!res)
+	return res;
    }
 
   switch (STMT_VINFO_DEF_TYPE (stmt_info))
@@ -9528,7 +9526,7 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "handled only by SLP analysis\n");
-      return true;
+      return opt_result::success ();
     }
 
   ok = true;
@@ -9573,30 +9571,22 @@  vect_analyze_stmt (stmt_vec_info stmt_info, bool *need_to_vectorize,
     }
 
   if (!ok)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: relevant stmt not supported: %G",
-			 stmt_info->stmt);
-
-      return false;
-    }
+    return opt_result::failure_at (stmt_info->stmt,
+				   "not vectorized:"
+				   " relevant stmt not supported: %G",
+				   stmt_info->stmt);
 
   /* Stmts that are (also) "live" (i.e. - that are used out of the loop)
       need extra handling, except for vectorizable reductions.  */
   if (!bb_vinfo
       && STMT_VINFO_TYPE (stmt_info) != reduc_vec_info_type
       && !can_vectorize_live_stmts (stmt_info, NULL, node, NULL, cost_vec))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: live stmt not supported: %G",
-			 stmt_info->stmt);
+    return opt_result::failure_at (stmt_info->stmt,
+				   "not vectorized:"
+				   " live stmt not supported: %G",
+				   stmt_info->stmt);
 
-       return false;
-    }
-
-  return true;
+  return opt_result::success ();
 }
 
 
@@ -10537,7 +10527,7 @@  vect_gen_while_not (gimple_seq *seq, tree mask_type, tree start_index,
      number of units needed to vectorize STMT_INFO, or NULL_TREE if the
      statement does not help to determine the overall number of units.  */
 
-bool
+opt_result
 vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 				tree *stmt_vectype_out,
 				tree *nunits_vectype_out)
@@ -10560,22 +10550,17 @@  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 	  if (dump_enabled_p ())
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "defer to SIMD clone analysis.\n");
-	  return true;
+	  return opt_result::success ();
 	}
 
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: irregular stmt.%G", stmt);
-      return false;
+      return opt_result::failure_at (stmt,
+				     "not vectorized: irregular stmt.%G", stmt);
     }
 
   if (VECTOR_MODE_P (TYPE_MODE (gimple_expr_type (stmt))))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: vector stmt in loop:%G", stmt);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized: vector stmt in loop:%G",
+				   stmt);
 
   tree vectype;
   tree scalar_type = NULL_TREE;
@@ -10606,7 +10591,7 @@  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 	      if (dump_enabled_p ())
 		dump_printf_loc (MSG_NOTE, vect_location,
 				 "pure bool operation.\n");
-	      return true;
+	      return opt_result::success ();
 	    }
 	}
 
@@ -10615,13 +10600,10 @@  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 			 "get vectype for scalar type:  %T\n", scalar_type);
       vectype = get_vectype_for_scalar_type (scalar_type);
       if (!vectype)
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported data-type %T\n",
-			     scalar_type);
-	  return false;
-	}
+	return opt_result::failure_at (stmt,
+				       "not vectorized:"
+				       " unsupported data-type %T\n",
+				       scalar_type);
 
       if (!*stmt_vectype_out)
 	*stmt_vectype_out = vectype;
@@ -10652,24 +10634,16 @@  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
       nunits_vectype = get_vectype_for_scalar_type (scalar_type);
     }
   if (!nunits_vectype)
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: unsupported data-type %T\n",
-			 scalar_type);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized: unsupported data-type %T\n",
+				   scalar_type);
 
   if (maybe_ne (GET_MODE_SIZE (TYPE_MODE (vectype)),
 		GET_MODE_SIZE (TYPE_MODE (nunits_vectype))))
-    {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: different sized vector "
-			 "types in statement, %T and %T\n",
-			 vectype, nunits_vectype);
-      return false;
-    }
+    return opt_result::failure_at (stmt,
+				   "not vectorized: different sized vector "
+				   "types in statement, %T and %T\n",
+				   vectype, nunits_vectype);
 
   if (dump_enabled_p ())
     {
@@ -10682,14 +10656,14 @@  vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
     }
 
   *nunits_vectype_out = nunits_vectype;
-  return true;
+  return opt_result::success ();
 }
 
 /* Try to determine the correct vector type for STMT_INFO, which is a
    statement that produces a scalar boolean result.  Return the vector
    type on success, otherwise return NULL_TREE.  */
 
-tree
+opt_tree
 vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 {
   gimple *stmt = stmt_info->stmt;
@@ -10704,12 +10678,8 @@  vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
       mask_type = get_mask_type_for_scalar_type (scalar_type);
 
       if (!mask_type)
-	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported mask\n");
-	  return NULL_TREE;
-	}
+	return opt_tree::failure_at (stmt,
+				     "not vectorized: unsupported mask\n");
     }
   else
     {
@@ -10720,13 +10690,9 @@  vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
       FOR_EACH_SSA_TREE_OPERAND (rhs, stmt, iter, SSA_OP_USE)
 	{
 	  if (!vect_is_simple_use (rhs, stmt_info->vinfo, &dt, &vectype))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: can't compute mask type "
-				 "for statement, %G", stmt);
-	      return NULL_TREE;
-	    }
+	    return opt_tree::failure_at (stmt,
+					 "not vectorized:can't compute mask"
+					 " type for statement, %G", stmt);
 
 	  /* No vectype probably means external definition.
 	     Allow it in case there is another operand which
@@ -10738,25 +10704,17 @@  vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 	    mask_type = vectype;
 	  else if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_type),
 			     TYPE_VECTOR_SUBPARTS (vectype)))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: different sized masks "
-				 "types in statement, %T and %T\n",
-				 mask_type, vectype);
-	      return NULL_TREE;
-	    }
+	    return opt_tree::failure_at (stmt,
+					 "not vectorized: different sized mask"
+					 " types in statement, %T and %T\n",
+					 mask_type, vectype);
 	  else if (VECTOR_BOOLEAN_TYPE_P (mask_type)
 		   != VECTOR_BOOLEAN_TYPE_P (vectype))
-	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: mixed mask and "
-				 "nonmask vector types in statement, "
-				 "%T and %T\n",
-				 mask_type, vectype);
-	      return NULL_TREE;
-	    }
+	    return opt_tree::failure_at (stmt,
+					 "not vectorized: mixed mask and "
+					 "nonmask vector types in statement, "
+					 "%T and %T\n",
+					 mask_type, vectype);
 	}
 
       /* We may compare boolean value loaded as vector of integers.
@@ -10770,9 +10728,10 @@  vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 
   /* No mask_type should mean loop invariant predicate.
      This is probably a subject for optimization in if-conversion.  */
-  if (!mask_type && dump_enabled_p ())
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-		     "not vectorized: can't compute mask type "
-		     "for statement, %G", stmt);
-  return mask_type;
+  if (!mask_type)
+    return opt_tree::failure_at (stmt,
+				 "not vectorized: can't compute mask type "
+				 "for statement: %G", stmt);
+
+  return opt_tree::success (mask_type);
 }
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 23bddf3..747fb67 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -79,6 +79,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "gimple-pretty-print.h"
+#include "opt-problem.h"
 
 
 /* Loop or bb location, with hotness information.  */
@@ -860,13 +861,25 @@  try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
   vect_location = find_loop_location (loop);
   if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
       && dump_enabled_p ())
-    dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
+    dump_printf (MSG_NOTE | MSG_PRIORITY_INTERNALS,
+		 "\nAnalyzing loop at %s:%d\n",
 		 LOCATION_FILE (vect_location.get_location_t ()),
 		 LOCATION_LINE (vect_location.get_location_t ()));
 
-  loop_vec_info loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo, &shared);
+  /* Try to analyze the loop, retaining an opt_problem if dump_enabled_p.  */
+  opt_loop_vec_info loop_vinfo
+    = vect_analyze_loop (loop, orig_loop_vinfo, &shared);
   loop->aux = loop_vinfo;
 
+  if (!loop_vinfo)
+    if (dump_enabled_p ())
+      if (opt_problem *problem = loop_vinfo.get_problem ())
+	{
+	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+			   "couldn't vectorize loop\n");
+	  problem->emit_and_clear ();
+	}
+
   if (!loop_vinfo || !LOOP_VINFO_VECTORIZABLE_P (loop_vinfo))
     {
       /* Free existing information if loop is analyzed with some
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index af5d5bf..63cff79 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -612,6 +612,12 @@  typedef struct _loop_vec_info : public vec_info {
 #define LOOP_VINFO_ORIG_MAX_VECT_FACTOR(L) \
   (LOOP_VINFO_MAX_VECT_FACTOR (LOOP_VINFO_ORIG_LOOP_INFO (L)))
 
+/* Wrapper for loop_vec_info, for tracking success/failure, where a non-NULL
+   value signifies success, and a NULL value signifies failure, supporting
+   propagating an opt_problem * describing the failure back up the call
+   stack.  */
+typedef opt_pointer_wrapper <loop_vec_info> opt_loop_vec_info;
+
 static inline loop_vec_info
 loop_vec_info_for_loop (struct loop *loop)
 {
@@ -1473,7 +1479,7 @@  extern unsigned record_stmt_cost (stmt_vector_for_cost *, int,
 extern stmt_vec_info vect_finish_replace_stmt (stmt_vec_info, gimple *);
 extern stmt_vec_info vect_finish_stmt_generation (stmt_vec_info, gimple *,
 						  gimple_stmt_iterator *);
-extern bool vect_mark_stmts_to_be_vectorized (loop_vec_info);
+extern opt_result vect_mark_stmts_to_be_vectorized (loop_vec_info);
 extern tree vect_get_store_rhs (stmt_vec_info);
 extern tree vect_get_vec_def_for_operand_1 (stmt_vec_info, enum vect_def_type);
 extern tree vect_get_vec_def_for_operand (tree, stmt_vec_info, tree = NULL);
@@ -1487,8 +1493,8 @@  extern tree vect_get_vec_def_for_stmt_copy (vec_info *, tree);
 extern bool vect_transform_stmt (stmt_vec_info, gimple_stmt_iterator *,
 				 slp_tree, slp_instance);
 extern void vect_remove_stores (stmt_vec_info);
-extern bool vect_analyze_stmt (stmt_vec_info, bool *, slp_tree, slp_instance,
-			       stmt_vector_for_cost *);
+extern opt_result vect_analyze_stmt (stmt_vec_info, bool *, slp_tree,
+				     slp_instance, stmt_vector_for_cost *);
 extern bool vectorizable_condition (stmt_vec_info, gimple_stmt_iterator *,
 				    stmt_vec_info *, tree, int, slp_tree,
 				    stmt_vector_for_cost *);
@@ -1504,8 +1510,9 @@  extern tree vect_gen_perm_mask_checked (tree, const vec_perm_indices &);
 extern void optimize_mask_stores (struct loop*);
 extern gcall *vect_gen_while (tree, tree, tree);
 extern tree vect_gen_while_not (gimple_seq *, tree, tree, tree);
-extern bool vect_get_vector_types_for_stmt (stmt_vec_info, tree *, tree *);
-extern tree vect_get_mask_type_for_stmt (stmt_vec_info);
+extern opt_result vect_get_vector_types_for_stmt (stmt_vec_info, tree *,
+						  tree *);
+extern opt_tree vect_get_mask_type_for_stmt (stmt_vec_info);
 
 /* In tree-vect-data-refs.c.  */
 extern bool vect_can_force_dr_alignment_p (const_tree, unsigned int);
@@ -1513,21 +1520,21 @@  extern enum dr_alignment_support vect_supportable_dr_alignment
                                            (dr_vec_info *, bool);
 extern tree vect_get_smallest_scalar_type (stmt_vec_info, HOST_WIDE_INT *,
                                            HOST_WIDE_INT *);
-extern bool vect_analyze_data_ref_dependences (loop_vec_info, unsigned int *);
+extern opt_result vect_analyze_data_ref_dependences (loop_vec_info, unsigned int *);
 extern bool vect_slp_analyze_instance_dependence (slp_instance);
-extern bool vect_enhance_data_refs_alignment (loop_vec_info);
-extern bool vect_analyze_data_refs_alignment (loop_vec_info);
-extern bool vect_verify_datarefs_alignment (loop_vec_info);
+extern opt_result vect_enhance_data_refs_alignment (loop_vec_info);
+extern opt_result vect_analyze_data_refs_alignment (loop_vec_info);
+extern opt_result vect_verify_datarefs_alignment (loop_vec_info);
 extern bool vect_slp_analyze_and_verify_instance_alignment (slp_instance);
-extern bool vect_analyze_data_ref_accesses (vec_info *);
-extern bool vect_prune_runtime_alias_test_list (loop_vec_info);
+extern opt_result vect_analyze_data_ref_accesses (vec_info *);
+extern opt_result vect_prune_runtime_alias_test_list (loop_vec_info);
 extern bool vect_gather_scatter_fn_p (bool, bool, tree, tree, unsigned int,
 				      signop, int, internal_fn *, tree *);
 extern bool vect_check_gather_scatter (stmt_vec_info, loop_vec_info,
 				       gather_scatter_info *);
-extern bool vect_find_stmt_data_reference (loop_p, gimple *,
-					   vec<data_reference_p> *);
-extern bool vect_analyze_data_refs (vec_info *, poly_uint64 *);
+extern opt_result vect_find_stmt_data_reference (loop_p, gimple *,
+						 vec<data_reference_p> *);
+extern opt_result vect_analyze_data_refs (vec_info *, poly_uint64 *);
 extern void vect_record_base_alignments (vec_info *);
 extern tree vect_create_data_ref_ptr (stmt_vec_info, tree, struct loop *, tree,
 				      tree *, gimple_stmt_iterator *,
@@ -1563,8 +1570,9 @@  extern stmt_vec_info vect_force_simple_reduction (loop_vec_info, stmt_vec_info,
 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,
-					vec_info_shared *);
+extern opt_loop_vec_info vect_analyze_loop (struct loop *,
+					    loop_vec_info,
+					    vec_info_shared *);
 extern tree vect_build_loop_niters (loop_vec_info, bool * = NULL);
 extern void vect_gen_vector_loop_niters (loop_vec_info, tree, tree *,
 					 tree *, bool);
@@ -1577,7 +1585,8 @@  extern tree vect_get_loop_mask (gimple_stmt_iterator *, vec_loop_masks *,
 
 /* Drive for loop transformation stage.  */
 extern struct loop *vect_transform_loop (loop_vec_info);
-extern loop_vec_info vect_analyze_loop_form (struct loop *, vec_info_shared *);
+extern opt_loop_vec_info vect_analyze_loop_form (struct loop *,
+						 vec_info_shared *);
 extern bool vectorizable_live_operation (stmt_vec_info, gimple_stmt_iterator *,
 					 slp_tree, int, stmt_vec_info *,
 					 stmt_vector_for_cost *);
@@ -1602,7 +1611,7 @@  extern bool vect_transform_slp_perm_load (slp_tree, vec<tree> ,
 					  slp_instance, bool, unsigned *);
 extern bool vect_slp_analyze_operations (vec_info *);
 extern void vect_schedule_slp (vec_info *);
-extern bool vect_analyze_slp (vec_info *, unsigned);
+extern opt_result vect_analyze_slp (vec_info *, unsigned);
 extern bool vect_make_slp_decision (loop_vec_info);
 extern void vect_detect_hybrid_slp (loop_vec_info);
 extern void vect_get_slp_defs (vec<tree> , slp_tree, vec<vec<tree> > *);