Home | History | Annotate | Line # | Download | only in gcc
      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