opt-problem.h revision 1.1.1.2
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 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License 18along 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 119class 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 145template <typename T> 146class 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 178class 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 228template <typename PtrType_t> 229class 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 287typedef opt_pointer_wrapper<tree> opt_tree; 288 289#endif /* #ifndef GCC_OPT_PROBLEM_H */ 290