(root)/
gcc-13.2.0/
gcc/
opt-problem.h
       1  /* Rich information on why an optimization wasn't possible.
       2     Copyright (C) 2018-2023 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:
     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  
     145  template <typename T>
     146  class 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  
     178  class 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  
     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>
     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  
     287  typedef opt_pointer_wrapper<tree> opt_tree;
     288  
     289  #endif /* #ifndef GCC_OPT_PROBLEM_H */