opt-problem.h 9.2 KB

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