Branch data Line data Source code
1 : : /* Rich information on why an optimization wasn't possible.
2 : : Copyright (C) 2018-2024 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #ifndef GCC_OPT_PROBLEM_H
22 : : #define GCC_OPT_PROBLEM_H
23 : :
24 : : #include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG. */
25 : : #include "optinfo.h" /* for optinfo. */
26 : :
27 : : /* This header declares a family of wrapper classes for tracking a
28 : : success/failure value, while optionally supporting propagating an
29 : : opt_problem * describing any failure back up the call stack.
30 : :
31 : : For instance, at the deepest point of the callstack where the failure
32 : : happens, rather than:
33 : :
34 : : if (!check_something ())
35 : : {
36 : : if (dump_enabled_p ())
37 : : dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
38 : : "foo is unsupported.\n");
39 : : return false;
40 : : }
41 : : // [...more checks...]
42 : :
43 : : // All checks passed:
44 : : return true;
45 : :
46 : : we can capture the cause of the failure via:
47 : :
48 : : if (!check_something ())
49 : : return opt_result::failure_at (stmt, "foo is unsupported");
50 : : // [...more checks...]
51 : :
52 : : // All checks passed:
53 : : return opt_result::success ();
54 : :
55 : : which effectively returns true or false, whilst recording any problem.
56 : :
57 : : opt_result::success and opt_result::failure return opt_result values
58 : : which "looks like" true/false respectively, via operator bool().
59 : : If dump_enabled_p, then opt_result::failure also creates an opt_problem *,
60 : : capturing the pertinent data (here, "foo is unsupported " and "stmt").
61 : : If dumps are disabled, then opt_problem instances aren't
62 : : created, and it's equivalent to just returning a bool.
63 : :
64 : : The opt_problem can be propagated via opt_result values back up
65 : : the call stack to where it makes most sense to the user.
66 : : For instance, rather than:
67 : :
68 : : bool ok = try_something_that_might_fail ();
69 : : if (!ok)
70 : : {
71 : : if (dump_enabled_p ())
72 : : dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
73 : : "some message.\n");
74 : : return false;
75 : : }
76 : :
77 : : we can replace the bool with an opt_result, so if dump_enabled_p, we
78 : : assume that if try_something_that_might_fail, an opt_problem * will be
79 : : created, and we can propagate it up the call chain:
80 : :
81 : : opt_result ok = try_something_that_might_fail ();
82 : : if (!ok)
83 : : {
84 : : if (dump_enabled_p ())
85 : : dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
86 : : "some message.\n");
87 : : return ok; // propagating the opt_result
88 : : }
89 : :
90 : : opt_result is an opt_wrapper<bool>, where opt_wrapper<T> is a base
91 : : class for wrapping a T, optionally propagating an opt_problem in
92 : : case of failure_at (when dumps are enabled). Similarly,
93 : : opt_pointer_wrapper<T> can be used to wrap pointer types (where non-NULL
94 : : signifies success, NULL signifies failure).
95 : :
96 : : In all cases, opt_wrapper<T> acts as if the opt_problem were one of its
97 : : fields, but the opt_problem is actually stored in a global, so that when
98 : : compiled, an opt_wrapper<T> is effectively just a T, so that we're
99 : : still just passing e.g. a bool around; the opt_wrapper<T> classes
100 : : simply provide type-checking and an API to ensure that we provide
101 : : error-messages deep in the callstack at the places where problems
102 : : occur, and that we propagate them. This also avoids having
103 : : to manage the ownership of the opt_problem instances.
104 : :
105 : : Using opt_result and opt_wrapper<T> documents the intent of the code
106 : : for the places where we represent success values, and allows the C++ type
107 : : system to track where the deepest points in the callstack are where we
108 : : need to emit the failure messages from. */
109 : :
110 : : /* A bundle of information about why an optimization failed (e.g.
111 : : vectorization), and the location in both the user's code and
112 : : in GCC itself where the problem occurred.
113 : :
114 : : Instances are created by static member functions in opt_wrapper
115 : : subclasses, such as opt_result::failure.
116 : :
117 : : Instances are only created when dump_enabled_p (). */
118 : :
119 : 30358 : class opt_problem
120 : : {
121 : : public:
122 : 699211 : static opt_problem *get_singleton () { return s_the_problem; }
123 : :
124 : : opt_problem (const dump_location_t &loc,
125 : : const char *fmt, va_list *ap)
126 : : ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
127 : :
128 : : const dump_location_t &
129 : 336 : get_dump_location () const { return m_optinfo.get_dump_location (); }
130 : :
131 : 168 : const optinfo & get_optinfo () const { return m_optinfo; }
132 : :
133 : : void emit_and_clear ();
134 : :
135 : : private:
136 : : optinfo m_optinfo;
137 : :
138 : : static opt_problem *s_the_problem;
139 : : };
140 : :
141 : : /* A base class for wrapper classes that track a success/failure value, while
142 : : optionally supporting propagating an opt_problem * describing any
143 : : failure back up the call stack. */
144 : :
145 : : template <typename T>
146 : : class opt_wrapper
147 : : {
148 : : public:
149 : : typedef T wrapped_t;
150 : :
151 : : /* Be accessible as the wrapped type. */
152 : 100854244 : operator wrapped_t () const { return m_result; }
153 : :
154 : : /* No public ctor. */
155 : :
156 : : wrapped_t get_result () const { return m_result; }
157 : 5882 : opt_problem *get_problem () const { return opt_problem::get_singleton (); }
158 : :
159 : : protected:
160 : 113642096 : opt_wrapper (wrapped_t result, opt_problem */*problem*/)
161 : : : m_result (result)
162 : : {
163 : : /* "problem" is ignored: although it looks like a field, we
164 : : actually just use the opt_problem singleton, so that
165 : : opt_wrapper<T> in memory is just a T. */
166 : : }
167 : :
168 : : private:
169 : : wrapped_t m_result;
170 : : };
171 : :
172 : : /* Subclass of opt_wrapper<T> for bool, where
173 : : - true signifies "success", and
174 : : - false signifies "failure"
175 : : whilst effectively propagating an opt_problem * describing any failure
176 : : back up the call stack. */
177 : :
178 : : class opt_result : public opt_wrapper <bool>
179 : : {
180 : : public:
181 : : /* Generate a "success" value: a wrapper around "true". */
182 : :
183 : 106033681 : static opt_result success () { return opt_result (true, NULL); }
184 : :
185 : : /* Generate a "failure" value: a wrapper around "false", and,
186 : : if dump_enabled_p, an opt_problem. */
187 : :
188 : 6647583 : static opt_result failure_at (const dump_location_t &loc,
189 : : const char *fmt, ...)
190 : : ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
191 : : {
192 : 6647583 : opt_problem *problem = NULL;
193 : 6647583 : if (dump_enabled_p ())
194 : : {
195 : 31252 : va_list ap;
196 : 31252 : va_start (ap, fmt);
197 : 31252 : problem = new opt_problem (loc, fmt, &ap);
198 : 31252 : va_end (ap);
199 : : }
200 : 6647583 : return opt_result (false, problem);
201 : : }
202 : :
203 : : /* Given a failure wrapper of some other kind, make an opt_result failure
204 : : object, for propagating the opt_problem up the call stack. */
205 : :
206 : : template <typename S>
207 : : static opt_result
208 : : propagate_failure (opt_wrapper <S> other)
209 : : {
210 : : return opt_result (false, other.get_problem ());
211 : : }
212 : :
213 : : private:
214 : : /* Private ctor. Instances should be created by the success and failure
215 : : static member functions. */
216 : 112681264 : opt_result (wrapped_t result, opt_problem *problem)
217 : 106033681 : : opt_wrapper <bool> (result, problem)
218 : : {}
219 : : };
220 : :
221 : : /* Subclass of opt_wrapper<T> where T is a pointer type, for tracking
222 : : success/failure, where:
223 : : - a non-NULL value signifies "success", and
224 : : - a NULL value signifies "failure",
225 : : whilst effectively propagating an opt_problem * describing any failure
226 : : back up the call stack. */
227 : :
228 : : template <typename PtrType_t>
229 : : class opt_pointer_wrapper : public opt_wrapper <PtrType_t>
230 : : {
231 : : public:
232 : : typedef PtrType_t wrapped_pointer_t;
233 : :
234 : : /* Given a non-NULL pointer, make a success object wrapping it. */
235 : :
236 : : static opt_pointer_wrapper <wrapped_pointer_t>
237 : 247852 : success (wrapped_pointer_t ptr)
238 : : {
239 : 247852 : return opt_pointer_wrapper <wrapped_pointer_t> (ptr, NULL);
240 : : }
241 : :
242 : : /* Make a NULL pointer failure object, with the given message
243 : : (if dump_enabled_p). */
244 : :
245 : : static opt_pointer_wrapper <wrapped_pointer_t>
246 : 19651 : failure_at (const dump_location_t &loc,
247 : : const char *fmt, ...)
248 : : ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
249 : : {
250 : 19651 : opt_problem *problem = NULL;
251 : 19651 : if (dump_enabled_p ())
252 : : {
253 : : va_list ap;
254 : 497 : va_start (ap, fmt);
255 : 497 : problem = new opt_problem (loc, fmt, &ap);
256 : 497 : va_end (ap);
257 : : }
258 : 19651 : return opt_pointer_wrapper <wrapped_pointer_t> (NULL, problem);
259 : : }
260 : :
261 : : /* Given a failure wrapper of some other kind, make a NULL pointer
262 : : failure object, propagating the problem. */
263 : :
264 : : template <typename S>
265 : : static opt_pointer_wrapper <wrapped_pointer_t>
266 : 693329 : propagate_failure (opt_wrapper <S> other)
267 : : {
268 : 693329 : return opt_pointer_wrapper <wrapped_pointer_t> (NULL,
269 : : other.get_problem ());
270 : : }
271 : :
272 : : /* Support accessing the underlying pointer via ->. */
273 : :
274 : 188732 : wrapped_pointer_t operator-> () const { return this->get_result (); }
275 : :
276 : : private:
277 : : /* Private ctor. Instances should be built using the static member
278 : : functions "success" and "failure". */
279 : 960832 : opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)
280 : 941181 : : opt_wrapper<PtrType_t> (result, problem)
281 : : {}
282 : : };
283 : :
284 : : /* A typedef for wrapping "tree" so that NULL_TREE can carry an
285 : : opt_problem describing the failure (if dump_enabled_p). */
286 : :
287 : : typedef opt_pointer_wrapper<tree> opt_tree;
288 : :
289 : : #endif /* #ifndef GCC_OPT_PROBLEM_H */
|