Line data Source code
1 : /* Rich information on why an optimization wasn't possible.
2 : Copyright (C) 2018-2026 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 25672 : class opt_problem
120 : {
121 : public:
122 835441 : 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 102758955 : operator wrapped_t () const { return m_result; }
153 :
154 : /* No public ctor. */
155 :
156 : wrapped_t get_result () const { return m_result; }
157 6274 : opt_problem *get_problem () const { return opt_problem::get_singleton (); }
158 :
159 : protected:
160 116779945 : 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 108274346 : 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 7322507 : static opt_result failure_at (const dump_location_t &loc,
189 : const char *fmt, ...)
190 : ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
191 : {
192 7322507 : opt_problem *problem = NULL;
193 7322507 : if (dump_enabled_p ())
194 : {
195 26122 : va_list ap;
196 26122 : va_start (ap, fmt);
197 26122 : problem = new opt_problem (loc, fmt, &ap);
198 26122 : va_end (ap);
199 : }
200 7322507 : 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 115596853 : opt_result (wrapped_t result, opt_problem *problem)
217 108274346 : : 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 330885 : success (wrapped_pointer_t ptr)
238 : {
239 330885 : 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 23040 : failure_at (const dump_location_t &loc,
247 : const char *fmt, ...)
248 : ATTRIBUTE_GCC_DUMP_PRINTF (2, 3)
249 : {
250 23040 : opt_problem *problem = NULL;
251 23040 : if (dump_enabled_p ())
252 : {
253 : va_list ap;
254 575 : va_start (ap, fmt);
255 575 : problem = new opt_problem (loc, fmt, &ap);
256 575 : va_end (ap);
257 : }
258 23040 : 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 829167 : propagate_failure (opt_wrapper <S> other)
267 : {
268 829167 : return opt_pointer_wrapper <wrapped_pointer_t> (NULL,
269 : other.get_problem ());
270 : }
271 :
272 : /* Support accessing the underlying pointer via ->. */
273 :
274 212726 : 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 1183092 : opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)
280 1160052 : : 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 */
|