123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- /* Rich information on why an optimization wasn't possible.
- Copyright (C) 2018-2019 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 <bool> (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 */
|