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