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
This commit is contained in:
David Malcolm 2018-10-04 17:50:52 +00:00 committed by David Malcolm
parent 7db960c5b6
commit f4ebbd243f
24 changed files with 1585 additions and 794 deletions

View File

@ -1,3 +1,170 @@
2018-10-04 David Malcolm <dmalcolm@redhat.com>
* 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 <dmalcolm@redhat.com>
* doc/invoke.texi (-fopt-info): Document new "internals"

View File

@ -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 \

View File

@ -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<stashed_item> 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 */

View File

@ -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<stashed_item> 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");
{

View File

@ -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. */

335
gcc/opt-problem.cc Normal file
View File

@ -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 */

289
gcc/opt-problem.h Normal file
View File

@ -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 */

View File

@ -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);

View File

@ -89,11 +89,51 @@ optinfo::add_item (optinfo_item *item)
m_items.safe_push (item);
}
/* Emit the optinfo to all of the "non-immediate" destinations
(emission to "immediate" destinations is done by emit_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 ()
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
dump_context::emit_item). */
void
optinfo::emit () const
{
/* -fsave-optimization-record. */
optimization_records_maybe_record_optinfo (this);

View File

@ -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. */

View File

@ -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 ();

View File

@ -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 ();

View File

@ -1,3 +1,11 @@
2018-10-04 David Malcolm <dmalcolm@redhat.com>
* 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 <dmalcolm@redhat.com>
* gcc.dg/plugin/dump-1.c: Update expected output for test_scopes

View File

@ -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;
}

View File

@ -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" } } */

View File

@ -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<data_reference_p> *datarefs)
{
unsigned i;
auto_vec<data_ref_loc, 2> 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

View File

@ -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<loop_p> *,
vec<data_reference_p> *,
@ -443,8 +445,8 @@ extern void free_dependence_relation (struct data_dependence_relation *);
extern void free_dependence_relations (vec<ddr_p> );
extern void free_data_ref (data_reference_p);
extern void free_data_refs (vec<data_reference_p> );
extern bool find_data_references_in_stmt (struct loop *, gimple *,
vec<data_reference_p> *);
extern opt_result find_data_references_in_stmt (struct loop *, gimple *,
vec<data_reference_p> *);
extern bool graphite_find_data_references_in_stmt (edge, loop_p, gimple *,
vec<data_reference_p> *);
tree find_data_references_in_loop (struct loop *, vec<data_reference_p> *);
@ -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<dr_with_seg_len_pair_t> *,
poly_uint64);

View File

@ -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))

View File

@ -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<data_reference_p> 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<data_reference_p> 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_operand_hash, tree_operand_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 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 true;
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<data_reference_p> *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<data_reference_p, 2> 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 <gcall *> (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 <loop_vec_info> (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 <loop_vec_info> (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 ();
}

View File

@ -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<stmt_vec_info > *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))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
"not vectorized: Bad inner loop.\n");
return false;
}
if (!expr_invariant_in_loop_p (loop, inner_niter))
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,
"not vectorized: inner-loop count not"
" invariant.\n");
return false;
"not vectorized: Bad inner loop.\n");
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))
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 <gimple *> (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<data_reference_p> *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++];

View File

@ -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 ();
}

View File

@ -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<stmt_vec_info> *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 <gcall *> (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);
}

View File

@ -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> *&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

View File

@ -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 <loop_vec_info> 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<data_reference_p> *);
extern bool vect_analyze_data_refs (vec_info *, poly_uint64 *);
extern opt_result vect_find_stmt_data_reference (loop_p, gimple *,
vec<data_reference_p> *);
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<tree> ,
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<tree> , slp_tree, vec<vec<tree> > *);