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