]>
Commit | Line | Data |
---|---|---|
1 | /* Rich information on why an optimization wasn't possible. | |
2 | Copyright (C) 2018-2020 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 */ |