/* Rich information on why an optimization wasn't possible. Copyright (C) 2018-2024 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. */ #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, where opt_wrapper 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 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 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 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: /* 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 static opt_result propagate_failure (opt_wrapper 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 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 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 (if dump_enabled_p). */ static opt_pointer_wrapper 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 (NULL, problem); } /* Given a failure wrapper of some other kind, make a NULL pointer failure object, propagating the problem. */ template static opt_pointer_wrapper propagate_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) {} }; /* 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 opt_tree; #endif /* #ifndef GCC_OPT_PROBLEM_H */