From 34c864bc9befde9d6d79c3bb85d38aab83796920 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 9 Jul 2018 17:34:25 -0400 Subject: [PATCH 28/68] Add opt-problem.h gcc/ChangeLog: * opt-problem.h: New file. * tree-vectorizer.h (opt_loop_vec_info): New typedef. --- gcc/opt-problem.h | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/tree-vectorizer.h | 6 + 2 files changed, 332 insertions(+) create mode 100644 gcc/opt-problem.h diff --git a/gcc/opt-problem.h b/gcc/opt-problem.h new file mode 100644 index 0000000..100eed0 --- /dev/null +++ b/gcc/opt-problem.h @@ -0,0 +1,326 @@ +/* Rich information on why an optimization wasn't possible. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#ifndef GCC_OPT_PROBLEM_H +#define GCC_OPT_PROBLEM_H + +#include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG. */ + +/* 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 ()) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "foo is unsupported.\n"); + return opt_result::failure ("foo is unsupported", + stmt); + } + // [...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, where opt_wrapper is a base + class for wrapping a T, optionally propagating an opt_problem in + case of failure (when dumps are enabled). Similarly, + opt_pointer_wrapper can be used to wrap pointer types (where non-NULL + signifies success, NULL signifies failure). + + In all cases, opt_wrapper 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 is effectively just a T, so that we're + still just passing e.g. a bool around; the opt_wrapper 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 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 high-level optimization-reporting API, with responsibility for grouping + the remark and any notes into one optimization record. + + For example, in: + + if (!loop_vinfo) + if (loop_vinfo.get_problem ()) + { + opt_report report; + if (report.remark (vect_location, "couldn't vectorize loop")) + loop_vinfo.get_problem ()->report_reason (report); + } + + any "note" diagnostics emitted by report_reason can be grouped into + the "remark" when written out as an optimization record. + + TODO: notes on user-facing, i18n, etc. */ + +class opt_report +{ +public: + bool remark (dump_location_t, const char *, ...) + ATTRIBUTE_GCC_DIAG(3,4); + + bool note (dump_location_t, const char *, ...) + ATTRIBUTE_GCC_DIAG(3,4); +}; + +/* 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 GTY(()) opt_problem +{ + public: + /* Factory function, which only makes an opt_problem if dumps are + enabled. */ + static opt_problem * + make (const char *text, gimple *stmt, + const dump_impl_location_t &impl_location) + { + if (!dump_enabled_p ()) + return NULL; + return new opt_problem (text, stmt, impl_location); + } + + void report_reason (opt_report &report); + + static opt_problem *get_singleton () { return s_the_problem; } + + private: + /* Private constructor, to be called by "make" member function. + Keeping this private ensures that instances are only created if + dump_enabled_p (). */ + opt_problem (const char *text, gimple *stmt, + const dump_impl_location_t &impl_location) + : m_text (text), m_stmt (stmt), + m_location (dump_location_t (stmt, impl_location)) + { + /* 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; + } + + const char *m_text; + gimple *m_stmt; + dump_location_t m_location; + + 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 +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 in memory is just a T. */ + } + + private: + wrapped_t m_result; +}; + +/* Subclass of opt_wrapper 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 +{ + public: + /* Deprecated ctor. During transition, allow construction from bool. + We want to eliminate this, as it doesn't capture the reason for + failures. */ + opt_result (bool result, + const dump_impl_location_t &impl_location + = dump_impl_location_t ()) + : opt_wrapper (result, + opt_problem::make ("UNKNOWN", NULL, impl_location)) + { + } + + static opt_result success () { return opt_result (true, NULL); } + static opt_result failure (const char *gmsgid, gimple *stmt, + const dump_impl_location_t &impl_location + = dump_impl_location_t ()) + { + return opt_result (false, + opt_problem::make (gmsgid, stmt, impl_location)); + } + + static opt_result bad_type (const char *gmsgid, gimple *stmt, tree /*type*/, + const dump_impl_location_t &impl_location + = dump_impl_location_t ()) + { + return failure (gmsgid, stmt, impl_location); + // FIXME: for now; ideally we ought to capture the type + } + + 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 where T is 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 +class opt_pointer_wrapper : public opt_wrapper +{ + public: + typedef PtrType_t wrapped_pointer_t; + + /* Given a non-NULL pointer, make a success object wrapping it. */ + + static opt_pointer_wrapper + success (wrapped_pointer_t ptr) + { + return opt_pointer_wrapper (ptr, NULL); + } + + /* Make a NULL pointer failure object, with the given message. */ + + static opt_pointer_wrapper + failure (const char *gmsgid, + const dump_impl_location_t &impl_location + = dump_impl_location_t ()) + { + opt_problem *problem = opt_problem::make (gmsgid, NULL, impl_location); + return opt_pointer_wrapper (NULL, problem); + } + + /* Given a failure wrapper of some other kind, make a NULL pointer + failure object, propagating the problem. */ + + template + static opt_pointer_wrapper + failure (opt_wrapper other) + { + return opt_pointer_wrapper (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 (result, problem) + {} +}; + +#endif /* #ifndef GCC_OPT_PROBLEM_H */ diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h index 28be41f..81b64c6 100644 --- a/gcc/tree-vectorizer.h +++ b/gcc/tree-vectorizer.h @@ -595,6 +595,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 opt_loop_vec_info; + static inline loop_vec_info loop_vec_info_for_loop (struct loop *loop) { -- 1.8.5.3