1 1.1 mrg /* Rich information on why an optimization wasn't possible. 2 1.1.1.3 mrg Copyright (C) 2018-2022 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