diff options
author | David Malcolm <dmalcolm@redhat.com> | 2018-10-04 17:50:52 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2018-10-04 17:50:52 +0000 |
commit | f4ebbd243f887b3c5e01c65ad80a8f64a8261e61 (patch) | |
tree | f76bbe59cb30638b7432efe17c3ddd67c6378b9d /gcc/opt-problem.cc | |
parent | 7db960c5b6adad2fd11789870aa514985ea0da04 (diff) |
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
Diffstat (limited to 'gcc/opt-problem.cc')
-rw-r--r-- | gcc/opt-problem.cc | 335 |
1 files changed, 335 insertions, 0 deletions
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 <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "pretty-print.h" +#include "opt-problem.h" +#include "dump-context.h" +#include "tree-pass.h" +#include "selftest.h" + +/* opt_problem's ctor. + + Use FMT and AP to emit a message to the "immediate" dump destinations + as if via: + dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, ...) + + The optinfo_item instances are not emitted yet. Instead, they + are retained internally so that the message can be replayed and + emitted when this problem is handled, higher up the call stack. */ + +opt_problem::opt_problem (const dump_location_t &loc, + const char *fmt, va_list *ap) +: m_optinfo (loc, OPTINFO_KIND_FAILURE, current_pass) +{ + /* We shouldn't be bothering to construct these objects if + dumping isn't enabled. */ + gcc_assert (dump_enabled_p ()); + + /* Update the singleton. */ + delete s_the_problem; + s_the_problem = this; + + /* Print the location to the "immediate" dump destinations. */ + dump_context &dc = dump_context::get (); + dc.dump_loc (MSG_MISSED_OPTIMIZATION, loc); + + /* Print the formatted string to this opt_problem's optinfo, dumping + the items to the "immediate" dump destinations, and storing items + for later retrieval. */ + { + dump_pretty_printer pp (&dump_context::get (), MSG_MISSED_OPTIMIZATION); + + text_info text; + text.err_no = errno; + text.args_ptr = ap; + text.format_spec = fmt; /* No i18n is performed. */ + + /* Phases 1 and 2, using pp_format. */ + pp_format (&pp, &text); + + /* Phase 3: dump the items to the "immediate" dump destinations, + and storing them into m_optinfo for later retrieval. */ + pp.emit_items (&m_optinfo); + } +} + +/* Emit this problem and delete it, clearing the current opt_problem. */ + +void +opt_problem::emit_and_clear () +{ + gcc_assert (this == s_the_problem); + + m_optinfo.emit_for_opt_problem (); + + delete this; + s_the_problem = NULL; +} + +/* The singleton opt_problem *. */ + +opt_problem *opt_problem::s_the_problem; + +#if CHECKING_P + +namespace selftest { + +static opt_result +function_that_succeeds () +{ + return opt_result::success (); +} + +/* Verify that opt_result::success works. */ + +static void +test_opt_result_success () +{ + /* Run all tests twice, with and then without dumping enabled. */ + for (int i = 0 ; i < 2; i++) + { + bool with_dumping = (i == 0); + + temp_dump_context tmp (with_dumping, with_dumping, + MSG_ALL_KINDS | MSG_ALL_PRIORITIES); + + if (with_dumping) + gcc_assert (dump_enabled_p ()); + else + gcc_assert (!dump_enabled_p ()); + + opt_result res = function_that_succeeds (); + + /* Verify that "success" can be used as a "true" boolean. */ + ASSERT_TRUE (res); + + /* Verify the underlying opt_wrapper<bool>. */ + ASSERT_TRUE (res.get_result ()); + ASSERT_EQ (res.get_problem (), NULL); + + /* Nothing should have been dumped. */ + ASSERT_DUMPED_TEXT_EQ (tmp, ""); + optinfo *info = tmp.get_pending_optinfo (); + ASSERT_EQ (info, NULL); + } +} + +/* Example of a function that fails, with a non-trivial + pre-canned error message. */ + +static opt_result +function_that_fails (const greturn *stmt) +{ + gcc_assert (stmt); + gcc_assert (gimple_return_retval (stmt)); + + AUTO_DUMP_SCOPE ("function_that_fails", stmt); + + return opt_result::failure_at (stmt, + "can't handle return type: %T for stmt: %G", + TREE_TYPE (gimple_return_retval (stmt)), + static_cast <const gimple *> (stmt)); +} + +/* Example of a function that indirectly fails. */ + +static opt_result +function_that_indirectly_fails (const greturn *stmt) +{ + AUTO_DUMP_SCOPE ("function_that_indirectly_fails", stmt); + + opt_result res = function_that_fails (stmt); + if (!res) + return res; + return opt_result::success (); +} + +/* Verify that opt_result::failure_at works. + Simulate a failure handling a stmt at one location whilst considering + an optimization that's notionally at another location (as a microcosm + of e.g. a problematic statement within a loop that prevents loop + vectorization). */ + +static void +test_opt_result_failure_at (const line_table_case &case_) +{ + /* Generate a location_t for testing. */ + line_table_test ltt (case_); + const line_map_ordinary *ord_map + = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, + "test.c", 0)); + linemap_line_start (line_table, 5, 100); + + /* A test location: "test.c:5:10". */ + const location_t line_5 = linemap_position_for_column (line_table, 10); + + /* Another test location: "test.c:6:12". */ + const location_t line_6 + = linemap_position_for_line_and_column (line_table, ord_map, 6, 12); + + if (line_6 > LINE_MAP_MAX_LOCATION_WITH_COLS) + return; + + /* Generate statements using "line_5" and "line_6" for testing. */ + greturn *stmt_at_5 = gimple_build_return (integer_one_node); + gimple_set_location (stmt_at_5, line_5); + + greturn *stmt_at_6 = gimple_build_return (integer_zero_node); + gimple_set_location (stmt_at_6, line_6); + + /* Run with and then without dumping enabled. */ + for (int i = 0; i < 2; i++) + { + bool with_dumping = (i == 0); + + /* Run with all 4 combinations of + with and without MSG_PRIORITY_INTERNALS and + with and without MSG_PRIORITY_REEMITTED. */ + for (int j = 0; j < 4; j++) + { + dump_flags_t filter = MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING; + if (j / 2) + filter |= MSG_PRIORITY_INTERNALS; + if (j % 2) + filter |= MSG_PRIORITY_REEMITTED; + + temp_dump_context tmp (with_dumping, with_dumping, filter); + + if (with_dumping) + gcc_assert (dump_enabled_p ()); + else + gcc_assert (!dump_enabled_p ()); + + /* Simulate attempting to optimize "stmt_at_6". */ + opt_result res = function_that_indirectly_fails (stmt_at_6); + + /* Verify that "failure" can be used as a "false" boolean. */ + ASSERT_FALSE (res); + + /* Verify the underlying opt_wrapper<bool>. */ + ASSERT_FALSE (res.get_result ()); + opt_problem *problem = res.get_problem (); + + if (with_dumping) + { + ASSERT_NE (problem, NULL); + ASSERT_EQ (problem->get_dump_location ().get_location_t (), + line_6); +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + /* Verify that the problem captures the implementation location + it was emitted from. */ + const dump_impl_location_t &impl_location + = problem->get_dump_location ().get_impl_location (); + ASSERT_STR_CONTAINS (impl_location.m_function, + "function_that_fails"); +#endif + + /* Verify that the underlying dump items are retained in the + opt_problem. */ + const optinfo &info = problem->get_optinfo (); + ASSERT_EQ (info.get_dump_location ().get_location_t (), line_6); + ASSERT_EQ (info.num_items (), 4); + ASSERT_IS_TEXT (info.get_item (0), "can't handle return type: "); + ASSERT_IS_TREE (info.get_item (1), UNKNOWN_LOCATION, "int"); + ASSERT_IS_TEXT (info.get_item (2), " for stmt: "); + ASSERT_IS_GIMPLE (info.get_item (3), line_6, "return 0;\n"); + + /* ...but not in the dump_context's pending_optinfo. */ + ASSERT_EQ (tmp.get_pending_optinfo (), NULL); + + /* Simulate emitting a high-level summary message, followed + by the problem. */ + dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt_at_5, + "can't optimize loop\n"); + problem->emit_and_clear (); + ASSERT_EQ (res.get_problem (), NULL); + + /* Verify that the error message was dumped (when the failure + occurred). We can't use a switch here as not all of the + values are const expressions (using C++98). */ + dump_flags_t effective_filter + = filter & (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED); + if (effective_filter + == (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED)) + /* The -fopt-info-internals case. */ + ASSERT_DUMPED_TEXT_EQ + (tmp, + "test.c:6:12: note: === function_that_indirectly_fails" + " ===\n" + "test.c:6:12: note: === function_that_fails ===\n" + "test.c:6:12: missed: can't handle return type: int" + " for stmt: return 0;\n" + "test.c:5:10: missed: can't optimize loop\n" + "test.c:6:12: missed: can't handle return type: int" + " for stmt: return 0;\n"); + else if (effective_filter == MSG_PRIORITY_INTERNALS) + /* The default for dump files. */ + ASSERT_DUMPED_TEXT_EQ + (tmp, + "test.c:6:12: note: === function_that_indirectly_fails" + " ===\n" + "test.c:6:12: note: === function_that_fails ===\n" + "test.c:6:12: missed: can't handle return type: int" + " for stmt: return 0;\n" + "test.c:5:10: missed: can't optimize loop\n"); + else if (effective_filter == MSG_PRIORITY_REEMITTED) + /* The default when -fopt-info is enabled. */ + ASSERT_DUMPED_TEXT_EQ + (tmp, + "test.c:5:10: missed: can't optimize loop\n" + "test.c:6:12: missed: can't handle return type: int" + " for stmt: return 0;\n"); + else + { + gcc_assert (effective_filter == 0); + ASSERT_DUMPED_TEXT_EQ + (tmp, + "test.c:5:10: missed: can't optimize loop\n"); + } + } + else + { + /* If dumping was disabled, then no problem should have been + created, and nothing should have been dumped. */ + ASSERT_EQ (problem, NULL); + ASSERT_DUMPED_TEXT_EQ (tmp, ""); + } + } + } +} + +/* Run all of the selftests within this file. */ + +void +opt_problem_cc_tests () +{ + test_opt_result_success (); + for_each_line_table_case (test_opt_result_failure_at); +} + +} // namespace selftest + +#endif /* CHECKING_P */ |