[1/5] Add opt-problem.h

Message ID 1531185096-17113-2-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • Higher-level reporting of vectorization problems
Related show

Commit Message

David Malcolm July 10, 2018, 1:11 a.m.
gcc/ChangeLog:
	* opt-problem.h: New file.
	* tree-vectorizer.h (opt_loop_vec_info): New typedef.
---
 gcc/opt-problem.h     | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/tree-vectorizer.h |   6 +
 2 files changed, 332 insertions(+)
 create mode 100644 gcc/opt-problem.h

Patch

diff --git a/gcc/opt-problem.h b/gcc/opt-problem.h
new file mode 100644
index 0000000..100eed0
--- /dev/null
+++ b/gcc/opt-problem.h
@@ -0,0 +1,326 @@ 
+/* Rich information on why an optimization wasn't possible.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_OPT_PROBLEM_H
+#define GCC_OPT_PROBLEM_H
+
+#include "diagnostic-core.h" /* for ATTRIBUTE_GCC_DIAG.  */
+
+/* This header declares a family of wrapper classes for tracking a
+   success/failure value, while optionally supporting propagating an
+   opt_problem * describing any failure back up the call stack.
+
+   For instance, at the deepest point of the callstack where the failure
+   happens, rather than:
+
+     if (!check_something ())
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "foo is unsupported.\n");
+         return false;
+       }
+     // [...more checks...]
+
+     // All checks passed:
+     return true;
+
+   we can capture the cause of the failure via:
+
+     if (!check_something ())
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "foo is unsupported.\n");
+         return opt_result::failure ("foo is unsupported",
+                                     stmt);
+       }
+     // [...more checks...]
+
+     // All checks passed:
+     return opt_result::success ();
+
+   which effectively returns true or false, whilst recording any problem.
+
+   opt_result::success and opt_result::failure return opt_result values
+   which "looks like" true/false respectively, via operator bool().
+   If dump_enabled_p, then opt_result::failure also creates an opt_problem *,
+   capturing the pertinent data (here, "foo is unsupported " and "stmt").
+   If dumps are disabled, then opt_problem instances aren't
+   created, and it's equivalent to just returning a bool.
+
+   The opt_problem can be propagated via opt_result values back up
+   the call stack to where it makes most sense to the user.
+   For instance, rather than:
+
+     bool ok = try_something_that_might_fail ();
+     if (!ok)
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "some message.\n");
+         return false;
+       }
+
+   we can replace the bool with an opt_result, so if dump_enabled_p, we
+   assume that if try_something_that_might_fail, an opt_problem * will be
+   created, and we can propagate it up the call chain:
+
+     opt_result ok = try_something_that_might_fail ();
+     if (!ok)
+       {
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                            "some message.\n");
+         return ok; // propagating the opt_result
+       }
+
+   opt_result is an opt_wrapper<bool>, where opt_wrapper<T> is a base
+   class for wrapping a T, optionally propagating an opt_problem in
+   case of failure (when dumps are enabled).  Similarly,
+   opt_pointer_wrapper<T> can be used to wrap pointer types (where non-NULL
+   signifies success, NULL signifies failure).
+
+   In all cases, opt_wrapper<T> acts as if the opt_problem were one of its
+   fields, but the opt_problem is actually stored in a global, so that when
+   compiled, an opt_wrapper<T> is effectively just a T, so that we're
+   still just passing e.g. a bool around; the opt_wrapper<T> classes
+   simply provide type-checking and an API to ensure that we provide
+   error-messages deep in the callstack at the places where problems
+   occur, and that we propagate them.  This also avoids having
+   to manage the ownership of the opt_problem instances.
+
+   Using opt_result and opt_wrapper<T> documents the intent of the code
+   for the places where we represent success values, and allows the C++ type
+   system to track where the deepest points in the callstack are where we
+   need to emit the failure messages from.  */
+
+/* A high-level optimization-reporting API, with responsibility for grouping
+   the remark and any notes into one optimization record.
+
+   For example, in:
+
+     if (!loop_vinfo)
+       if (loop_vinfo.get_problem ())
+         {
+           opt_report report;
+           if (report.remark (vect_location, "couldn't vectorize loop"))
+             loop_vinfo.get_problem ()->report_reason (report);
+         }
+
+   any "note" diagnostics emitted by report_reason can be grouped into
+   the "remark" when written out as an optimization record.
+
+   TODO: notes on user-facing, i18n, etc.  */
+
+class opt_report
+{
+public:
+  bool remark (dump_location_t, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
+
+  bool note (dump_location_t, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
+};
+
+/* A bundle of information about why an optimization failed (e.g.
+   vectorization), and the location in both the user's code and
+   in GCC itself where the problem occurred.
+
+   Instances are created by static member functions in opt_wrapper
+   subclasses, such as opt_result::failure.
+
+   Instances are only created when dump_enabled_p ().  */
+
+class GTY(()) opt_problem
+{
+ public:
+  /* Factory function, which only makes an opt_problem if dumps are
+     enabled.  */
+  static opt_problem *
+  make (const char *text, gimple *stmt,
+	const dump_impl_location_t &impl_location)
+  {
+    if (!dump_enabled_p ())
+      return NULL;
+    return new opt_problem (text, stmt, impl_location);
+  }
+
+  void report_reason (opt_report &report);
+
+  static opt_problem *get_singleton () { return s_the_problem; }
+
+ private:
+  /* Private constructor, to be called by "make" member function.
+     Keeping this private ensures that instances are only created if
+     dump_enabled_p ().  */
+  opt_problem (const char *text, gimple *stmt,
+	       const dump_impl_location_t &impl_location)
+  : m_text (text), m_stmt (stmt),
+    m_location (dump_location_t (stmt, impl_location))
+  {
+    /* We shouldn't be bothering to construct these objects if
+       dumping isn't enabled.  */
+    gcc_assert (dump_enabled_p ());
+
+    /* Update the singleton.  */
+    delete s_the_problem;
+    s_the_problem = this;
+  }
+
+  const char *m_text;
+  gimple *m_stmt;
+  dump_location_t m_location;
+
+  static opt_problem *s_the_problem;
+};
+
+/* A base class for wrapper classes that track a success/failure value, while
+   optionally supporting propagating an opt_problem * describing any
+   failure back up the call stack.  */
+
+template <typename T>
+class opt_wrapper
+{
+ public:
+  typedef T wrapped_t;
+
+  /* Be accessible as the wrapped type.  */
+  operator wrapped_t () const { return m_result; }
+
+  /* No public ctor.  */
+
+  wrapped_t get_result () const { return m_result; }
+  opt_problem *get_problem () const { return opt_problem::get_singleton (); }
+
+ protected:
+  opt_wrapper (wrapped_t result, opt_problem */*problem*/)
+  : m_result (result)
+  {
+    /* "problem" is ignored: although it looks like a field, we
+       actually just use the opt_problem singleton, so that
+       opt_wrapper<T> in memory is just a T.  */
+  }
+
+ private:
+  wrapped_t m_result;
+};
+
+/* Subclass of opt_wrapper<T> for bool, where
+   - true signifies "success", and
+   - false signifies "failure"
+   whilst effectively propagating an opt_problem * describing any failure
+   back up the call stack.  */
+
+class opt_result : public opt_wrapper <bool>
+{
+ public:
+  /* Deprecated ctor.  During transition, allow construction from bool.
+     We want to eliminate this, as it doesn't capture the reason for
+     failures.  */
+  opt_result (bool result,
+	      const dump_impl_location_t &impl_location
+		= dump_impl_location_t ())
+    : opt_wrapper <bool> (result,
+			  opt_problem::make ("UNKNOWN", NULL, impl_location))
+  {
+  }
+
+  static opt_result success () { return opt_result (true, NULL); }
+  static opt_result failure (const char *gmsgid, gimple *stmt,
+			     const dump_impl_location_t &impl_location
+			       = dump_impl_location_t ())
+  {
+    return opt_result (false,
+		       opt_problem::make (gmsgid, stmt, impl_location));
+  }
+
+  static opt_result bad_type (const char *gmsgid, gimple *stmt, tree /*type*/,
+			      const dump_impl_location_t &impl_location
+				= dump_impl_location_t ())
+  {
+    return failure (gmsgid, stmt, impl_location);
+    // FIXME: for now; ideally we ought to capture the type
+  }
+
+ private:
+  /* Private ctor.  Instances should be created by the success and failure
+     static member functions.  */
+  opt_result (wrapped_t result, opt_problem *problem)
+  : opt_wrapper (result, problem)
+  {}
+};
+
+/* Subclass of opt_wrapper<T> where T is pointer type, for tracking
+   success/failure, where:
+   - a non-NULL value signifies "success", and
+   - a NULL value signifies "failure",
+   whilst effectively propagating an opt_problem * describing any failure
+   back up the call stack.  */
+
+template <typename PtrType_t>
+class opt_pointer_wrapper : public opt_wrapper <PtrType_t>
+{
+ public:
+  typedef PtrType_t wrapped_pointer_t;
+
+  /* Given a non-NULL pointer, make a success object wrapping it.  */
+
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  success (wrapped_pointer_t ptr)
+  {
+    return opt_pointer_wrapper <wrapped_pointer_t> (ptr, NULL);
+  }
+
+  /* Make a NULL pointer failure object, with the given message.  */
+
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  failure (const char *gmsgid,
+	   const dump_impl_location_t &impl_location
+	     = dump_impl_location_t ())
+  {
+    opt_problem *problem = opt_problem::make (gmsgid, NULL, impl_location);
+    return opt_pointer_wrapper <wrapped_pointer_t> (NULL, problem);
+  }
+
+  /* Given a failure wrapper of some other kind, make a NULL pointer
+     failure object, propagating the problem.  */
+
+  template <typename S>
+  static opt_pointer_wrapper <wrapped_pointer_t>
+  failure (opt_wrapper <S> other)
+  {
+    return opt_pointer_wrapper <wrapped_pointer_t> (NULL,
+						    other.get_problem ());
+  }
+
+  /* Support accessing the underlying pointer via ->.  */
+
+  wrapped_pointer_t operator-> () const { return this->get_result (); }
+
+ private:
+  /* Private ctor.  Instances should be built using the static member
+     functions "success" and "failure".  */
+  opt_pointer_wrapper (wrapped_pointer_t result, opt_problem *problem)
+  : opt_wrapper<PtrType_t> (result, problem)
+  {}
+};
+
+#endif /* #ifndef GCC_OPT_PROBLEM_H */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 28be41f..81b64c6 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -595,6 +595,12 @@  typedef struct _loop_vec_info : public vec_info {
 #define LOOP_VINFO_ORIG_MAX_VECT_FACTOR(L) \
   (LOOP_VINFO_MAX_VECT_FACTOR (LOOP_VINFO_ORIG_LOOP_INFO (L)))
 
+/* Wrapper for loop_vec_info, for tracking success/failure, where a non-NULL
+   value signifies success, and a NULL value signifies failure, supporting
+   propagating an opt_problem * describing the failure back up the call
+   stack.  */
+typedef opt_pointer_wrapper <loop_vec_info> opt_loop_vec_info;
+
 static inline loop_vec_info
 loop_vec_info_for_loop (struct loop *loop)
 {