Message ID | 1538161880-64793-4-git-send-email-dmalcolm@redhat.com |
---|---|
State | New |
Headers | show |
Series | Higher-level reporting of vectorization problems (v3) | expand |
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> 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
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
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 > >
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> > *);