From: David Malcolm Date: Thu, 4 Oct 2018 17:50:52 +0000 (+0000) Subject: Report vectorization problems via a new opt_problem class X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f4ebbd243f887b3c5e01c65ad80a8f64a8261e61;p=gcc.git Report vectorization problems via a new opt_problem class 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 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. From-SVN: r264852 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 85e3f3a281c..68319016135 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,170 @@ +2018-10-04 David Malcolm + + * 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. + 2018-10-04 David Malcolm * doc/invoke.texi (-fopt-info): Document new "internals" diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 4b7cec82382..116ed6ea8a5 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 20b94a7a93c..3a45f230105 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 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 e15edc7a31c..0b140ffb8be 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 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 59339057c1b..c82157d295c 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 00000000000..dad3a8c008b --- /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 . + +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 +. */ + +#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. */ + 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 (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. */ + 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 00000000000..68d7e4a5f7b --- /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 . + +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 +. */ + +#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, where opt_wrapper 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 can be used to wrap pointer types (where non-NULL + signifies success, NULL signifies failure). + + In all cases, opt_wrapper 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 is effectively just a T, so that we're + still just passing e.g. a bool around; the opt_wrapper 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 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 +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 in memory is just a T. */ + } + + private: + wrapped_t m_result; +}; + +/* Subclass of opt_wrapper 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 +{ + 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 + static opt_result + propagate_failure (opt_wrapper 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 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 +class opt_pointer_wrapper : public opt_wrapper +{ + public: + typedef PtrType_t wrapped_pointer_t; + + /* Given a non-NULL pointer, make a success object wrapping it. */ + + static opt_pointer_wrapper + success (wrapped_pointer_t ptr) + { + return opt_pointer_wrapper (ptr, NULL); + } + + /* Make a NULL pointer failure object, with the given message + (if dump_enabled_p). */ + + static opt_pointer_wrapper + 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 (NULL, problem); + } + + /* Given a failure wrapper of some other kind, make a NULL pointer + failure object, propagating the problem. */ + + template + static opt_pointer_wrapper + propagate_failure (opt_wrapper other) + { + return opt_pointer_wrapper (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 (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 opt_tree; + +#endif /* #ifndef GCC_OPT_PROBLEM_H */ diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc index efdbdb3ae01..31029ad8479 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 b858c3ca08a..de781a52125 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 8ac961c9e92..99bd22c7538 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 5adb03391c2..562ada74a5f 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 ede77324453..8da7c4ae1c0 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/ChangeLog b/gcc/testsuite/ChangeLog index f410171a370..d3eb299132d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2018-10-04 David Malcolm + + * 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. + 2018-10-04 David Malcolm * gcc.dg/plugin/dump-1.c: Update expected output for test_scopes 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 00000000000..94c55a92bb4 --- /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 1e5fc273ec1..750193e3cd4 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 bf30a61c868..69c5f7b28ae 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 *datarefs) { unsigned i; auto_vec 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 8739853336a..525d27f04b9 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 *, vec *, @@ -443,8 +445,8 @@ extern void free_dependence_relation (struct data_dependence_relation *); extern void free_dependence_relations (vec ); extern void free_data_ref (data_reference_p); extern void free_data_refs (vec ); -extern bool find_data_references_in_stmt (struct loop *, gimple *, - vec *); +extern opt_result find_data_references_in_stmt (struct loop *, gimple *, + vec *); extern bool graphite_find_data_references_in_stmt (edge, loop_p, gimple *, vec *); tree find_data_references_in_loop (struct loop *, vec *); @@ -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 *, poly_uint64); diff --git a/gcc/tree-predcom.c b/gcc/tree-predcom.c index 2bde732f8ad..1711027bdf7 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 56b7968dab0..c4805e72447 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 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 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_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 *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 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 (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 (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 (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 fdac10bab5a..6ea1e77d4ba 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 *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 (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 *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 ae1c453c5df..f60fea0a581 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 7a6efdb5117..8108d52a3cb 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 *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 (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 23bddf3f455..747fb67ba13 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_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 af5d5bf2b36..63cff797dd8 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 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 *); -extern bool vect_analyze_data_refs (vec_info *, poly_uint64 *); +extern opt_result vect_find_stmt_data_reference (loop_p, gimple *, + vec *); +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 , 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 , slp_tree, vec > *);