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