131
|
1 /* Rich information on why an optimization wasn't possible.
|
145
|
2 Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
131
|
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 */
|