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.h | |
parent | 7db960c5b6adad2fd11789870aa514985ea0da04 (diff) | |
download | gcc-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.h | 289 |
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 */ |