aboutsummaryrefslogtreecommitdiff
path: root/gcc/opt-problem.h
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.h
parent7db960c5b6adad2fd11789870aa514985ea0da04 (diff)
downloadgcc-f4ebbd243f887b3c5e01c65ad80a8f64a8261e61.zip
gcc-f4ebbd243f887b3c5e01c65ad80a8f64a8261e61.tar.gz
gcc-f4ebbd243f887b3c5e01c65ad80a8f64a8261e61.tar.bz2
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.h')
-rw-r--r--gcc/opt-problem.h289
1 files changed, 289 insertions, 0 deletions
diff --git a/gcc/opt-problem.h b/gcc/opt-problem.h
new file mode 100644
index 0000000..68d7e4a
--- /dev/null
+++ b/gcc/opt-problem.h
@@ -0,0 +1,289 @@
+/* Rich information on why an optimization wasn't possible.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_OPT_PROBLEM_H
+#define GCC_OPT_PROBLEM_H
+
+#include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG. */
+#include "optinfo.h" /* for optinfo. */
+
+/* This header declares a family of wrapper classes for tracking a
+ success/failure value, while optionally supporting propagating an
+ opt_problem * describing any failure back up the call stack.
+
+ For instance, at the deepest point of the callstack where the failure
+ happens, rather than:
+
+ if (!check_something ())
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "foo is unsupported.\n");
+ return false;
+ }
+ // [...more checks...]
+
+ // All checks passed:
+ return true;
+
+ we can capture the cause of the failure via:
+
+ if (!check_something ())
+ return opt_result::failure_at (stmt, "foo is unsupported");
+ // [...more checks...]
+
+ // All checks passed:
+ return opt_result::success ();
+
+ which effectively returns true or false, whilst recording any problem.
+
+ opt_result::success and opt_result::failure return opt_result values
+ which "looks like" true/false respectively, via operator bool().
+ If dump_enabled_p, then opt_result::failure also creates an opt_problem *,
+ capturing the pertinent data (here, "foo is unsupported " and "stmt").
+ If dumps are disabled, then opt_problem instances aren't
+ created, and it's equivalent to just returning a bool.
+
+ The opt_problem can be propagated via opt_result values back up
+ the call stack to where it makes most sense to the user.
+ For instance, rather than:
+
+ bool ok = try_something_that_might_fail ();
+ if (!ok)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "some message.\n");
+ return false;
+ }
+
+ we can replace the bool with an opt_result, so if dump_enabled_p, we
+ assume that if try_something_that_might_fail, an opt_problem * will be
+ created, and we can propagate it up the call chain:
+
+ opt_result ok = try_something_that_might_fail ();
+ if (!ok)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "some message.\n");
+ return ok; // propagating the opt_result
+ }
+
+ opt_result is an opt_wrapper<bool>, where opt_wrapper<T> is a base
+ class for wrapping a T, optionally propagating an opt_problem in
+ case of failure_at (when dumps are enabled). Similarly,
+ opt_pointer_wrapper<T> can be used to wrap pointer types (where non-NULL
+ signifies success, NULL signifies failure).
+
+ In all cases, opt_wrapper<T> acts as if the opt_problem were one of its
+ fields, but the opt_problem is actually stored in a global, so that when
+ compiled, an opt_wrapper<T> is effectively just a T, so that we're
+ still just passing e.g. a bool around; the opt_wrapper<T> classes
+ simply provide type-checking and an API to ensure that we provide
+ error-messages deep in the callstack at the places where problems
+ occur, and that we propagate them. This also avoids having
+ to manage the ownership of the opt_problem instances.
+
+ Using opt_result and opt_wrapper<T> documents the intent of the code
+ for the places where we represent success values, and allows the C++ type
+ system to track where the deepest points in the callstack are where we
+ need to emit the failure messages from. */
+
+/* A bundle of information about why an optimization failed (e.g.
+ vectorization), and the location in both the user's code and
+ in GCC itself where the problem occurred.
+
+ Instances are created by static member functions in opt_wrapper
+ subclasses, such as opt_result::failure.
+
+ Instances are only created when dump_enabled_p (). */
+
+class opt_problem
+{
+ public:
+ static opt_problem *get_singleton () { return s_the_problem; }
+
+ opt_problem (const dump_location_t &loc,
+ const char *fmt, va_list *ap)
+ ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
+
+ const dump_location_t &
+ get_dump_location () const { return m_optinfo.get_dump_location (); }
+
+ const optinfo & get_optinfo () const { return m_optinfo; }
+
+ void emit_and_clear ();
+
+ private:
+ optinfo m_optinfo;
+
+ static opt_problem *s_the_problem;
+};
+
+/* A base class for wrapper classes that track a success/failure value, while
+ optionally supporting propagating an opt_problem * describing any
+ failure back up the call stack. */
+
+template <typename T>
+class opt_wrapper
+{
+ public:
+ typedef T wrapped_t;
+
+ /* Be accessible as the wrapped type. */
+ operator wrapped_t () const { return m_result; }
+
+ /* No public ctor. */
+
+ wrapped_t get_result () const { return m_result; }
+ opt_problem *get_problem () const { return opt_problem::get_singleton (); }
+
+ protected:
+ opt_wrapper (wrapped_t result, opt_problem */*problem*/)
+ : m_result (result)
+ {
+ /* "problem" is ignored: although it looks like a field, we
+ actually just use the opt_problem singleton, so that
+ opt_wrapper<T> in memory is just a T. */
+ }
+
+ private:
+ wrapped_t m_result;
+};
+
+/* Subclass of opt_wrapper<T> for bool, where
+ - true signifies "success", and
+ - false signifies "failure"
+ whilst effectively propagating an opt_problem * describing any failure
+ back up the call stack. */
+
+class opt_result : public opt_wrapper <bool>
+{
+ public:
+ /* Generate a "success" value: a wrapper around "true". */
+
+ static opt_result success () { return opt_result (true, NULL); }
+
+ /* Generate a "failure" value: a wrapper around "false", and,
+ if dump_enabled_p, an opt_problem. */
+
+ static opt_result failure_at (const dump_location_t &loc,
+ const char *fmt, ...)
+ ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
+ {
+ opt_problem *problem = NULL;
+ if (dump_enabled_p ())
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ problem = new opt_problem (loc, fmt, &ap);
+ va_end (ap);
+ }
+ return opt_result (false, problem);
+ }
+
+ /* Given a failure wrapper of some other kind, make an opt_result failure
+ object, for propagating the opt_problem up the call stack. */
+
+ template <typename S>
+ static opt_result
+ propagate_failure (opt_wrapper <S> other)
+ {
+ return opt_result (false, other.get_problem ());
+ }
+
+ private:
+ /* Private ctor. Instances should be created by the success and failure
+ static member functions. */
+ opt_result (wrapped_t result, opt_problem *problem)
+ : opt_wrapper (result, problem)
+ {}
+};
+
+/* Subclass of opt_wrapper<T> where T is a pointer type, for tracking
+ success/failure, where:
+ - a non-NULL value signifies "success", and
+ - a NULL value signifies "failure",
+ whilst effectively propagating an opt_problem * describing any failure
+ back up the call stack. */
+
+template <typename PtrType_t>
+class opt_pointer_wrapper : public opt_wrapper <PtrType_t>
+{
+ public:
+ typedef PtrType_t wrapped_pointer_t;
+
+ /* Given a non-NULL pointer, make a success object wrapping it. */
+
+ static opt_pointer_wrapper <wrapped_pointer_t>
+ success (wrapped_pointer_t ptr)
+ {
+ return opt_pointer_wrapper <wrapped_pointer_t> (ptr, NULL);
+ }
+
+ /* Make a NULL pointer failure object, with the given message
+ (if dump_enabled_p). */
+
+ static opt_pointer_wrapper <wrapped_pointer_t>
+ failure_at (const dump_location_t &loc,
+ const char *fmt, ...)
+ ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
+ {
+ opt_problem *problem = NULL;
+ if (dump_enabled_p ())
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ problem = new opt_problem (loc, fmt, &ap);
+ va_end (ap);
+ }
+ return opt_pointer_wrapper <wrapped_pointer_t> (NULL, problem);
+ }
+
+ /* Given a failure wrapper of some other kind, make a NULL pointer
+ failure object, propagating the problem. */
+
+ template <typename S>
+ static opt_pointer_wrapper <wrapped_pointer_t>
+ propagate_failure (opt_wrapper <S> other)
+ {
+ return opt_pointer_wrapper <wrapped_pointer_t> (NULL,
+ other.get_problem ());
+ }
+
+ /* Support accessing the underlying pointer via ->. */
+
+ wrapped_pointer_t operator-> () const { return this->get_result (); }
+
+ private:
+ /* Private ctor. Instances should be built using the static member
+ functions "success" and "failure". */
+ opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)
+ : opt_wrapper<PtrType_t> (result, problem)
+ {}
+};
+
+/* A typedef for wrapping "tree" so that NULL_TREE can carry an
+ opt_problem describing the failure (if dump_enabled_p). */
+
+typedef opt_pointer_wrapper<tree> opt_tree;
+
+#endif /* #ifndef GCC_OPT_PROBLEM_H */