1 /* Rich information on why an optimization wasn't possible.
2    Copyright (C) 2018-2019 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:
get_singleton()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 &
get_dump_location()129   get_dump_location () const { return m_optinfo.get_dump_location (); }
130 
get_optinfo()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.  */
wrapped_t()152   operator wrapped_t () const { return m_result; }
153 
154   /* No public ctor.  */
155 
get_result()156   wrapped_t get_result () const { return m_result; }
get_problem()157   opt_problem *get_problem () const { return opt_problem::get_singleton (); }
158 
159  protected:
opt_wrapper(wrapped_t result,opt_problem *)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 
success()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 
failure_at(const dump_location_t & loc,const char * fmt,...)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
propagate_failure(opt_wrapper<S> other)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.  */
opt_result(wrapped_t result,opt_problem * problem)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>
success(wrapped_pointer_t ptr)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>
failure_at(const dump_location_t & loc,const char * fmt,...)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>
propagate_failure(opt_wrapper<S> other)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".  */
opt_pointer_wrapper(wrapped_pointer_t result,opt_problem * problem)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