summaryrefslogtreecommitdiff
path: root/gcc/opt-problem.cc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2018-10-04 17:50:52 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2018-10-04 17:50:52 +0000
commitf4ebbd243f887b3c5e01c65ad80a8f64a8261e61 (patch)
treef76bbe59cb30638b7432efe17c3ddd67c6378b9d /gcc/opt-problem.cc
parent7db960c5b6adad2fd11789870aa514985ea0da04 (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.cc335
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 */