public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 02/10] Add JSON implementation
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (2 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 08/10] Experiment with using optinfo for devirtualization David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-30 17:31   ` Eric Gallager
  2018-05-29 20:32 ` [PATCH 05/10] Experiment with using optinfo for vectorization David Malcolm
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch is the JSON patch I posted last year;
it adds support to gcc for reading and writing JSON,
based on DOM-like trees of json::value instances.

This is overkill for what's needed by the rest of the
patch kit (which just needs to be able to write JSON),
but this code already existed, so I'm using it for now.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add json.o.
	* json.cc: New file.
	* json.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call json_cc_tests.
	* selftest.h (selftest::json_cc_tests): New decl.
---
 gcc/Makefile.in          |    1 +
 gcc/json.cc              | 1914 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/json.h               |  214 ++++++
 gcc/selftest-run-tests.c |    1 +
 gcc/selftest.h           |    1 +
 5 files changed, 2131 insertions(+)
 create mode 100644 gcc/json.cc
 create mode 100644 gcc/json.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 20bee04..b3c7d5d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1385,6 +1385,7 @@ OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
diff --git a/gcc/json.cc b/gcc/json.cc
new file mode 100644
index 0000000..e0d5a76
--- /dev/null
+++ b/gcc/json.cc
@@ -0,0 +1,1914 @@
+/* JSON parsing
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "math.h"
+#include "selftest.h"
+
+using namespace json;
+
+/* class json::value.  */
+
+/* Generate a char * for this json::value tree.
+   The returned value must be freed by the caller.  */
+
+char *
+value::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+/* Dump this json::value tree to OUTF.
+   No formatting is done.  There are no guarantees about the order
+   in which the key/value pairs of json::objects are printed.  */
+
+void
+value::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+/* If this json::value is a json::object, return it,
+   otherwise return NULL.  */
+
+const object *
+value::as_object () const
+{
+  if (get_kind () != JSON_OBJECT)
+    return NULL;
+  return static_cast <const object *> (this);
+}
+
+/* If this json::value is a json::array, return it,
+   otherwise return NULL.  */
+
+const array *
+value::as_array () const
+{
+  if (get_kind () != JSON_ARRAY)
+    return NULL;
+  return static_cast <const array *> (this);
+}
+
+/* If this json::value is a json::number, return it,
+   otherwise return NULL.  */
+
+const number *
+value::as_number () const
+{
+  if (get_kind () != JSON_NUMBER)
+    return NULL;
+  return static_cast <const number *> (this);
+}
+
+/* If this json::value is a json::string, return it,
+   otherwise return NULL.  */
+
+const string *
+value::as_string () const
+{
+  if (get_kind () != JSON_STRING)
+    return NULL;
+  return static_cast <const string *> (this);
+}
+
+/* Attempt to get the value of a key/value pair from this value
+   as if THIS value were an object.
+
+   If THIS is not a json::object, return write an error message to OUT_ERR
+   (which must be freed by the caller) and return false.
+
+   Otherwise write the value ptr (possibly NULL) to OUT_VALUE and
+   return true.  */
+
+bool
+value::get_optional_value_by_key (const char *name, const value *&out_value,
+				  char *&out_err) const
+{
+  const json::object *obj = as_object ();
+  if (!obj)
+    {
+      out_err = xstrdup ("not an object");
+      return false;
+    }
+  out_value = obj->get (name);
+  return true;
+}
+
+/* Attempt to get a string value of a key/value pair from this value
+   as if THIS value were an object.
+
+   If THIS is a json::object, and KEY is either not present, is a string,
+   or is the "null" JSON literal, then return true, and write to OUT_VALUE.
+   If a string, then the ptr is written to OUT_VALUE, otherwise NULL
+   is written to OUT_VALUE.
+
+   If THIS is not a json::object, or KEY is not a string/"null",
+   return false and write an error message to OUT_ERR
+   (which must be freed by the caller).  */
+
+bool
+value::get_optional_string_by_key (const char *name, const char *&out_value,
+				   char *&out_err) const
+{
+  const json::value *v;
+  if (!get_optional_value_by_key (name, v, out_err))
+    return false;
+  if (v && v->get_kind () != JSON_NULL)
+    {
+      const json::string *s = v->as_string ();
+      if (!s)
+	{
+	  out_err = xasprintf ("not a string: \"%s\"", name);
+	  return false;
+	}
+      out_value = s->get_string ();
+      return true;
+    }
+  else
+    {
+      out_value = NULL;
+      return true;
+    }
+}
+
+/* Attempt to get lookup the value of a key/value pair from this value
+   as if this value were an object.
+
+   To succeed, THIS must be a json::object, and it must have a key named
+   NAME.
+
+   On success, return true and write the value to OUT_VALUE.
+   On failure, return false and write an error message to OUT_ERR
+   (which must be freed by the caller).  */
+
+bool
+value::get_value_by_key (const char *name, const value *&out_value,
+			 char *&out_err) const
+{
+  const json::object *obj = as_object ();
+  if (!obj)
+    {
+      out_err = xstrdup ("not an object");
+      return false;
+    }
+  const json::value *v = obj->get (name);
+  if (!v)
+    {
+      out_err = xasprintf ("missing attribute: \"%s\"", name);
+      return false;
+    }
+  out_value = v;
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be a number;
+   if successful, write it as an int to OUT_VALUE.  */
+
+bool
+value::get_int_by_key (const char *name, int &out_value, char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::number *n = v->as_number ();
+  if (!n)
+    {
+      out_err = xasprintf ("not a number: \"%s\"", name);
+      return false;
+    }
+  out_value = n->get ();
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be a string;
+   if successful, write it as const char * to OUT_VALUE.  */
+
+bool
+value::get_string_by_key (const char *name, const char *&out_value,
+			  char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::string *s = v->as_string ();
+  if (!s)
+    {
+      out_err = xasprintf ("not a string: \"%s\"", name);
+      return false;
+    }
+  out_value = s->get_string ();
+  return true;
+}
+
+/* As value::get_value_by_key, but the value must be an array;
+   if successful, write it as a json::array * to OUT_VALUE.  */
+
+bool
+value::get_array_by_key (const char *name, const array *&out_value,
+			 char *&out_err) const
+{
+  const json::value *v;
+  if (!get_value_by_key (name, v, out_err))
+    return false;
+  const json::array *arr = v->as_array ();
+  if (!arr)
+    {
+      out_err = xasprintf ("not an array: \"%s\"", name);
+      return false;
+    }
+  out_value = arr;
+  return true;
+}
+
+/* class json::object, a subclass of json::value, representing
+   an unordered collection of key/value pairs.  */
+
+/* json:object's dtor.  */
+
+object::~object ()
+{
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      delete ((*it).second);
+    }
+}
+
+/* Implementation of json::value::print for json::object.  */
+
+void
+object::print (pretty_printer *pp) const
+{
+  /* Note that the order is not guaranteed.  */
+  pp_character (pp, '{');
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      if (it != m_map.begin ())
+	pp_string (pp, ", ");
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
+      value->print (pp);
+    }
+  pp_character (pp, '}');
+}
+
+/* Implementation of json::value::clone for json::object.  */
+
+value *
+object::clone () const
+{
+  object *other = new object ();
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      other->set (key, value->clone ());
+    }
+  return other;
+}
+
+/* Get the json::value * for KEY, or NULL if the key is not present.  */
+
+value *
+object::get (const char *key) const
+{
+  value **slot = const_cast <object*> (this)->m_map.get (key);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+/* As object::get (KEY), but return NULL if the value of the key
+   is the "null" JSON literal.  */
+
+value *
+object::get_if_nonnull (const char *key) const
+{
+  value *result = get (key);
+  if (!result)
+    return NULL;
+  if (result->get_kind () == JSON_NULL)
+    return NULL;
+  return result;
+}
+
+/* Set the json::value * for KEY, taking ownership of VALUE
+   (and taking a copy of KEY if necessary).  */
+
+void
+object::set (const char *key, value *v)
+{
+  value **ptr = m_map.get (key);
+  if (ptr)
+    {
+      /* If the key is already present, delete the existing value
+	 and overwrite it.  */
+      delete *ptr;
+      *ptr = v;
+    }
+  else
+    /* If the key wasn't already present, take a copy of the key,
+       and store the value.  */
+    m_map.put (xstrdup (key), v);
+}
+
+/* class json::array, a subclass of json::value, representing
+   an ordered collection of values.  */
+
+/* json::array's dtor.  */
+
+array::~array ()
+{
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    delete v;
+}
+
+/* Implementation of json::value::print for json::array.  */
+
+void
+array::print (pretty_printer *pp) const
+{
+  pp_character (pp, '[');
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    {
+      if (i)
+	pp_string (pp, ", ");
+      v->print (pp);
+    }
+  pp_character (pp, ']');
+}
+
+/* Implementation of json::value::clone for json::array.  */
+
+value *
+array::clone () const
+{
+  array *other = new array ();
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    other->append (v->clone ());
+  return other;
+}
+
+/* class json::number, a subclass of json::value, wrapping a double.  */
+
+/* Implementation of json::value::print for json::number.  */
+
+void
+number::print (pretty_printer *pp) const
+{
+  char tmp[1024];
+  snprintf (tmp, sizeof (tmp), "%g", m_value);
+  pp_string (pp, tmp);
+}
+
+/* Implementation of json::value::clone for json::number.  */
+
+value *
+number::clone () const
+{
+  return new number (m_value);
+}
+
+/* class json::string, a subclass of json::value.  */
+
+void
+string::print (pretty_printer *pp) const
+{
+  pp_character (pp, '"');
+  for (const char *ptr = m_utf8; *ptr; ptr++)
+    {
+      char ch = *ptr;
+      switch (ch)
+	{
+	case '"':
+	  pp_string (pp, "\\\"");
+	  break;
+	case '\\':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\b':
+	  pp_string (pp, "\\b");
+	  break;
+	case '\f':
+	  pp_string (pp, "\\f");
+	  break;
+	case '\n':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\r':
+	  pp_string (pp, "\\r");
+	  break;
+	case '\t':
+	  pp_string (pp, "\\t");
+	  break;
+
+	default:
+	  pp_character (pp, ch);
+	}
+    }
+  pp_character (pp, '"');
+}
+
+/* Implementation of json::value::clone for json::string.  */
+
+value *
+string::clone () const
+{
+  return new string (m_utf8);
+}
+
+/* class json::literal, a subclass of json::value.  */
+
+/* Implementation of json::value::print for json::literal.  */
+
+void
+literal::print (pretty_printer *pp) const
+{
+  switch (m_kind)
+    {
+    case JSON_TRUE:
+      pp_string (pp, "true");
+      break;
+    case JSON_FALSE:
+      pp_string (pp, "false");
+      break;
+    case JSON_NULL:
+      pp_string (pp, "null");
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Implementation of json::value::clone for json::literal.  */
+
+value *
+literal::clone () const
+{
+  return new literal (m_kind);
+}
+
+\f
+/* Declarations relating to parsing JSON, all within an
+   anonymous namespace.  */
+
+namespace {
+
+/* A typedef representing a single unicode character.  */
+
+typedef unsigned unichar;
+
+/* An enum for discriminating different kinds of JSON token.  */
+
+enum token_id
+{
+  TOK_ERROR,
+
+  TOK_EOF,
+
+  /* Punctuation.  */
+  TOK_OPEN_SQUARE,
+  TOK_OPEN_CURLY,
+  TOK_CLOSE_SQUARE,
+  TOK_CLOSE_CURLY,
+  TOK_COLON,
+  TOK_COMMA,
+
+  /* Literal names.  */
+  TOK_TRUE,
+  TOK_FALSE,
+  TOK_NULL,
+
+  TOK_STRING,
+  TOK_NUMBER
+};
+
+/* Human-readable descriptions of enum token_id.  */
+
+static const char *token_id_name[] = {
+  "error",
+  "EOF",
+  "'['",
+  "'{'",
+  "']'",
+  "'}'",
+  "':'",
+  "','",
+  "'true'",
+  "'false'",
+  "'null'",
+  "string",
+  "number"
+};
+
+/* Tokens within the JSON lexer.  */
+
+struct token
+{
+  /* The kind of token.  */
+  enum token_id id;
+
+  /* The location of this token within the unicode
+     character stream.  */
+  int index;
+
+  union
+  {
+    /* Value for TOK_ERROR and TOK_STRING.  */
+    char *string;
+
+    /* Value for TOK_NUMBER.  */
+    double number;
+  } u;
+};
+
+/* A class for lexing JSON.  */
+
+class lexer
+{
+ public:
+  lexer ();
+  ~lexer ();
+  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
+
+  const token *peek ();
+  void consume ();
+
+ private:
+  bool get_char (unichar &out);
+  void unget_char ();
+  static void dump_token (FILE *outf, const token *tok);
+  void lex_token (token *out);
+  void lex_string (token *out);
+  void lex_number (token *out, unichar first_char);
+  bool rest_of_literal (const char *suffix);
+
+ private:
+  auto_vec<unichar> m_buffer;
+  int m_next_char_idx;
+
+  static const int MAX_TOKENS = 1;
+  token m_next_tokens[MAX_TOKENS];
+  int m_num_next_tokens;
+};
+
+/* A class for parsing JSON.  */
+
+class parser
+{
+ public:
+  parser (char **err_out);
+  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
+  value *parse_value (int depth);
+  object *parse_object (int depth);
+  array *parse_array (int depth);
+
+  bool seen_error_p () const { return *m_err_out; }
+  void require_eof ();
+
+ private:
+  void require (enum token_id tok_id);
+  void error_at (int, const char *, ...) ATTRIBUTE_PRINTF_3;
+
+ private:
+  lexer m_lexer;
+  char **m_err_out;
+};
+
+} // anonymous namespace for parsing implementation
+
+/* Parser implementation.  */
+
+/* lexer's ctor.  */
+
+lexer::lexer ()
+: m_buffer (), m_next_char_idx (0), m_num_next_tokens (0)
+{
+}
+
+/* lexer's dtor.  */
+
+lexer::~lexer ()
+{
+  while (m_num_next_tokens > 0)
+    consume ();
+}
+
+/* Peek the next token.  */
+
+const token *
+lexer::peek ()
+{
+  if (m_num_next_tokens == 0)
+    {
+      lex_token (&m_next_tokens[0]);
+      m_num_next_tokens++;
+    }
+  return &m_next_tokens[0];
+}
+
+/* Consume the next token.  */
+
+void
+lexer::consume ()
+{
+  if (m_num_next_tokens == 0)
+    peek ();
+
+  gcc_assert (m_num_next_tokens > 0);
+  gcc_assert (m_num_next_tokens <= MAX_TOKENS);
+
+  if (0)
+    {
+      fprintf (stderr, "consuming token: ");
+      dump_token (stderr, &m_next_tokens[0]);
+      fprintf (stderr, "\n");
+    }
+
+  if (m_next_tokens[0].id == TOK_ERROR
+      || m_next_tokens[0].id == TOK_STRING)
+    free (m_next_tokens[0].u.string);
+
+  m_num_next_tokens--;
+  memmove (&m_next_tokens[0], &m_next_tokens[1],
+	   sizeof (token) * m_num_next_tokens);
+}
+
+/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this lexer's
+   buffer.  */
+
+bool
+lexer::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
+{
+  /* FIXME: adapted from charset.c:one_utf8_to_cppchar.  */
+  static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+  static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+  const uchar *inbuf = (const unsigned char *) (utf8_buf);
+  const uchar **inbufp = &inbuf;
+  size_t *inbytesleftp = &length;
+
+  while (length > 0)
+    {
+      unichar c;
+      const uchar *inbuf = *inbufp;
+      size_t nbytes, i;
+
+      c = *inbuf;
+      if (c < 0x80)
+	{
+	  m_buffer.safe_push (c);
+	  *inbytesleftp -= 1;
+	  *inbufp += 1;
+	  continue;
+	}
+
+      /* The number of leading 1-bits in the first byte indicates how many
+	 bytes follow.  */
+      for (nbytes = 2; nbytes < 7; nbytes++)
+	if ((c & ~masks[nbytes-1]) == patns[nbytes-1])
+	  goto found;
+      *err_out = xstrdup ("ill-formed UTF-8 sequence");
+      return false;
+    found:
+
+      if (*inbytesleftp < nbytes)
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	  return false;
+	}
+
+      c = (c & masks[nbytes-1]);
+      inbuf++;
+      for (i = 1; i < nbytes; i++)
+	{
+	  unichar n = *inbuf++;
+	  if ((n & 0xC0) != 0x80)
+	    {
+	      *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	      return false;
+	    }
+	  c = ((c << 6) + (n & 0x3F));
+	}
+
+      /* Make sure the shortest possible encoding was used.  */
+      if ((   c <=      0x7F && nbytes > 1)
+	  || (c <=     0x7FF && nbytes > 2)
+	  || (c <=    0xFFFF && nbytes > 3)
+	  || (c <=  0x1FFFFF && nbytes > 4)
+	  || (c <= 0x3FFFFFF && nbytes > 5))
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8:"
+			      " shortest possible encoding not used");
+	  return false;
+	}
+
+      /* Make sure the character is valid.  */
+      if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF))
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8: invalid character");
+	  return false;
+	}
+
+      m_buffer.safe_push (c);
+      *inbufp = inbuf;
+      *inbytesleftp -= nbytes;
+    }
+  return true;
+}
+
+/* Attempt to get the next unicode character from this lexer's buffer.
+   If successful, write it to OUT and return true.
+   Otherwise, return false.  */
+
+bool
+lexer::get_char (unichar &out)
+{
+  if (m_next_char_idx >= (int)m_buffer.length ())
+    return false;
+
+  out = m_buffer[m_next_char_idx++];
+  return true;
+}
+
+/* FIXME.  */
+
+void
+lexer::unget_char ()
+{
+  --m_next_char_idx;
+}
+
+/* Print a textual representation of TOK to OUTF.
+   This is intended for debugging the lexer and parser,
+   rather than for user-facing output.  */
+
+void
+lexer::dump_token (FILE *outf, const token *tok)
+{
+  switch (tok->id)
+    {
+    case TOK_ERROR:
+      fprintf (outf, "TOK_ERROR (\"%s\")", tok->u.string);
+      break;
+
+    case TOK_EOF:
+      fprintf (outf, "TOK_EOF");
+      break;
+
+    case TOK_OPEN_SQUARE:
+      fprintf (outf, "TOK_OPEN_SQUARE");
+      break;
+
+    case TOK_OPEN_CURLY:
+      fprintf (outf, "TOK_OPEN_CURLY");
+      break;
+
+    case TOK_CLOSE_SQUARE:
+      fprintf (outf, "TOK_CLOSE_SQUARE");
+      break;
+
+    case TOK_CLOSE_CURLY:
+      fprintf (outf, "TOK_CLOSE_CURLY");
+      break;
+
+    case TOK_COLON:
+      fprintf (outf, "TOK_COLON");
+      break;
+
+    case TOK_COMMA:
+      fprintf (outf, "TOK_COMMA");
+      break;
+
+    case TOK_TRUE:
+      fprintf (outf, "TOK_TRUE");
+      break;
+
+    case TOK_FALSE:
+      fprintf (outf, "TOK_FALSE");
+      break;
+
+    case TOK_NULL:
+      fprintf (outf, "TOK_NULL");
+      break;
+
+    case TOK_STRING:
+      fprintf (outf, "TOK_STRING (\"%s\")", tok->u.string);
+      break;
+
+    case TOK_NUMBER:
+      fprintf (outf, "TOK_NUMBER (%f)", tok->u.number);
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Attempt to lex the input buffer, writing the next token to OUT.
+   On errors, TOK_ERROR (or TOK_EOF) is written to OUT.  */
+
+void
+lexer::lex_token (token *out)
+{
+  /* Skip to next non-whitespace char.  */
+  unichar next_char;
+  while (1)
+    {
+      out->index = m_next_char_idx;
+      if (!get_char (next_char))
+	{
+	  out->id = TOK_EOF;
+	  return;
+	}
+      if (next_char != ' '
+	  && next_char != '\t'
+	  && next_char != '\n'
+	  && next_char != '\r')
+	break;
+    }
+
+  switch (next_char)
+    {
+    case '[':
+      out->id = TOK_OPEN_SQUARE;
+      break;
+
+    case '{':
+      out->id = TOK_OPEN_CURLY;
+      break;
+
+    case ']':
+      out->id = TOK_CLOSE_SQUARE;
+      break;
+
+    case '}':
+      out->id = TOK_CLOSE_CURLY;
+      break;
+
+    case ':':
+      out->id = TOK_COLON;
+      break;
+
+    case ',':
+      out->id = TOK_COMMA;
+      break;
+
+    case '"':
+      lex_string (out);
+      break;
+
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      lex_number (out, next_char);
+      break;
+
+    case 't':
+      /* Handle literal "true".  */
+      if (rest_of_literal ("rue"))
+	{
+	  out->id = TOK_TRUE;
+	  break;
+	}
+      else
+	goto err;
+
+    case 'f':
+      /* Handle literal "false".  */
+      if (rest_of_literal ("alse"))
+	{
+	  out->id = TOK_FALSE;
+	  break;
+	}
+      else
+	goto err;
+
+    case 'n':
+      /* Handle literal "null".  */
+      if (rest_of_literal ("ull"))
+	{
+	  out->id = TOK_NULL;
+	  break;
+	}
+      else
+	goto err;
+
+    err:
+    default:
+      out->id = TOK_ERROR;
+      out->u.string = xasprintf ("unexpected character: %c", next_char);
+      break;
+    }
+}
+
+/* Having consumed an open-quote character from the lexer's buffer, attempt
+   to lex the rest of a JSON string, writing the result to OUT (or TOK_ERROR)
+   if an error occurred.
+   (ECMA-404 section 9; RFC 7159 section 7).  */
+
+void
+lexer::lex_string (token *out)
+{
+  auto_vec<unichar> content;
+  bool still_going = true;
+  while (still_going)
+    {
+      unichar uc;
+      if (!get_char (uc))
+	{
+	  out->id = TOK_ERROR;
+	  out->u.string = xstrdup ("EOF within string");
+	  return;
+	}
+      switch (uc)
+	{
+	case '"':
+	  still_going = false;
+	  break;
+	case '\\':
+	  {
+	    unichar next_char;
+	    if (!get_char (next_char))
+	      {
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("EOF within string");;
+		return;
+	      }
+	    switch (next_char)
+	      {
+	      case '"':
+	      case '\\':
+	      case '/':
+		content.safe_push (next_char);
+		break;
+
+	      case 'b':
+		content.safe_push ('\b');
+		break;
+
+	      case 'f':
+		content.safe_push ('\f');
+		break;
+
+	      case 'n':
+		content.safe_push ('\n');
+		break;
+
+	      case 'r':
+		content.safe_push ('\r');
+		break;
+
+	      case 't':
+		content.safe_push ('\t');
+		break;
+
+	      case 'u':
+		{
+		  unichar result = 0;
+		  for (int i = 0; i < 4; i++)
+		    {
+		      unichar hexdigit;
+		      if (!get_char (hexdigit))
+			{
+			  out->id = TOK_ERROR;
+			  out->u.string = xstrdup ("EOF within string");
+			  return;
+			}
+		      result <<= 4;
+		      if (hexdigit >= '0' && hexdigit <= '9')
+			result += hexdigit - '0';
+		      else if (hexdigit >= 'a' && hexdigit <= 'f')
+			result += (hexdigit - 'a') + 10;
+		      else if (hexdigit >= 'A' && hexdigit <= 'F')
+			result += (hexdigit - 'A') + 10;
+		      else
+			{
+			  out->id = TOK_ERROR;
+			  out->u.string = xstrdup ("bogus hex char");
+			  return;
+			}
+		    }
+		  content.safe_push (result);
+		}
+		break;
+
+	      default:
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("unrecognized escape char");
+		return;
+	      }
+	  }
+	  break;
+
+	default:
+	  /* Reject unescaped control characters U+0000 through U+001F
+	     (ECMA-404 section 9 para 1; RFC 7159 section 7 para 1).  */
+	  if (uc <= 0x1f)
+	    {
+		out->id = TOK_ERROR;
+		out->u.string = xstrdup ("unescaped control char");
+		return;
+	    }
+
+	  /* Otherwise, add regular unicode code point.  */
+	  content.safe_push (uc);
+	  break;
+	}
+    }
+
+  out->id = TOK_STRING;
+
+  auto_vec<char> utf8_buf;
+  // FIXME: adapted from libcpp/charset.c:one_cppchar_to_utf8
+  for (unsigned i = 0; i < content.length (); i++)
+    {
+      static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+      static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
+      size_t nbytes;
+      uchar buf[6], *p = &buf[6];
+      unichar c = content[i];
+
+      nbytes = 1;
+      if (c < 0x80)
+	*--p = c;
+      else
+	{
+	  do
+	    {
+	      *--p = ((c & 0x3F) | 0x80);
+	      c >>= 6;
+	      nbytes++;
+	    }
+	  while (c >= 0x3F || (c & limits[nbytes-1]));
+	  *--p = (c | masks[nbytes-1]);
+	}
+
+      while (p < &buf[6])
+	utf8_buf.safe_push (*p++);
+    }
+
+  out->u.string = XNEWVEC (char, utf8_buf.length () + 1);
+  for (unsigned i = 0; i < utf8_buf.length (); i++)
+    out->u.string[i] = utf8_buf[i];
+  out->u.string[utf8_buf.length ()] = '\0';
+
+  // FIXME: leaks?  have a json_context do the allocation
+}
+
+/* Having consumed FIRST_CHAR, an initial digit or '-' character from
+   the lexer's buffer attempt to lex the rest of a JSON number, writing
+   the result to OUT (or TOK_ERROR) if an error occurred.
+   (ECMA-404 section 8; RFC 7159 section 6).  */
+
+void
+lexer::lex_number (token *out, unichar first_char)
+{
+  bool negate = false;
+  double value = 0.0;
+  if (first_char == '-')
+    {
+      negate = true;
+      if (!get_char (first_char))
+	{
+	  out->id = TOK_ERROR;
+	  out->u.string = xstrdup ("expected digit");
+	  return;
+	}
+    }
+
+  if (first_char == '0')
+    value = 0.0;
+  else if (!ISDIGIT (first_char))
+    {
+      out->id = TOK_ERROR;
+      out->u.string = xstrdup ("expected digit");
+      return;
+    }
+  else
+    {
+      /* Got a nonzero digit; expect zero or more digits.  */
+      value = first_char - '0';
+      while (1)
+	{
+	  unichar uc;
+	  if (!get_char (uc))
+	    break;
+	  if (ISDIGIT (uc))
+	    {
+	      value *= 10;
+	      value += uc -'0';
+	      continue;
+	    }
+	  else
+	    {
+	      unget_char ();
+	      break;
+	    }
+	}
+    }
+
+  /* Optional '.', followed by one or more decimals.  */
+  unichar next_char;
+  if (get_char (next_char))
+    {
+      if (next_char == '.')
+	{
+	  /* Parse decimal digits.  */
+	  bool had_digit = false;
+	  // FIXME: does this lose too much precision?
+	  double digit_factor = 0.1;
+	  while (get_char (next_char))
+	    {
+	      if (!ISDIGIT (next_char))
+		{
+		  unget_char ();
+		  break;
+		}
+	      value += (next_char - '0') * digit_factor;
+	      digit_factor *= 0.1;
+	      had_digit = true;
+	    }
+	  if (!had_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("expected digit");
+	      return;
+	    }
+	}
+      else
+	unget_char ();
+    }
+
+  /* Parse 'e' and 'E'.  */
+  unichar exponent_char;
+  if (get_char (exponent_char))
+    {
+      if (exponent_char == 'e' || exponent_char == 'E')
+	{
+	  /* Optional +/-.  */
+	  unichar sign_char;
+	  int exponent = 0;
+	  bool negate_exponent = false;
+	  bool had_exponent_digit = false;
+	  if (!get_char (sign_char))
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("EOF within exponent");
+	      return;
+	    }
+	  if (sign_char == '-')
+	    negate_exponent = true;
+	  else if (sign_char == '+')
+	    ;
+	  else if (ISDIGIT (sign_char))
+	    {
+	      exponent = sign_char - '0';
+	      had_exponent_digit = true;
+	    }
+	  else
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string
+		= xstrdup ("expected '-','+' or digit within exponent");
+	      return;
+	    }
+
+	  /* One or more digits (we might have seen the digit above,
+	     though).  */
+	  while (1)
+	    {
+	      unichar uc;
+	      if (!get_char (uc))
+		break;
+	      if (ISDIGIT (uc))
+		{
+		  exponent *= 10;
+		  exponent += uc -'0';
+		  had_exponent_digit = true;
+		  continue;
+		}
+	      else
+		{
+		  unget_char ();
+		  break;
+		}
+	    }
+	  if (!had_exponent_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xstrdup ("expected digit within exponent");
+	      return;
+	    }
+	  if (negate_exponent)
+	    exponent = -exponent;
+	  /* FIXME: better way to do this?  */
+	  value = value * pow (10, exponent);
+	}
+      else
+	unget_char ();
+    }
+
+  if (negate)
+    value = -value;
+
+  out->id = TOK_NUMBER;
+  out->u.number = value;
+}
+
+/* Determine if the next characters to be lexed match SUFFIX.
+   SUFFIX must be pure ASCII.
+   If so, consume the characters and return true.
+   Otherwise, return false.  */
+
+bool
+lexer::rest_of_literal (const char *suffix)
+{
+  int suffix_idx = 0;
+  int buf_idx = m_next_char_idx;
+  while (1)
+    {
+      if (suffix[suffix_idx] == '\0')
+	{
+	  m_next_char_idx += suffix_idx;
+	  return true;
+	}
+      if (buf_idx >= (int)m_buffer.length ())
+	return false;
+      /* This assumes that suffix is ASCII.  */
+      if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx])
+	return false;
+      buf_idx++;
+      suffix_idx++;
+    }
+}
+
+/* parser's ctor.  */
+
+parser::parser (char **err_out)
+: m_lexer (), m_err_out (err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+  *err_out = NULL;
+}
+
+/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this parser's
+   lexer's buffer.  */
+
+bool
+parser::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
+{
+  return m_lexer.add_utf8 (length, utf8_buf, err_out);
+}
+
+/* Parse a JSON value (object, array, number, string, or literal).
+   (ECMA-404 section 5; RFC 7159 section 3).  */
+
+value *
+parser::parse_value (int depth)
+{
+  const token *tok = m_lexer.peek ();
+
+  /* Avoid stack overflow with deeply-nested inputs; RFC 7159 section 9
+     states: "An implementation may set limits on the maximum depth
+     of nesting.".
+
+     Ideally we'd avoid this limit (e.g. by rewriting parse_value,
+     parse_object, and parse_array into a single function with a vec of
+     state).  */
+  const int MAX_DEPTH = 100;
+  if (depth >= MAX_DEPTH)
+    {
+      error_at (tok->index, "maximum nesting depth exceeded: %i", MAX_DEPTH);
+      return NULL;
+    }
+
+  switch (tok->id)
+    {
+    case TOK_OPEN_CURLY:
+      return parse_object (depth);
+
+    case TOK_STRING:
+      {
+	string *result = new string (tok->u.string);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_OPEN_SQUARE:
+      return parse_array (depth);
+
+    case TOK_NUMBER:
+      {
+	number *result = new number (tok->u.number);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_TRUE:
+      {
+	literal *result = new literal (JSON_TRUE);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_FALSE:
+      {
+	literal *result = new literal (JSON_FALSE);
+	m_lexer.consume ();
+	return result;
+      }
+
+    case TOK_NULL:
+      {
+	literal *result = new literal (JSON_NULL);
+	m_lexer.consume ();
+	return result;
+      }
+
+    default:
+      error_at (tok->index, "unexpected token: %s", token_id_name[tok->id]);
+      return NULL;
+    }
+}
+
+/* Parse a JSON object.
+   (ECMA-404 section 6; RFC 7159 section 4).  */
+
+object *
+parser::parse_object (int depth)
+{
+  require (TOK_OPEN_CURLY);
+
+  object *result = new object ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_CURLY)
+    {
+      require (TOK_CLOSE_CURLY);
+      return result;
+    }
+  if (tok->id != TOK_STRING)
+    {
+      error_at (tok->index, "expected string for object key");
+      return result;
+    }
+  while (!seen_error_p ())
+    {
+      tok = m_lexer.peek ();
+      if (tok->id != TOK_STRING)
+	{
+	  error_at (tok->index, "expected string for object key");
+	  return result;
+	}
+      char *key = xstrdup (tok->u.string);
+      m_lexer.consume ();
+
+      require (TOK_COLON);
+
+      value *v = parse_value (depth + 1);
+      if (!v)
+	{
+	  free (key);
+	  return result;
+	}
+      /* We don't enforce uniqueness for keys.  */
+      result->set (key, v);
+      free (key);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_CURLY);
+	  break;
+	}
+    }
+  return result;
+}
+
+/* Parse a JSON array.
+   (ECMA-404 section 7; RFC 7159 section 5).  */
+
+array *
+parser::parse_array (int depth)
+{
+  require (TOK_OPEN_SQUARE);
+
+  array *result = new array ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_SQUARE)
+    {
+      m_lexer.consume ();
+      return result;
+    }
+
+  while (!seen_error_p ())
+    {
+      value *v = parse_value (depth + 1);
+      if (!v)
+	return result;
+
+      result->append (v);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_SQUARE);
+	  break;
+	}
+    }
+
+  return result;
+}
+
+/* Require an EOF, or fail if there is surplus input.  */
+
+void
+parser::require_eof ()
+{
+  require (TOK_EOF);
+}
+
+/* Consume the next token, issuing an error if it is not of kind TOK_ID.  */
+
+void
+parser::require (enum token_id tok_id)
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id != tok_id)
+    {
+      if (tok->id == TOK_ERROR)
+	error_at (tok->index, "expected %s; got bad token: %s",
+		  token_id_name[tok_id], tok->u.string);
+      else
+	error_at (tok->index, "expected %s; got %s", token_id_name[tok_id],
+		  token_id_name[tok->id]);
+    }
+  m_lexer.consume ();
+}
+
+/* Issue a parsing error.  If this is the first error that has occurred on
+   the parser, store it within the parser's m_err_out (the buffer will
+   eventually need to be free by the caller of the parser).
+   Otherwise the error is discarded.
+
+   TODO: maybe provide a callback so that client code can print all errors?  */
+
+void
+parser::error_at (int index, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  char *formatted = xvasprintf (fmt, ap);
+  va_end (ap);
+
+  char *msg_with_index = xasprintf ("error at index %i: %s",
+				    index, formatted);
+  free (formatted);
+
+  if (0)
+    fprintf (stderr, "%s\n", msg_with_index);
+  if (*m_err_out == NULL)
+    *m_err_out = msg_with_index;
+  else
+    free (msg_with_index);
+}
+
+/* Attempt to parse the UTF-8 encoded buffer at UTF8_BUF
+   of the given LENGTH.
+   If successful, return a non-NULL json::value *.
+   if there was a problem, return NULL and write an error
+   message to err_out, which must be freed by the caller.  */
+
+value *
+json::parse_utf8_string (size_t length, const char *utf8_buf,
+			 char **err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+
+  parser p (err_out);
+  if (!p.add_utf8 (length, utf8_buf, err_out))
+    return NULL;
+  value *result = p.parse_value (0);
+  if (!p.seen_error_p ())
+    p.require_eof ();
+  if (p.seen_error_p ())
+    {
+      gcc_assert (*err_out);
+      delete result;
+      return NULL;
+    }
+  return result;
+}
+
+/* Attempt to parse the nil-terminated UTF-8 encoded buffer at
+   UTF8_BUF.
+   If successful, return a non-NULL json::value *.
+   if there was a problem, return NULL and write an error
+   message to err_out, which must be freed by the caller.  */
+
+value *
+json::parse_utf8_string (const char *utf8, char **err_out)
+{
+  return parse_utf8_string (strlen (utf8), utf8, err_out);
+}
+
+\f
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Verify that JV->to_str () equals EXPECTED_JSON.  */
+
+static void
+assert_to_str_eq (const char *expected_json, json::value *jv)
+{
+  char *json = jv->to_str ();
+  ASSERT_STREQ (expected_json, json);
+  free (json);
+}
+
+/* FIXME.  */
+
+static void
+test_parse_string ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("\"foo\"", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  ASSERT_STREQ ("foo", ((json::string *)jv)->get_string ());
+  assert_to_str_eq ("\"foo\"", jv);
+
+  json::value *clone = jv->clone ();
+  ASSERT_EQ (JSON_STRING, clone->get_kind ());
+  ASSERT_STREQ ("foo", ((json::string *)clone)->get_string ());
+  assert_to_str_eq ("\"foo\"", clone);
+  delete clone;
+  delete jv;
+
+  const char *contains_quotes = "\"before \\\"quoted\\\" after\"";
+  jv = parse_utf8_string (contains_quotes, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  ASSERT_STREQ ("before \"quoted\" after", ((json::string *)jv)->get_string ());
+  assert_to_str_eq (contains_quotes, jv);
+  delete jv;
+
+  /* Test of non-ASCII input.  This string is the Japanese word "mojibake",
+     written as C octal-escaped UTF-8.  */
+  const char *mojibake = (/* Opening quote.  */
+			  "\""
+			  /* U+6587 CJK UNIFIED IDEOGRAPH-6587
+			     UTF-8: 0xE6 0x96 0x87
+			     C octal escaped UTF-8: \346\226\207.  */
+			  "\346\226\207"
+			  /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
+			     UTF-8: 0xE5 0xAD 0x97
+			     C octal escaped UTF-8: \345\255\227.  */
+			  "\345\255\227"
+			 /* U+5316 CJK UNIFIED IDEOGRAPH-5316
+			      UTF-8: 0xE5 0x8C 0x96
+			      C octal escaped UTF-8: \345\214\226.  */
+			  "\345\214\226"
+			 /* U+3051 HIRAGANA LETTER KE
+			      UTF-8: 0xE3 0x81 0x91
+			      C octal escaped UTF-8: \343\201\221.  */
+			  "\343\201\221"
+			  /* Closing quote.  */
+			  "\"");
+  jv = parse_utf8_string (mojibake, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  /* Result of get_string should be UTF-8 encoded, without quotes.  */
+  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221",
+		((json::string *)jv)->get_string ());
+  /* Result of dump should be UTF-8 encoded, with quotes.  */
+  assert_to_str_eq (mojibake, jv);
+  delete jv;
+
+  /* Test of \u-escaped unicode.  This is "mojibake" again, as above.  */
+  const char *escaped_unicode = "\"\\u6587\\u5b57\\u5316\\u3051\"";
+  jv = parse_utf8_string (escaped_unicode, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  /* Result of get_string should be UTF-8 encoded, without quotes.  */
+  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221",
+		((json::string *)jv)->get_string ());
+  /* Result of dump should be UTF-8 encoded, with quotes.  */
+  assert_to_str_eq (mojibake, jv);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_number ()
+{
+  json::value *jv, *clone;
+
+  char *err = NULL;
+  jv = parse_utf8_string ("42", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (42.0, ((json::number *)jv)->get ());
+  assert_to_str_eq ("42", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_NUMBER, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  /* Negative number.  */
+  jv = parse_utf8_string ("-17", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (-17.0, ((json::number *)jv)->get ());
+  assert_to_str_eq ("-17", jv);
+  delete jv;
+
+  /* Decimal.  */
+  jv = parse_utf8_string ("3.141", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
+  assert_to_str_eq ("3.141", jv);
+  delete jv;
+
+  /* Exponents.  */
+  jv = parse_utf8_string ("3.141e+0", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
+  assert_to_str_eq ("3.141", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e2", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4200, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4200", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e-1", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4.2, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4.2", jv);
+  delete jv;
+
+}
+
+/* FIXME.  */
+
+static void
+test_parse_array ()
+{
+  json::value *jv, *clone;
+
+  char *err = NULL;
+  jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_ARRAY, jv->get_kind ());
+  json::array *arr = static_cast <json::array *> (jv);
+  ASSERT_EQ (10, arr->get_length ());
+  for (int i = 0; i < 10; i++)
+    {
+      json::value *element = arr->get (i);
+      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
+      ASSERT_EQ (i, ((json::number *)element)->get ());
+    }
+  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", jv);
+
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_ARRAY, clone->get_kind ());
+  arr = static_cast <json::array *> (clone);
+  ASSERT_EQ (10, arr->get_length ());
+  for (int i = 0; i < 10; i++)
+    {
+      json::value *element = arr->get (i);
+      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
+      ASSERT_EQ (i, ((json::number *)element)->get ());
+    }
+  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", clone);
+  delete clone;
+
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_object ()
+{
+  char *err = NULL;
+  json::value *jv
+    = parse_utf8_string ("{\"foo\": \"bar\", \"baz\": [42, null]}", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+  json::object *jo = static_cast <json::object *> (jv);
+
+  json::value *foo_value = jo->get ("foo");
+  ASSERT_TRUE (foo_value != NULL);
+  ASSERT_EQ (JSON_STRING, foo_value->get_kind ());
+  ASSERT_STREQ ("bar", ((json::string *)foo_value)->get_string ());
+
+  json::value *baz_value = jo->get ("baz");
+  ASSERT_TRUE (baz_value != NULL);
+  ASSERT_EQ (JSON_ARRAY, baz_value->get_kind ());
+
+  json::array *baz_array = (json::array *)baz_value;
+  ASSERT_EQ (2, baz_array->get_length ());
+  ASSERT_EQ (42, baz_array->get (0)->as_number ()->get ());
+  ASSERT_EQ (JSON_NULL, baz_array->get (1)->get_kind ());
+
+  // TODO: error-handling
+  // TODO: partial document
+
+  /* We can't use assert_to_str_eq since ordering is not guaranteed.  */
+
+  json::value *clone = jv->clone ();
+  ASSERT_EQ (JSON_OBJECT, clone->get_kind ());
+  ASSERT_EQ (JSON_STRING, clone->as_object ()->get ("foo")->get_kind ());
+  delete clone;
+
+  delete jv;
+}
+
+/* Verify that the literals "true", "false" and "null" are parsed,
+   dumped, and are clonable.  */
+
+static void
+test_parse_literals ()
+{
+  json::value *jv, *clone;
+  char *err = NULL;
+  jv = parse_utf8_string ("true", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_TRUE, jv->get_kind ());
+  assert_to_str_eq ("true", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_TRUE, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  jv = parse_utf8_string ("false", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_FALSE, jv->get_kind ());
+  assert_to_str_eq ("false", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_FALSE, clone->get_kind ());
+  delete clone;
+  delete jv;
+
+  jv = parse_utf8_string ("null", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_NULL, jv->get_kind ());
+  assert_to_str_eq ("null", jv);
+  clone = jv->clone ();
+  ASSERT_EQ (JSON_NULL, clone->get_kind ());
+  delete clone;
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_jsonrpc ()
+{
+  char *err = NULL;
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *jv = parse_utf8_string (request, &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_parse_empty_object ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("{}", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+  assert_to_str_eq ("{}", jv);
+  delete jv;
+}
+
+/* FIXME.  */
+
+static void
+test_error_empty_string ()
+{
+  char *err = NULL;
+  json::value *jv = parse_utf8_string ("", &err);
+  ASSERT_STREQ ("error at index 0: unexpected token: EOF", err);
+  ASSERT_TRUE (jv == NULL);
+  free (err);
+}
+
+/* FIXME.  */
+
+static void
+test_error_missing_comma ()
+{
+  char *err = NULL;
+  /*                  01234567.  */
+  const char *json = "[0, 1 2]";
+  json::value *jv = parse_utf8_string (json, &err);
+  ASSERT_STREQ ("error at index 6: expected ']'; got number",
+		err);
+  // FIXME: unittest the lexer?
+  ASSERT_TRUE (jv == NULL);
+  free (err);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_cc_tests ()
+{
+  test_parse_string ();
+  test_parse_number ();
+  test_parse_array ();
+  test_parse_object ();
+  test_parse_literals ();
+  test_parse_jsonrpc ();
+  test_parse_empty_object ();
+  test_error_empty_string ();
+  test_error_missing_comma ();
+
+  /* FIXME: tests for roundtripping (noting that we don't preserve
+     object key ordering).  */
+
+  /* FIXME: cloning.  */
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json.h b/gcc/json.h
new file mode 100644
index 0000000..aedf84a
--- /dev/null
+++ b/gcc/json.h
@@ -0,0 +1,214 @@
+/* JSON parsing
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+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_JSON_H
+#define GCC_JSON_H
+
+/* Implementation of JSON, a lightweight data-interchange format.
+
+   See http://www.json.org/
+   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+   and https://tools.ietf.org/html/rfc7159
+
+   Supports parsing text into a DOM-like tree of json::value *, dumping
+   json::value * to text.  */
+
+namespace json
+{
+
+/* Forward decls of json::value and its subclasses (using indentation
+   to denote inheritance.  */
+
+class value;
+  class object;
+  class array;
+  class number;
+  class string;
+  class literal;
+
+/* An enum for discriminating the subclasses of json::value.  */
+
+enum kind
+{
+  /* class json::object.  */
+  JSON_OBJECT,
+
+  /* class json::array.  */
+  JSON_ARRAY,
+
+  /* class json::number.  */
+  JSON_NUMBER,
+
+  /* class json::string.  */
+  JSON_STRING,
+
+  /* class json::literal uses these three values to identify the
+     particular literal.  */
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+
+/* Base class of JSON value.  */
+
+class value
+{
+ public:
+  virtual ~value () {}
+  virtual enum kind get_kind () const = 0;
+  virtual void print (pretty_printer *pp) const = 0;
+
+  /* Create a deep copy of the value, returning a value which must be
+     deleted by the caller.  */
+  virtual value *clone () const = 0;
+
+  char *to_str () const;
+  void dump (FILE *) const;
+
+  /* Methods for dynamically casting a value to one of the subclasses,
+     returning NULL if the value is of the wrong kind.  */
+  const object *as_object () const;
+  const array *as_array () const;
+  const number *as_number () const;
+  const string *as_string () const;
+
+  /* Convenience accessors for attempting to perform key/value lookups
+     on this value as if it were an json::object.
+
+     On success, return true and write the value to OUT_VALUE.
+     On failure, return false and write an error message to OUT_ERR
+     (which must be freed by the caller).  */
+  bool get_value_by_key (const char *name, const value *&out_value,
+			 char *&out_err) const;
+  bool get_int_by_key (const char *name, int &out_value, char *&out_err) const;
+  bool get_string_by_key (const char *name, const char *&out_value,
+			  char *&out_err) const;
+  bool get_array_by_key (const char *name, const array *&out_value,
+			 char *&out_err) const;
+
+  /* As above, but the key is optional.  THIS must still be an object,
+     though.  */
+  bool get_optional_value_by_key (const char *name, const value *&out_value,
+				  char *&out_err) const;
+  bool get_optional_string_by_key (const char *name, const char *&out_value,
+				   char *&out_err) const;
+};
+
+/* Subclass of value for objects: an unordered collection of
+   key/value pairs.  */
+
+class object : public value
+{
+ public:
+  ~object ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  value *get (const char *key) const;
+  value *get_if_nonnull (const char *key) const;
+
+  void set (const char *key, value *v);
+
+ private:
+  typedef hash_map <char *, value *,
+    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
+  map_t m_map;
+};
+
+/* Subclass of value for arrays.  */
+
+class array : public value
+{
+ public:
+  ~array ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  unsigned get_length () const { return m_elements.length (); }
+  value *get (int idx) const { return m_elements[idx]; }
+  void append (value *v) { m_elements.safe_push (v); }
+
+ private:
+  auto_vec<value *> m_elements;
+};
+
+/* Subclass of value for numbers.  */
+
+class number : public value
+{
+ public:
+  number (double value) : m_value (value) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  double get () const { return m_value; }
+
+ private:
+  double m_value;
+};
+
+/* Subclass of value for strings.  */
+
+class string : public value
+{
+ public:
+  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
+  ~string () { free (m_utf8); }
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+  const char *get_string () const { return m_utf8; }
+
+ private:
+  char *m_utf8;
+};
+
+/* Subclass of value for the three JSON literals "true", "false",
+   and "null".  */
+
+class literal : public value
+{
+ public:
+  literal (enum kind kind) : m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+  value *clone () const FINAL OVERRIDE;
+
+ private:
+  enum kind m_kind;
+};
+
+/* Declarations for parsing JSON to a json::value * tree.  */
+
+extern value *parse_utf8_string (size_t length, const char *utf8_buf,
+				 char **err_out);
+extern value *parse_utf8_string (const char *utf8, char **err_out);
+
+} // namespace json
+
+#endif  /* GCC_JSON_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index fe221ff..36879cf 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -70,6 +70,7 @@ selftest::run_tests ()
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
+  json_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index e3117c6..2a912d8 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -199,6 +199,7 @@ extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void json_cc_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 10/10] Experiment with optinfo in tree-ssa-loop-im.c
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (4 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 05/10] Experiment with using optinfo for vectorization David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 07/10] Experiment with using optinfo for loop-handling David Malcolm
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

---
 gcc/tree-ssa-loop-im.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
index 030aac0..fcf5d24 100644
--- a/gcc/tree-ssa-loop-im.c
+++ b/gcc/tree-ssa-loop-im.c
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "tree-scalar-evolution.h"
 #include "tree-ssa-loop-niter.h"
+#include "optinfo.h"
 
 /* TODO:  Support for predicated code motion.  I.e.
 
@@ -1125,6 +1126,12 @@ move_computations_worker (basic_block bb)
 	  fprintf (dump_file, "(cost %u) out of loop %d.\n\n",
 		   cost, level->num);
 	}
+      /* FIXME.  */
+      if (optinfo_enabled_p ())
+	OPTINFO_SUCCESS (stmt)
+	  << "moving PHI node "
+	  << stmt
+	  << optinfo_printf (" (cost %u) out of loop %d", cost, level->num);
 
       if (gimple_phi_num_args (stmt) == 1)
 	{
@@ -1194,6 +1201,12 @@ move_computations_worker (basic_block bb)
 	  fprintf (dump_file, "(cost %u) out of loop %d.\n\n",
 		   cost, level->num);
 	}
+      /* FIXME.  */
+      if (optinfo_enabled_p ())
+	OPTINFO_SUCCESS (stmt)
+	  << "moving statement "
+	  << stmt
+	  << optinfo_printf (" (cost %u) out of loop %d", cost, level->num);
 
       e = loop_preheader_edge (level);
       gcc_assert (!gimple_vdef (stmt));
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 03/10] Add optinfo, remarks and optimization records
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (7 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:35 ` [PATCH 01/10] Convert dump and optgroup flags to enums David Malcolm
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch adds a way to create optimization information or "optinfo"
instances, referring to source code locations (based on statements,
loops, or basic blocks), populate them with messages and data
(trees, statements, symtab nodes),
and to send them to up to three "sinks"/destinations:
* via -fopt-info to files/dumpfiles/stderr
* as diagnostic "remarks"
* as JSON files via -fsave-optimization-record

It doesn't add any uses of the code (except via a test plugin), so
to get a better idea of how it might work, see the later patches
in the kit (e.g. patch 5, which uses it for vectorization).

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
	optinfo-emit-fopt-info.o, optinfo-emit-json.o.
	(GTFILES): Add $(srcdir)/optinfo.h.
	* common.opt (fremarks): New option.
	(fsave-optimization-record): New option.
	* coretypes.h (struct kv_pair): Move here from dumpfile.c.
	* diagnostic-color.c (color_dict): Add "remark", as bold green.
	* diagnostic-core.h (remark): New decl.
	* diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
	(remark): New function.
	* diagnostic.def (DK_REMARK): New diagnostic kind.
	* doc/invoke.texi (Remarks): New section.
	(-fsave-optimization-record): New option.
	(-fremarks): New option.
	* dumpfile.c (struct kv_pair): Move from here to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_loc): Make static; add overload taking no FILE *.
	* dumpfile.h (dump_loc): New decl.
	(optgroup_options): New decl.

gcc/fortran/ChangeLog:
	* gfc-diagnostic.def (DK_REMARK): New diagnostic kind.

gcc/ChangeLog:
	* gengtype.c (open_base_files): Add "optinfo.h".
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo-emit-fopt-info.cc: New file.
	* optinfo-emit-fopt-info.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* opts.c (print_specific_help): Handle CL_REMARK.
	(common_handle_option): Likewise.
	* opts.h (CL_REMARK): New macro.
	(CL_MAX_OPTION_CLASS): Update for CL_REMARK.
	(CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD,
	CL_PCH_IGNORE): Likewise.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
	remarks_plugin.c.
	* gcc.dg/plugin/remarks-1.c: New test.
	* gcc.dg/plugin/remarks_plugin.c: New test plugin.
	* lib/gcc-dg.exp (dg-remark): New function.

gcc/ChangeLog:
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_start and
	optimization_records_finish.
	* tree-ssa-live.c: Include "optinfo.h".
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
---
 gcc/Makefile.in                              |   5 +
 gcc/common.opt                               |   9 +
 gcc/coretypes.h                              |   8 +
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 ++
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  34 ++-
 gcc/dumpfile.c                               |  24 +-
 gcc/dumpfile.h                               |   3 +
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gengtype.c                               |   3 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 141 +++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 ++
 gcc/optinfo-emit-fopt-info.cc                | 100 +++++++
 gcc/optinfo-emit-fopt-info.h                 |  26 ++
 gcc/optinfo-emit-json.cc                     | 408 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  37 +++
 gcc/optinfo-internal.h                       | 139 +++++++++
 gcc/optinfo.cc                               | 272 ++++++++++++++++++
 gcc/optinfo.h                                | 389 +++++++++++++++++++++++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/profile-count.c                          |  28 ++
 gcc/profile-count.h                          |   5 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  32 +++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 158 +++++++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-ssa-live.c                          |   4 +-
 32 files changed, 1889 insertions(+), 19 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-fopt-info.cc
 create mode 100644 gcc/optinfo-emit-fopt-info.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index b3c7d5d..5ae5713 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1422,6 +1422,10 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
+	optinfo-emit-diagnostics.o \
+	optinfo-emit-fopt-info.o \
+	optinfo-emit-json.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
@@ -2588,6 +2592,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/internal-fn.h \
   $(srcdir)/hsa-common.c \
   $(srcdir)/calls.c \
+  $(srcdir)/optinfo.h \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/common.opt b/gcc/common.opt
index d6ef859..42570aa 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -505,6 +505,11 @@ Driver Negative(Qn)
 R
 Driver Joined Separate
 
+fremarks
+Common Remark Var(flag_remarks)
+Emit diagnostic remarks about optimizations
+FIXME: should this be -fdiagnostics-foo or somesuch?
+
 S
 Driver
 
@@ -1941,6 +1946,10 @@ fopt-info-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fopt-info[-<type>=filename]	Dump compiler optimization details.
 
+fsave-optimization-record
+Common Report Var(flag_save_optimization_record) Optimization
+Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..6fe56b5 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -325,6 +325,14 @@ namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 3ee21bc..bcc3767 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
+  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
+	       6, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index aa5807e..63166b8 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool remark (location_t, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index e22c17b..97ed88b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
     {
     case DK_DEBUG:
     case DK_NOTE:
+    case DK_REMARK:
     case DK_ANACHRONISM:
     case DK_WARNING:
       break;
@@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   return ret;
 }
 
+/* Emit an optimization remark at LOCATION.  This is used by the optinfo
+   framework.
+   Return true if the remark was printed, false if it was inhibited.  */
+// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
+
+bool
+remark (location_t location, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, location);
+  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
index ce3dc56..b58095d 100644
--- a/gcc/diagnostic.def
+++ b/gcc/diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9c7fe18..e6f58f1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
 * Debugging Options::   Producing debuggable code.
 * Optimize Options::    How much optimization?
 * Instrumentation Options:: Enabling profiling and extra run-time error checking.
+* Remarks::             Details on how your code is being optimized.
 * Preprocessor Options:: Controlling header files and macro definitions.
                          Also, getting dependency information for Make.
 * Assembler Options::   Passing options to the assembler.
@@ -416,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
 -freorder-blocks-algorithm=@var{algorithm} @gol
 -freorder-blocks-and-partition  -freorder-functions @gol
 -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
--frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
+-frounding-math  -fsave-optimization-record @gol
+-fsched2-use-superblocks  -fsched-pressure @gol
 -fsched-spec-load  -fsched-spec-load-dangerous @gol
 -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
 -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
@@ -479,6 +481,10 @@ Objective-C and Objective-C++ Dialects}.
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
 
+@item Remarks
+@xref{Remarks,,Options to Control Remarks from the Compiler}.
+@gccoptlist{-fremarks}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -9854,6 +9860,15 @@ Future versions of GCC may provide finer control of this setting
 using C99's @code{FENV_ACCESS} pragma.  This command-line option
 will be used to specify the default state for @code{FENV_ACCESS}.
 
+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+FIXME: The precise format is not yet set in stone, but it ought
+to be stabilized and then documented somewhere.
+FIXME: should this be described here within the optimization options,
+or within the developer options?
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
@@ -12126,6 +12141,23 @@ The NOP instructions are inserted at---and maybe before, depending on
 @end table
 
 
+@node Remarks
+@section Options to Control Remarks from the Compiler
+@cindex remarks
+@cindex options, remarks
+
+These options are aimed at advanced users who may be interested
+in seeing additional diagnostics from the compiler, giving information
+on the decisions it is making on the code.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+FIXME: better description needed here.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 6af1445..038cdb5 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -73,14 +73,6 @@ static struct dump_file_info dump_files[TDI_end] =
   DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
 };
 
-/* Define a name->number mapping for a dump flag value.  */
-template <typename ValueType>
-struct kv_pair
-{
-  const char *const name;	/* the name of the value */
-  const ValueType value;	/* the value of the name */
-};
-
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
 static const kv_pair<dump_flags_t> dump_options[] =
@@ -131,7 +123,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 };
 
 /* Flags used for -fopt-info groups.  */
-static const kv_pair<optgroup_flags_t> optgroup_options[] =
+const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -344,7 +336,7 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
 
 /* Print source location on DFILE if enabled.  */
 
-void
+static void
 dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
 {
   if (dump_kind)
@@ -360,6 +352,18 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Print source location on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_loc (dump_flags_t dump_kind, source_location loc)
+{
+  if (dump_file && (dump_kind & pflags))
+    dump_loc (dump_kind, dump_file, loc);
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    dump_loc (dump_kind, alt_dump_file, loc);
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index b5582f7..3c4d5a6 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -277,6 +277,7 @@ extern const char *dump_flag_name (int);
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
 extern void dump_printf_loc (dump_flags_t, source_location,
                              const char *, ...) ATTRIBUTE_PRINTF_3;
+extern void dump_loc (dump_flags_t dump_kind, source_location loc);
 extern void dump_function (int phase, tree fn);
 extern void dump_basic_block (int, basic_block, int);
 extern void dump_generic_expr_loc (int, source_location, int, tree);
@@ -311,6 +312,8 @@ dump_enabled_p (void)
   return (dump_file || alt_dump_file);
 }
 
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
+
 namespace gcc {
 
 class dump_manager
diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
index 565fa83..a10b6aa 100644
--- a/gcc/fortran/gfc-diagnostic.def
+++ b/gcc/fortran/gfc-diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 0db5528..68455f0 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1724,7 +1724,8 @@ open_base_files (void)
       "tree-dfa.h", "tree-ssa.h", "reload.h", "cpp-id-data.h", "tree-chrec.h",
       "except.h", "output.h",  "cfgloop.h", "target.h", "lto-streamer.h",
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
-      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h", NULL
+      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h",
+      "optinfo.h", NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 819a962..abd363c 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,6 +105,7 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
+	  test_flag("Remark", flags,  " | CL_REMARK") \
 	  test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
 	sub( "^0 \\| ", "", result )
 	return result
diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
new file mode 100644
index 0000000..2f3ebf8
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,141 @@
+/* Emit optimization information as "remark" diagnostics.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "gimple.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-internal.h"
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_translate_identifiers (&pp) = false;
+
+  bool show_color = pp_show_color (global_dc->printer);
+
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_optinfo_scope_depth (); i > 0; i--)
+    pp_space (&pp);
+
+  /* Print the items into PP.  */
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text
+	      = (const optinfo_item_text *)item;
+	    pp_string (&pp, as_text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree
+	      = (const optinfo_item_tree *)item;
+	    pp_begin_quote (&pp, show_color);
+	    dump_generic_node (&pp, as_tree->get_node (), 0, TDF_DETAILS,
+			       false);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_string (&pp, node->dump_name ());
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	}
+    }
+
+  /* Add metadata: which pass?  */
+  if (current_pass)
+    {
+      pp_string (&pp, " [");
+      pp_string (&pp, colorize_start (show_color,
+				      diagnostic_get_color_for_kind (DK_REMARK)));
+      pp_string (&pp, "pass=");
+      pp_string (&pp, current_pass->name);
+      pp_string (&pp, colorize_stop (show_color));
+      pp_string (&pp, "]");
+    }
+
+  /* Add metadata: hotness.  */
+  gimple *stmt = optinfo->get_location ().m_stmt;
+  if (stmt)
+    if (stmt->bb)
+      if (stmt->bb->count.initialized_p ())
+	{
+	  profile_count count = stmt->bb->count;
+	  pp_string (&pp, " [");
+	  pp_string (&pp, colorize_start (show_color,
+					  diagnostic_get_color_for_kind (DK_NOTE)));
+	  pp_string (&pp, "count(");
+	  pp_string (&pp, profile_quality_as_string (count.quality ()));
+	  pp_string (&pp, ")=");
+	  pp_scalar (&pp, "%li", count.to_gcov_type ());
+	  pp_string (&pp, colorize_stop (show_color));
+	  pp_string (&pp, "]");
+	}
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
new file mode 100644
index 0000000..820cefd
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.h
@@ -0,0 +1,26 @@
+/* Emit optimization information as "remark" diagnostics.
+   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_OPTINFO_EMIT_DIAGNOSTICS_H
+#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
+
+extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
diff --git a/gcc/optinfo-emit-fopt-info.cc b/gcc/optinfo-emit-fopt-info.cc
new file mode 100644
index 0000000..b01a301
--- /dev/null
+++ b/gcc/optinfo-emit-fopt-info.cc
@@ -0,0 +1,100 @@
+/* Emit optimization information via -fopt-info.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "tree.h"
+#include "tree-pass.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-fopt-info.h"
+#include "optinfo-internal.h"
+
+/* Emit OPTINFO via -fopt-info.  */
+
+void
+emit_optinfo_via_fopt_info (const optinfo *optinfo)
+{
+  dump_flags_t flags = TDF_NONE;
+  switch (optinfo->get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      flags = MSG_OPTIMIZED_LOCATIONS;
+      break;
+    case OPTINFO_KIND_FAILURE:
+      flags = MSG_MISSED_OPTIMIZATION;
+      break;
+    case OPTINFO_KIND_NOTE:
+    case OPTINFO_KIND_SCOPE:
+      flags = MSG_NOTE;
+      break;
+    }
+  if (optinfo->details_p ())
+    flags |= TDF_DETAILS;
+
+  dump_loc (flags, optinfo->get_location_t ());
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *text = (const optinfo_item_text *)item;
+	    dump_printf (flags, "%s", text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *tree = (const optinfo_item_tree *)item;
+	    dump_generic_expr (flags, TDF_DETAILS, tree->get_node ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    dump_gimple_stmt (flags, TDF_SLIM, stmt, 0);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    dump_printf (flags, "%s", node->dump_name ());
+	  }
+	  break;
+	}
+    }
+  dump_printf (flags, "\n");
+}
diff --git a/gcc/optinfo-emit-fopt-info.h b/gcc/optinfo-emit-fopt-info.h
new file mode 100644
index 0000000..35868ab
--- /dev/null
+++ b/gcc/optinfo-emit-fopt-info.h
@@ -0,0 +1,26 @@
+/* Emit optimization information via -fopt-info.
+   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_OPTINFO_EMIT_FOPT_INFO_H
+#define GCC_OPTINFO_EMIT_FOPT_INFO_H
+
+extern void emit_optinfo_via_fopt_info (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_FOPT_INFO_H */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..0cda963
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,408 @@
+/* Emit optimization information as JSON files.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+impl_location_to_json (optinfo_impl_location loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+static json::object *
+profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Create a JSON object representing PASS.  */
+
+static json::object *
+pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+static json::value *
+inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  obj->set ("fndecl",
+		    new json::string (lang_hooks.decl_printable_name (fndecl, 2)));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* The array of optimization records.
+   Currently the JSON values are stored in memory, and flushed when the
+   compiler exits.  It would probably be better to simply write out
+   the JSON as we go.  */
+
+static json::array *records;
+
+/* The currently open scopes, for expressing nested optimization records.  */
+
+static vec<json::array *> scopes;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  records = new json::array ();
+
+  scopes.safe_push (records);
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      records->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+
+  delete records;
+  records = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return records != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to then queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  gimple *stmt = optinfo->get_location ().m_stmt;//get_best_stmt ();
+
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text = (const optinfo_item_text *)item;
+	    message->append (new json::string (as_text->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree = (const optinfo_item_tree *)item;
+	    tree node = as_tree->get_node ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+	         dump_generic_expr (MSG_NOTE, flags, node);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+	         print_generic_expr (alt_dump_file, node, dump_flags | extra_dump_flags);
+	       which is:
+		 maybe_init_pretty_print (file);
+		 dump_generic_node (tree_pp, node, 0, flags, false);
+		 pp_flush (tree_pp);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_translate_identifiers (&pp) = false;
+
+	    dump_generic_node (&pp, node, 0, as_tree->get_flags (), false);
+
+	    item->set ("expr", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the node.  */
+	    if (EXPR_HAS_LOCATION (node))
+	      item->set ("location", location_to_json (EXPR_LOCATION (node)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+		 dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+		 print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
+	       which is:
+		 pp_needs_newline (&buffer) = true;
+		 buffer.buffer->stream = file;
+		 pp_gimple_stmt_1 (&buffer, g, spc, flags);
+		 pp_newline_and_flush (&buffer);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    item->set ("stmt", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the stmt.  */
+	    if (gimple_location (stmt))
+	      item->set ("location", location_to_json (gimple_location (stmt)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    json::object *item = new json::object ();
+	    item->set ("name", new json::string (node->name ()));
+	    item->set ("order", new json::number (node->order));
+	    if (DECL_SOURCE_LOCATION (node->decl) != UNKNOWN_LOCATION)
+	      item->set ("location",
+			 location_to_json (DECL_SOURCE_LOCATION (node->decl)));
+	    message->append (item);
+	  }
+	  break;
+	}
+   }
+
+  if (current_pass)
+    obj->set ("pass", pass_to_json (current_pass));
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (stmt)
+    {
+      loc = gimple_location (stmt);
+      if (stmt->bb)
+	if (stmt->bb->count.initialized_p ())
+	  obj->set ("count", profile_count_to_json (stmt->bb->count));
+    }
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  /* Add to innermost scope.  */
+  gcc_assert (scopes.length () > 0);
+  scopes[scopes.length () - 1]->append (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      scopes.safe_push (children);
+    }
+}
+
+/* Handling for the end of an optinfo scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_optinfo_scope ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  scopes.pop ();
+}
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..f228c67
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,37 @@
+/* Emit optimization information as JSON files.
+   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_OPTINFO_EMIT_JSON_H
+#define GCC_OPTINFO_EMIT_JSON_H
+
+class optinfo;
+struct opt_pass;
+
+extern void optimization_records_start ();
+extern void optimization_records_finish ();
+
+// FIXME: maybe make this inline?
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_optinfo_scope ();
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..b2c1b26
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,139 @@
+/* Implementation details of optinfo.
+   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_OPTINFO_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (optinfo_tree node_with_flags)
+    : m_node_with_flags (node_with_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node_with_flags.m_node; }
+  dump_flags_t get_flags () const { return m_node_with_flags.m_flags; }
+
+ private:
+  optinfo_tree m_node_with_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt) : m_stmt (stmt) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+
+ private:
+  gimple *m_stmt;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..6e227ce
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,272 @@
+/* Optimization information.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cfgloop.h" // for struct loop and loop_p, etc
+#include "tree-scalar-evolution.h" // for get_loop_exit_condition
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-emit-fopt-info.h"
+#include "optinfo-internal.h"
+
+/* Constructing an optinfo_location from a basic block.  */
+
+optinfo_location::optinfo_location (basic_block bb)
+  : m_stmt (NULL)
+{
+  // FIXME:
+  // TODO: deal with both RTL and gimple
+  if (bb->flags & BB_RTL)
+    ;
+  else
+    {
+       /* Use the first stmt in the bb that has a location.  */
+       gimple *stmt = NULL;
+       for (gimple_stmt_iterator si = gsi_start_bb (bb);
+	    !gsi_end_p (si); gsi_next (&si))
+	{
+	  stmt = gsi_stmt (si);
+	  if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+	    {
+		m_stmt = stmt;
+		return;
+	    }
+	}
+	/* Otherwise, just use any stmt for now.  */
+        m_stmt = stmt;
+    }
+}
+
+/* Constructing an optinfo_location from a loop.  */
+
+optinfo_location::optinfo_location (struct loop *loop)
+  : m_stmt (NULL)
+{
+  if (loop)
+    {
+      m_stmt = get_loop_exit_condition (loop);
+      if (m_stmt)
+	return;
+
+      gimple_stmt_iterator si;
+      for (si = gsi_start_bb (loop->header); !gsi_end_p (si); gsi_next (&si))
+	{
+	  gimple *stmt = gsi_stmt (si);
+	  if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+	    {
+	      m_stmt = stmt;
+	      return;
+	    }
+	}
+    }
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Flush the pending information.  */
+  emit ();
+
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Get a location_t from an optinfo.  */
+
+location_t
+optinfo::get_location_t () const
+{
+  if (m_loc.m_stmt)
+    return gimple_location (m_loc.m_stmt);
+  else
+    return UNKNOWN_LOCATION;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit () const
+{
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
+
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
+
+  /* -fopt-info.  */
+  emit_optinfo_via_fopt_info (this);
+}
+
+/* pending_optinfo's ctor.  */
+
+pending_optinfo::pending_optinfo (const optinfo_impl_location &impl_location,
+				  enum optinfo_kind kind,
+				  optinfo_location loc,
+				  bool details)
+  : m_optinfo (new optinfo (impl_location, kind, loc, details))
+{
+}
+
+/* Append a string literal to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const char *msg)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (msg), false);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* Append a tree node with flags to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (optinfo_tree node_with_flags)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_tree (node_with_flags));
+  return *this;
+}
+
+/* Append a gimple statement to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (gimple *stmt)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_gimple (stmt));
+  return *this;
+}
+
+/* Append a symbol table node to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (symtab_node *node)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_symtab_node (node));
+  return *this;
+}
+
+/* Append printf-formatted text to this pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const optinfo_printf &p)
+{
+  // FIXME: there are some double-copies going on here
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (p.get_formatted_text ()), true);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   pending_optinfo.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const optinfo_print_dec &arg)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (arg.m_wi, buf, arg.m_sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_optinfo->m_items.safe_push (item);
+  return *this;
+}
+
+/* The current nesting depth of optinfo scopes, for use
+   by remarks (for showing nesting via indentation).  */
+
+static unsigned int scope_depth;
+
+/* Push a nested optinfo scope.  */
+
+void
+push_optinfo_scope ()
+{
+  scope_depth++;
+}
+
+/* Pop a nested optinfo scope.  */
+
+void
+pop_optinfo_scope ()
+{
+  scope_depth--;
+  optimization_records_maybe_pop_optinfo_scope ();
+}
+
+/* Get the current optinfo scope-nesting depth.
+   For use by remarks (for showing nesting via indentation).  */
+
+unsigned int
+get_optinfo_scope_depth ()
+{
+  return scope_depth;
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  return dump_enabled_p () || optimization_records_enabled_p () || flag_remarks;
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return optimization_records_enabled_p ();
+}
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..0870e8a
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,389 @@
+/* Optimization information.
+   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_OPTINFO_H
+#define GCC_OPTINFO_H
+
+#include "unique-ptr.h"
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations:
+
+   * the "-fopt-info" destination(s)
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file via "-fsave-optimization-record"
+
+   Building an optinfo instance is non-trivial, so all usage should be
+   guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.
+
+   They're intended to be be short-lived; in particular, there's no
+   interaction with GTY: it's assumed that no GC happens during the
+   lifetime of an optinfo.  */
+
+/* Forward decls.  */
+class pending_optinfo;
+
+/* optinfo-internal.h.  */
+class optinfo_item;
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* A class for describing a source-code location for an optinfo,
+   with various constructors for convenience.
+   In particular, this lets us associate optinfo instances
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.
+   Currently this is GTY-marked, so that vect_optinfo_location can be a
+   GC root.  */
+
+class GTY(()) optinfo_location
+{
+ public:
+  optinfo_location () : m_stmt (NULL) {}
+  optinfo_location (gimple *stmt) : m_stmt (stmt) {}
+  optinfo_location (basic_block bb);
+  optinfo_location (struct loop *loop);
+
+  gimple *m_stmt;
+  // Or maybe a basic_block and a location_t, so that we can support RTL.
+};
+
+/* A way to identify where in the compiler source that optimization information
+   is being emitted from.  */
+/* FIXME: taken from selftest::location; should this be refactored?  */
+
+struct optinfo_impl_location
+{
+  optinfo_impl_location (const char *file, int line, const char *function)
+    : m_file (file), m_line (line), m_function (function) {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* The current source location, expressed as an optinfo_impl_location.  */
+
+#define OPTINFO_IMPL_LOCATION \
+  (optinfo_impl_location (__FILE__, __LINE__, __FUNCTION__))
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.
+
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo
+{
+  friend class pending_optinfo;
+ public:
+  optinfo (const optinfo_impl_location &impl_location,
+	   enum optinfo_kind kind,
+	   optinfo_location loc,
+	   bool details)
+  : m_impl_location (impl_location), m_kind (kind), m_loc (loc), m_items (),
+    m_details (details)
+  {}
+  ~optinfo ();
+
+  optinfo_impl_location get_impl_location () const { return m_impl_location; }
+  enum optinfo_kind get_kind () const { return m_kind; }
+  optinfo_location get_location () const { return m_loc; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+  bool details_p () const { return m_details; }
+
+  location_t get_location_t () const;
+
+ private:
+   void emit () const;
+
+ private:
+  optinfo_impl_location m_impl_location;
+  enum optinfo_kind m_kind;
+  optinfo_location m_loc;
+  auto_vec <optinfo_item *> m_items;
+  /* If true, gate this with TDF_DETAILS when emitting to dump/-fopt-info.  */
+  bool m_details;
+};
+
+/* Various helper classes and functions for adding messages and data
+   to an optinfo.
+   All of these are used via "<<" on a pending optinfo, e.g.
+      OPTINFO_NOTE (loop)
+         << optinfo_printf ("something about loop %d: ", loop->num)
+	 << slim (expr);  */
+
+/* Packaging up a tree with dump flags.  */
+// FIXME: should this be GTY-marked?
+
+struct optinfo_tree
+{
+  optinfo_tree (tree node, dump_flags_t flags)
+    : m_node (node), m_flags (flags)
+  {
+    /* Only the TDF_* flags are allowed.  */
+    gcc_assert ((m_flags & MSG_ALL) == 0);
+    // FIXME: what happened to TDF_KIND_MASK, mentioned in dumpfile.h ?
+  }
+
+  tree m_node;
+  dump_flags_t m_flags;
+};
+
+/* Helper function for adding a "slim" representation of a tree to
+   an optinfo, e.g.:
+      OPTINFO_NOTE (stmt) << "expr is: " << slim (expr);  */
+
+static inline optinfo_tree slim (tree node)
+{
+  return optinfo_tree (node, TDF_SLIM);
+}
+
+/* Helper function for adding a "detailed" representation of a tree to
+   an optinfo, e.g.:
+      OPTINFO_NOTE (stmt) << "expr is: " << details (expr);  */
+
+static inline optinfo_tree details (tree node)
+{
+  return optinfo_tree (node, TDF_DETAILS);
+}
+
+/* A class for adding printf-style formatted strings to an optinfo,
+   e.g.:
+     OPTINFO_SUCCESS (edge->call_stmt)
+       << optinfo_printf ("devirtualizing call in %s to %s",
+                          edge->caller->dump_name (),
+                          target->dump_name ());  */
+class optinfo_printf
+{
+ public:
+  optinfo_printf (const char *fmt, ...)
+   ATTRIBUTE_PRINTF_2
+  {
+    va_list ap;
+    va_start (ap, fmt);
+    m_formatted_text = xvasprintf (fmt, ap);
+    va_end (ap);
+  }
+
+  ~optinfo_printf () { free (m_formatted_text); } // FIXME: really?
+
+  const char *get_formatted_text () const { return m_formatted_text; }
+
+ private:
+  char *m_formatted_text;
+};
+
+/* Helper struct for adding a wide_int_ref to an optinfo.  */
+
+struct optinfo_print_dec
+{
+  optinfo_print_dec (const wide_int_ref &wi, signop sgn)
+  : m_wi (wi), m_sgn (sgn) {}
+
+  const wide_int_ref &m_wi;
+  signop m_sgn;
+};
+
+/* Helper function for adding a signed decimal representation of a
+   wide_int_ref to an optinfo.  */
+
+static inline optinfo_print_dec
+decs (const wide_int_ref &wi)
+{
+  return optinfo_print_dec (wi, SIGNED);
+}
+
+/* Helper function for adding an unsigned decimal representation of
+   a wide_int_ref to an optinfo.  */
+
+static inline optinfo_print_dec
+decu (const wide_int_ref &wi)
+{
+  return optinfo_print_dec (wi, UNSIGNED);
+}
+
+/* Support class for building and emitting optinfo instances.
+   Intended to be used via the macros below, e.g.:
+
+     if (optinfo_enabled_p ())
+       OPTINFO_NOTE (stmt) << "some message about: " << stmt;
+
+   or, if control flow is needed:
+
+     if (optinfo_enabled_p ())
+       {
+         pending_optinfo info = OPTINFO_NOTE (stmt);
+         info << "some message about: " << stmt;
+         if (some_condition)
+	   info << " etc";
+         else
+           info << " some other message";
+       }
+
+   The info is emitted to all active sinks when the pending_optinfo
+   goes out of scope.  */
+
+class pending_optinfo
+{
+ public:
+  pending_optinfo (const optinfo_impl_location &impl_location,
+		   enum optinfo_kind kind,
+		   optinfo_location loc,
+		   bool details);
+
+  /* Pre-canned ways of adding information to the optinfo.  */
+  pending_optinfo& operator<< (const char *);
+  pending_optinfo& operator<< (optinfo_tree);
+  pending_optinfo& operator<< (gimple *);
+  pending_optinfo& operator<< (symtab_node *node);
+  pending_optinfo& operator<< (const optinfo_printf &);
+  pending_optinfo& operator<< (const optinfo_print_dec &);
+
+  template<unsigned int N, typename C>
+  pending_optinfo& operator<< (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      *this << optinfo_print_dec (value.coeffs[0], sgn);
+    else
+      {
+	*this << "[";
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    *this << optinfo_print_dec (value.coeffs[i], sgn);
+	    *this << (i == N - 1 ? "]" : ",");
+	  }
+      }
+    return *this;
+  }
+
+ private:
+  // FIXME: are we allowed this yet?  or do we need a gnu::shared_ptr?
+  std::shared_ptr <optinfo> m_optinfo;
+};
+
+/* Emit optimization information at LOC of the given KIND.  */
+
+#define OPTINFO(KIND, LOC) \
+  pending_optinfo ((OPTINFO_IMPL_LOCATION), (KIND), (LOC), false)
+
+/* Emit that a successful optimization happened at LOC.  */
+
+#define OPTINFO_SUCCESS(LOC)\
+  OPTINFO(OPTINFO_KIND_SUCCESS, (LOC))
+
+/* Emit that a failed optimization happened at LOC.  */
+
+#define OPTINFO_FAILURE(LOC)\
+  OPTINFO(OPTINFO_KIND_FAILURE, (LOC))
+
+/* Emit a note relating to an optimization at LOC.  */
+
+#define OPTINFO_NOTE(LOC)\
+  OPTINFO(OPTINFO_KIND_NOTE, (LOC))
+
+/* Emit optimization information at LOC of the given KIND,
+   tagging it with TDF_DETAILS for -fopt-info.  */
+
+#define OPTINFO_DETAILS(KIND, LOC) \
+  pending_optinfo ((OPTINFO_IMPL_LOCATION), (KIND), (LOC), true)
+
+/* Emit that a successful optimization happened at LOC,
+   tagging it with TDF_DETAILS for -fopt-info.  */
+
+#define OPTINFO_SUCCESS_DETAILS(LOC)\
+  OPTINFO_DETAILS(OPTINFO_KIND_SUCCESS, (LOC))
+
+extern void push_optinfo_scope ();
+extern void pop_optinfo_scope ();
+extern unsigned int get_optinfo_scope_depth ();
+
+/* Implementation detail of the OPTINFO_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit optinfo remarks
+   about entering and exiting the body of a given function.  */
+
+class optinfo_scope
+{
+ public:
+  optinfo_scope (const char *name, optinfo_location loc,
+		 const optinfo_impl_location &impl_location)
+  : m_name (name), m_loc (loc)
+  {
+    if (optinfo_enabled_p ())
+      {
+	pending_optinfo (impl_location, OPTINFO_KIND_SCOPE, loc, false)
+	  << optinfo_printf ("=== %s ===", name);
+	push_optinfo_scope ();
+      }
+  }
+  ~optinfo_scope ()
+  {
+    if (optinfo_enabled_p ())
+      pop_optinfo_scope ();
+  }
+
+ private:
+  const char *m_name;
+  optinfo_location m_loc;
+};
+
+/* A macro for emitting an optinfo note about entering a scope,
+   pushing and popping the scope, so that all optinfos "within"
+   the scope are nested within it.  */
+
+#define OPTINFO_SCOPE(NAME, LOC) \
+  optinfo_scope scope (NAME, LOC, (OPTINFO_IMPL_LOCATION))
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/opts.c b/gcc/opts.c
index 33efcc0..8029c08 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
 	case CL_WARNING:
 	  description = _("The following options control compiler warning messages");
 	  break;
+	case CL_REMARK:
+	  description = _("The following options control compiler remarks");
+	  break;
 	case CL_OPTIMIZATION:
 	  description = _("The following options control optimizations");
 	  break;
@@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
 	      { "optimizers", CL_OPTIMIZATION },
 	      { "target", CL_TARGET },
 	      { "warnings", CL_WARNING },
+	      { "remarks", CL_REMARK },
 	      { "undocumented", CL_UNDOCUMENTED },
 	      { "params", CL_PARAMS },
 	      { "joined", CL_JOINED },
diff --git a/gcc/opts.h b/gcc/opts.h
index 484fc1c..31ea706 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -135,20 +135,21 @@ extern const unsigned int cl_lang_count;
 #define CL_DRIVER		(1U << 19) /* Driver option.  */
 #define CL_TARGET		(1U << 20) /* Target-specific option.  */
 #define CL_COMMON		(1U << 21) /* Language-independent.  */
+#define CL_REMARK		(1U << 22) /* Enables an (optional) remark.  */
 
 #define CL_MIN_OPTION_CLASS	CL_PARAMS
-#define CL_MAX_OPTION_CLASS	CL_COMMON
+#define CL_MAX_OPTION_CLASS	CL_REMARK
 
 /* From here on the bits describe attributes of the options.
    Before this point the bits have described the class of the option.
    This distinction is important because --help will not list options
    which only have these higher bits set.  */
 
-#define CL_JOINED		(1U << 22) /* If takes joined argument.  */
-#define CL_SEPARATE		(1U << 23) /* If takes a separate argument.  */
-#define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
-#define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
-#define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_JOINED		(1U << 23) /* If takes joined argument.  */
+#define CL_SEPARATE		(1U << 24) /* If takes a separate argument.  */
+#define CL_UNDOCUMENTED		(1U << 25) /* Do not output with --help.  */
+#define CL_NO_DWARF_RECORD	(1U << 26) /* Do not add to producer string.  */
+#define CL_PCH_IGNORE		(1U << 27) /* Do compare state for pch.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@ enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@ public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5a19fc9..1f0a079 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -96,6 +96,8 @@ set plugin_test_list [list \
 	  must-tail-call-2.c } \
     { expensive_selftests_plugin.c \
 	  expensive-selftests-1.c } \
+    { remarks_plugin.c \
+	  remarks-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
new file mode 100644
index 0000000..c42ad96
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-fremarks" } */
+
+extern void test_string_literal (void);
+extern void test_tree (void);
+extern void test_gimple (int);
+extern void test_cgraph_node (void);
+extern void test_printf (void);
+extern void test_wide_int_signed (void);
+extern void test_wide_int_unsigned (void);
+extern void test_poly_int (void);
+extern void test_scopes (void);
+
+void test_remarks (void)
+{
+  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
+  test_tree (); /* { dg-remark "test of tree: '0'" } */
+  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
+  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
+  test_printf (); /* { dg-remark "test of optinfo_printf: 42" } */
+  test_wide_int_signed (); /* { dg-remark "test of wide int: 0" } */
+  test_wide_int_unsigned (); /* { dg-remark "test of wide int: 0" } */
+  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
+
+  test_scopes (); /* { dg-line test_scopes_line } */
+  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
new file mode 100644
index 0000000..09fa4a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,158 @@
+/* Test of remark-emission by optinfo.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "optinfo.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cgraph.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_remarks =
+{
+  GIMPLE_PASS, /* type */
+  "test_remarks", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_remarks : public gimple_opt_pass
+{
+public:
+  pass_test_remarks(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_remarks, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_remarks
+
+unsigned int
+pass_test_remarks::execute (function *fun)
+{
+  basic_block bb;
+  
+  FOR_ALL_BB_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+	 !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+	gimple *stmt = gsi_stmt (gsi);
+	gcall *call = dyn_cast <gcall *> (stmt);
+	if (!call)
+	  continue;
+	tree callee_decl = gimple_call_fndecl (call);
+	if (!callee_decl)
+	  continue;
+	tree callee_name = DECL_NAME (callee_decl);
+	if (!callee_name)
+	  continue;
+	const char *callee = IDENTIFIER_POINTER (callee_name);
+
+	/* Various optinfo tests, done at callsites,
+	   controlled by the callee name.  */
+	if (strcmp (callee, "test_string_literal") == 0)
+	  {
+	    OPTINFO_NOTE (stmt) << "test of remark for " << callee;
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of tree: "
+	      << slim (integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of gimple: "
+	      << stmt;
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of callgraph node: "
+	      << cgraph_node::get (callee_decl);
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << optinfo_printf ("test of optinfo_printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int_signed") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of wide int: "
+	      << decs (wi::to_wide (integer_zero_node));
+	  }
+	else if (strcmp (callee, "test_wide_int_unsigned") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of wide int: "
+	      << decu (wi::to_wide (integer_zero_node));
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    OPTINFO_NOTE (stmt)
+	      << "test of poly int: "
+	      << poly_int64 (42);
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    OPTINFO_SCOPE ("outer scope", stmt);
+	    {
+	      OPTINFO_NOTE (stmt) << "at outer scope";
+	      OPTINFO_SCOPE ("middle scope", stmt);
+	      {
+		OPTINFO_NOTE (stmt) << "at middle scope";
+		OPTINFO_SCOPE ("innermost scope", stmt);
+		OPTINFO_NOTE (stmt) << "at innermost scope";
+	      }
+	    }
+	  }
+      }
+
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_remarks (gcc::context *ctxt)
+{
+  return new pass_test_remarks (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  pass_info.pass = make_pass_test_remarks (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index 3770f69..4d7a450 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1152,6 +1152,15 @@ proc dg-locus { args } {
     verbose "process-message:\n${dg-messages}" 2
 }
 
+# Handle remarks.
+
+proc dg-remark { args } {
+    # Make this variable available here and to the saved proc.
+    upvar dg-messages dg-messages
+
+    process-message saved-dg-error "remark: " "$args"
+}
+
 # Check the existence of a gdb in the path, and return true if there
 # is one.
 #
diff --git a/gcc/toplev.c b/gcc/toplev.c
index b066bcc..cb12f73 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -488,6 +489,8 @@ compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2094,6 +2097,8 @@ do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 62316ba..9ff03fe 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -526,7 +527,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 04/10] Use indentation to show nesting for -fopt-info
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
  2018-05-29 20:32 ` [PATCH 06/10] Experiments with using optinfo for inlining David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 08/10] Experiment with using optinfo for devirtualization David Malcolm
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This converts e.g. from:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note: === analyze_loop_nest ===
test.c:8:3: note: === vect_analyze_loop_form ===
test.c:8:3: note: === get_loop_niters ===
test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

to:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note:  === analyze_loop_nest ===
test.c:8:3: note:   === vect_analyze_loop_form ===
test.c:8:3: note:    === get_loop_niters ===
test.c:8:3: note:   symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note:   not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

showing the nesting of the optimization analysis and where each
message is being emitted from within it (this is of more use as
the number of messages and the nesting depth increases)

gcc/ChangeLog:
	* optinfo-emit-fopt-info.cc (emit_optinfo_via_fopt_info): Use
	indentation to show nesting.
---
 gcc/optinfo-emit-fopt-info.cc | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/gcc/optinfo-emit-fopt-info.cc b/gcc/optinfo-emit-fopt-info.cc
index b01a301..842681f 100644
--- a/gcc/optinfo-emit-fopt-info.cc
+++ b/gcc/optinfo-emit-fopt-info.cc
@@ -58,7 +58,13 @@ emit_optinfo_via_fopt_info (const optinfo *optinfo)
   if (optinfo->details_p ())
     flags |= TDF_DETAILS;
 
+  /* Source location and "note: ".  */
   dump_loc (flags, optinfo->get_location_t ());
+
+  /* Indentation to show scope nesting.  */
+  dump_printf (flags, "%*s", get_optinfo_scope_depth (), "");
+
+  /* The text itself.  */
   for (unsigned i = 0; i < optinfo->num_items (); i++)
     {
       const optinfo_item *item = optinfo->get_item (i);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 07/10] Experiment with using optinfo for loop-handling
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (5 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 10/10] Experiment with optinfo in tree-ssa-loop-im.c David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc David Malcolm
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

gcc/ChangeLog:
	* tree-ssa-loop-ivcanon.c: Include "optinfo.h".
	(try_unroll_loop_completely): Port to optinfo.
	(canonicalize_loop_induction_variables): Use OPTINFO_NOTE.
	* tree-ssa-loop-niter.c: Include "optinfo.h".
	(number_of_iterations_exit): Port to OPTINFO_FAILURE.
---
 gcc/tree-ssa-loop-ivcanon.c | 38 ++++++++++++++++++++++++++++----------
 gcc/tree-ssa-loop-niter.c   |  7 ++++---
 2 files changed, 32 insertions(+), 13 deletions(-)

diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 24bf60e..0ab9d61 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -63,6 +63,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-inline.h"
 #include "tree-cfgcleanup.h"
 #include "builtins.h"
+#include "optinfo.h"
 
 /* Specifies types of loops that may be unrolled.  */
 
@@ -936,21 +937,19 @@ try_unroll_loop_completely (struct loop *loop,
   loops_to_unloop.safe_push (loop);
   loops_to_unloop_nunroll.safe_push (n_unroll);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       if (!n_unroll)
-        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, locus,
-                         "loop turned into non-loop; it never loops\n");
+	OPTINFO_SUCCESS_DETAILS (loop)
+	  << "loop turned into non-loop; it never loops";
       else
         {
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, locus,
-                           "loop with %d iterations completely unrolled",
-			   (int) n_unroll);
+	  pending_optinfo info = OPTINFO_SUCCESS_DETAILS (loop)
+	    << optinfo_printf ("loop with %d iterations completely unrolled",
+			       (int) n_unroll);
           if (loop->header->count.initialized_p ())
-            dump_printf (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS,
-                         " (header execution count %d)",
-                         (int)loop->header->count.to_gcov_type ());
-          dump_printf (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, "\n");
+            info << optinfo_printf (" (header execution count %d)",
+				    (int)loop->header->count.to_gcov_type ());
         }
     }
 
@@ -1240,6 +1239,25 @@ canonicalize_loop_induction_variables (struct loop *loop,
       fprintf (dump_file, "Loop %d likely iterates at most %i times.\n",
 	       loop->num, (int)likely_max_loop_iterations_int (loop));
     }
+  /* FIXME: this duplicates the above usage of "dump_file".  */
+  if (optinfo_enabled_p ())
+    {
+      if (TREE_CODE (niter) == INTEGER_CST)
+	OPTINFO_NOTE (loop)
+	  << optinfo_printf ("loop %d iterates ", loop->num)
+	  << slim (niter)
+	  << " times";
+      else if (maxiter >= 0)
+	OPTINFO_NOTE (loop)
+	  << optinfo_printf ("loop %d iterates at most %i times",
+			     loop->num,
+			     (int)maxiter);
+      else if (likely_max_loop_iterations_int (loop) >= 0)
+	OPTINFO_NOTE (loop)
+	  << optinfo_printf ("loop %d likely iterates at most %i times",
+			     loop->num,
+			     (int)likely_max_loop_iterations_int (loop));
+    }
 
   /* Remove exits that are known to be never taken based on loop bound.
      Needs to be called after compilation of max_loop_iterations_int that
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 7a54c5f..8eee6de 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-scalar-evolution.h"
 #include "params.h"
 #include "tree-dfa.h"
+#include "optinfo.h"
 
 
 /* The maximum number of dominator BBs we search for conditions
@@ -2447,9 +2448,9 @@ number_of_iterations_exit (struct loop *loop, edge exit,
     return true;
 
   if (warn)
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
-		     "missed loop optimization: niters analysis ends up "
-		     "with assumptions.\n");
+    OPTINFO_FAILURE (stmt)
+      << ("missed loop optimization: niters analysis ends up "
+	  "with assumptions");
 
   return false;
 }
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 08/10] Experiment with using optinfo for devirtualization
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
  2018-05-29 20:32 ` [PATCH 06/10] Experiments with using optinfo for inlining David Malcolm
  2018-05-29 20:32 ` [PATCH 04/10] Use indentation to show nesting for -fopt-info David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 02/10] Add JSON implementation David Malcolm
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

gcc/ChangeLog:
	* gimple-fold.c: Include "optinfo.h".
	(fold_gimple_assign): Port to optinfo.
	(gimple_fold_call): Likewise.
	* ipa-devirt.c: Include "optinfo.h".
	(ipa_devirt): Port to optinfo.
	* ipa.c: Include "optinfo.h".
	(walk_polymorphic_call_targets): Port to optinfo.
---
 gcc/gimple-fold.c | 29 ++++++++++++++---------------
 gcc/ipa-devirt.c  | 14 +++++++-------
 gcc/ipa.c         | 17 +++++++----------
 3 files changed, 28 insertions(+), 32 deletions(-)

diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index bd8c44a..d8a7a8b 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -66,6 +66,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "calls.h"
 #include "tree-vector-builder.h"
 #include "tree-ssa-strlen.h"
+#include "optinfo.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -346,15 +347,14 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 		  = possible_polymorphic_call_targets (rhs, stmt, &final);
 		if (final && targets.length () <= 1 && dbg_cnt (devirt))
 		  {
-		    if (dump_enabled_p ())
+		    if (optinfo_enabled_p ())
 		      {
-			location_t loc = gimple_location_safe (stmt);
-			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
-					 "resolving virtual function address "
-					 "reference to function %s\n",
-					 targets.length () == 1
-					 ? targets[0]->name ()
-					 : "NULL");
+			OPTINFO_SUCCESS (stmt)
+			  << optinfo_printf ("resolving virtual function address "
+					     "reference to function %s",
+					     targets.length () == 1
+					     ? targets[0]->name ()
+					     : "NULL");
 		      }
 		    if (targets.length () == 1)
 		      {
@@ -4064,14 +4064,13 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 	  if (final && targets.length () <= 1 && dbg_cnt (devirt))
 	    {
 	      tree lhs = gimple_call_lhs (stmt);
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
-				   "folding virtual function call to %s\n",
-		 		   targets.length () == 1
-		  		   ? targets[0]->name ()
-		  		   : "__builtin_unreachable");
+		  OPTINFO_SUCCESS (stmt)
+		    << optinfo_printf ("folding virtual function call to %s",
+				       targets.length () == 1
+				       ? targets[0]->name ()
+				       : "__builtin_unreachable");
 		}
 	      if (targets.length () == 1)
 		{
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 308b6e6..f2a0785 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -131,6 +131,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 /* Hash based set of pairs of types.  */
 struct type_pair
@@ -3753,14 +3754,13 @@ ipa_devirt (void)
 	      }
 	    else if (dbg_cnt (devirt))
 	      {
-		if (dump_enabled_p ())
+		if (optinfo_enabled_p ())
                   {
-                    location_t locus = gimple_location_safe (e->call_stmt);
-                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
-				     "speculatively devirtualizing call "
-				     "in %s to %s\n",
-				     n->dump_name (),
-				     likely_target->dump_name ());
+		    OPTINFO_SUCCESS (e->call_stmt)
+		      << "speculatively devirtualizing call in "
+		      << n
+		      << " to "
+		      << likely_target;
                   }
 		if (!likely_target->can_be_discarded_p ())
 		  {
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 9330de5..8daa768 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "debug.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 /* Return true when NODE has ADDR reference.  */
 
@@ -222,17 +223,13 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 	    target = cgraph_node::get_create
 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
             {
-	      location_t locus;
-	      if (edge->call_stmt)
-		locus = gimple_location (edge->call_stmt);
-	      else
-		locus = UNKNOWN_LOCATION;
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
-			       "devirtualizing call in %s to %s\n",
-			       edge->caller->dump_name (),
-			       target->dump_name ());
+	      OPTINFO_SUCCESS (edge->call_stmt)
+		<< "devirtualizing call in "
+		<< edge->caller
+		<< " to "
+		<< target;
 	    }
 	  edge = edge->make_direct (target);
 	  if (ipa_fn_summaries)
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 06/10] Experiments with using optinfo for inlining
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 04/10] Use indentation to show nesting for -fopt-info David Malcolm
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

gcc/ChangeLog:
	* ipa-inline.c: Include "optinfo.h".
	(report_inline_failed_reason): Use OPTINFO_FAILURE.
	(flatten_function): Use OPTINFO_SUCCESS.
	(early_inline_small_functions): Likewise.
	* tree-inline.c: Include "optinfo.h".
	(expand_call_inline): Use OPTINFO_SUCCESS.
---
 gcc/ipa-inline.c  | 25 +++++++++++++++++++++++++
 gcc/tree-inline.c | 10 ++++++++++
 2 files changed, 35 insertions(+)

diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index b7f213f..3fd064a 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -120,6 +120,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "optinfo.h"
 
 typedef fibonacci_heap <sreal, cgraph_edge> edge_heap_t;
 typedef fibonacci_node <sreal, cgraph_edge> edge_heap_node_t;
@@ -250,6 +251,18 @@ report_inline_failed_reason (struct cgraph_edge *e)
 	  (dump_file, 2, opts_for_fn (e->caller->decl),
 	   opts_for_fn (e->callee->ultimate_alias_target ()->decl));
     }
+  /* FIXME: partial re-implementation in terms of optinfo.  */
+  if (optinfo_enabled_p ())
+    {
+      OPTINFO_FAILURE (e->call_stmt)
+	<< "not inlinable: "
+	<< e->caller
+	<< " -> "
+	<< e->callee
+	<< ": "
+	<< cgraph_inline_failed_string (e->inline_failed);
+      // FIXME: etc; see the option mismatch code above
+    }
 }
 
  /* Decide whether sanitizer-related attributes allow inlining. */
@@ -2167,6 +2180,12 @@ flatten_function (struct cgraph_node *node, bool early)
 	fprintf (dump_file, " Inlining %s into %s.\n",
 		 xstrdup_for_dump (callee->name ()),
 		 xstrdup_for_dump (e->caller->name ()));
+      if (optinfo_enabled_p ())
+	OPTINFO_SUCCESS (e->call_stmt)
+	  << "inlining "
+	  << callee
+	  << " into "
+	  << e->caller;
       orig_callee = callee;
       inline_call (e, true, NULL, NULL, false);
       if (e->callee != orig_callee)
@@ -2677,6 +2696,12 @@ early_inline_small_functions (struct cgraph_node *node)
 	fprintf (dump_file, " Inlining %s into %s.\n",
 		 xstrdup_for_dump (callee->name ()),
 		 xstrdup_for_dump (e->caller->name ()));
+      if (optinfo_enabled_p ())
+	OPTINFO_SUCCESS (e->call_stmt)
+	  << "inlining "
+	  << callee
+	  << " into "
+	  << e->caller;
       inline_call (e, true, NULL, NULL, false);
       inlined = true;
     }
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 5a0a252..5d3598e 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "sreal.h"
+#include "optinfo.h"
 
 /* I'm not real happy about this, but we need to handle gimple and
    non-gimple trees.  */
@@ -4719,6 +4720,15 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id)
       id->src_node->dump (dump_file);
       id->dst_node->dump (dump_file);
     }
+  /* FIXME.  */
+  if (optinfo_enabled_p ())
+    OPTINFO_SUCCESS(call_stmt)
+      << "Inlining "
+      << id->src_node
+      << " to "
+      << id->dst_node
+      << optinfo_printf (" with frequency %4.2f",
+			 cg_edge->sreal_frequency ().to_double ());
 
   /* This is it.  Duplicate the callee body.  Assume callee is
      pre-gimplified.  Note that we must not alter the caller
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 05/10] Experiment with using optinfo for vectorization
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (3 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 02/10] Add JSON implementation David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-05-29 20:32 ` [PATCH 10/10] Experiment with optinfo in tree-ssa-loop-im.c David Malcolm
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch is a mixture of hand-written changes and a lot
of search-and-replace, converting uses of dump_printf_loc
in the vectorization code to using optinfo.

...and probably a bunch of places that got touched by
the search-and-replace, but need some kind of further clean-up;
it's a prototype.

The use of VECT_OPTINFO_SCOPE means that we capture the
nested structured of what's calling what within the
vectorization code, and where the other messages fit.

gcc/ChangeLog:
	* Makefile.in (GTFILES): Add $(srcdir)/tree-vectorizer.h and
	$(srcdir)/tree-vectorizer.c.
	* gengtype.c (open_base_files): Add "tree-vectorizer.h".
	* tree-vect-data-refs.c: Port various code to use optinfo.  A lot
	of this involved simple search-and-replace.
	(dump_lower_bound): Convert to...
	(operator<<): ...this, for vec_lower_bound.
	* tree-vect-loop-manip.c: Likewise.
	* tree-vect-loop.c: Likewise.
	(emit_optinfo_at_vect_location): New function.
	* tree-vect-patterns.c: Likewise.
	* tree-vect-slp.c: Likewise.
	* tree-vect-stmts.c: Likewise.
	* tree-vectorizer.c: Likewise.
	* tree-vectorizer.h: Include "optinfo.h".
	(vect_optinfo_location): New decl.
	(emit_optinfo_at_vect_location): New decl.
	(OPTINFO_VECT): New macro.
	(OPTINFO_VECT_SUCCESS): New macro.
	(OPTINFO_VECT_FAILURE): New macro.
	(OPTINFO_VECT_NOTE): New macro.
	(VECT_OPTINFO_SCOPE): New macro.
---
 gcc/Makefile.in            |    2 +
 gcc/gengtype.c             |    2 +-
 gcc/tree-vect-data-refs.c  |  979 +++++++++++++++----------------
 gcc/tree-vect-loop-manip.c |   98 ++--
 gcc/tree-vect-loop.c       | 1390 +++++++++++++++++++++-----------------------
 gcc/tree-vect-patterns.c   |  100 ++--
 gcc/tree-vect-slp.c        |  507 ++++++++--------
 gcc/tree-vect-stmts.c      |  617 ++++++++++----------
 gcc/tree-vectorizer.c      |   42 +-
 gcc/tree-vectorizer.h      |   37 ++
 10 files changed, 1808 insertions(+), 1966 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5ae5713..459db0f 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2593,6 +2593,8 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/hsa-common.c \
   $(srcdir)/calls.c \
   $(srcdir)/optinfo.h \
+  $(srcdir)/tree-vectorizer.h \
+  $(srcdir)/tree-vectorizer.c \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 68455f0..06aef6c 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1725,7 +1725,7 @@ open_base_files (void)
       "except.h", "output.h",  "cfgloop.h", "target.h", "lto-streamer.h",
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
       "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h",
-      "optinfo.h", NULL
+      "optinfo.h", "tree-vectorizer.h", NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index ebc56c0..b33213c 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -72,28 +72,29 @@ vect_lanes_optab_supported_p (const char *name, convert_optab optab,
       limit_p = !targetm.array_mode_supported_p (mode, count);
       if (!int_mode_for_size (bits, limit_p).exists (&array_mode))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "no array mode for %s["
-			     HOST_WIDE_INT_PRINT_DEC "]\n",
-			     GET_MODE_NAME (mode), count);
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << optinfo_printf ("no array mode for %s["
+				 HOST_WIDE_INT_PRINT_DEC "]",
+				 GET_MODE_NAME (mode), count);
 	  return false;
 	}
     }
 
   if (convert_optab_handler (optab, array_mode, mode) == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "cannot use %s<%s><%s>\n", name,
-                         GET_MODE_NAME (array_mode), GET_MODE_NAME (mode));
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << optinfo_printf ("cannot use %s<%s><%s>", name,
+			     GET_MODE_NAME (array_mode), GET_MODE_NAME (mode));
       return false;
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "can use %s<%s><%s>\n", name, GET_MODE_NAME (array_mode),
-                     GET_MODE_NAME (mode));
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("can use %s<%s><%s>", name,
+			 GET_MODE_NAME (array_mode),
+			 GET_MODE_NAME (mode));
 
   return true;
 }
@@ -181,11 +182,12 @@ vect_check_nonzero_value (loop_vec_info loop_vinfo, tree value)
     if (checks[i] == value)
       return;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "need run-time check that ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, value);
-      dump_printf (MSG_NOTE, " is nonzero\n");
+      OPTINFO_VECT_NOTE
+	<< "need run-time check that "
+	<< slim (value)
+	<< " is nonzero";
     }
   LOOP_VINFO_CHECK_NONZERO (loop_vinfo).safe_push (value);
 }
@@ -338,32 +340,26 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "versioning for alias not supported for: "
-			       "can't determine dependence between ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				 DR_REF (dra));
-	      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				 DR_REF (drb));
-	      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_FAILURE
+		<< ("versioning for alias not supported for: "
+		    "can't determine dependence between ")
+		<< slim (DR_REF (dra))
+		<< " and "
+		<< slim (DR_REF (drb));
 	    }
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "versioning for alias required: "
-			   "can't determine dependence between ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-			     DR_REF (dra));
-	  dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-			     DR_REF (drb));
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	  OPTINFO_VECT_FAILURE
+	    << ("versioning for alias required: "
+		"can't determine dependence between ")
+	    << slim (DR_REF (dra))
+	    << " and "
+	    << slim (DR_REF (drb));
 	}
 
       /* Add to list of ddrs that need to be tested at run-time.  */
@@ -386,30 +382,26 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "versioning for alias not supported for: "
-			       "bad dist vector for ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				 DR_REF (dra));
-	      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				 DR_REF (drb));
-	      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_FAILURE
+		<< ("versioning for alias not supported for: "
+		    "bad dist vector for ")
+		<< slim (DR_REF (dra))
+		<< " and "
+		<< slim (DR_REF (drb));
 	    }
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "versioning for alias required: "
-                           "bad dist vector for ");
-          dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, DR_REF (dra));
-          dump_printf (MSG_MISSED_OPTIMIZATION,  " and ");
-          dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, DR_REF (drb));
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+          OPTINFO_VECT_FAILURE
+	    << ("versioning for alias required: "
+		"bad dist vector for ")
+	    << slim (DR_REF (dra))
+	    << " and "
+	    << slim (DR_REF (drb));
         }
       /* Add to list of ddrs that need to be tested at run-time.  */
       return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
@@ -426,20 +418,19 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
     {
       int dist = dist_v[loop_depth];
 
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "dependence distance  = %d.\n", dist);
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << optinfo_printf ("dependence distance  = %d", dist);
 
       if (dist == 0)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-	                       "dependence distance == 0 between ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dra));
-	      dump_printf (MSG_NOTE, " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (drb));
-	      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "dependence distance == 0 between "
+		<< slim (DR_REF (dra))
+		<< " and "
+		<< slim (DR_REF (drb));
 	    }
 
 	  /* When we perform grouped accesses and perform implicit CSE
@@ -462,9 +453,9 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	     where loads from the group interleave with the store.  */
 	  if (!vect_preserves_scalar_order_p (DR_STMT (dra), DR_STMT (drb)))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "READ_WRITE dependence in interleaving.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "READ_WRITE dependence in interleaving";
 	      return true;
 	    }
 
@@ -475,9 +466,9 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 		vect_check_nonzero_value (loop_vinfo, indicator);
 	      else if (integer_zerop (indicator))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "access also has a zero step\n");
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE
+		      << "access also has a zero step";
 		  return true;
 		}
 	    }
@@ -489,9 +480,9 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  /* If DDR_REVERSED_P the order of the data-refs in DDR was
 	     reversed (to make distance vector positive), and the actual
 	     distance is negative.  */
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	                     "dependence distance negative.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "dependence distance negative";
 	  /* Record a negative dependence distance to later limit the
 	     amount of stmt copying / unrolling we can perform.
 	     Only need to handle read-after-write dependence.  */
@@ -508,31 +499,30 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  /* The dependence distance requires reduction of the maximal
 	     vectorization factor.  */
 	  *max_vf = abs (dist);
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-	                     "adjusting maximal vectorization factor to %i\n",
-	                     *max_vf);
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << optinfo_printf ("adjusting maximal vectorization factor to %i",
+				 *max_vf);
 	}
 
       if (abs_dist >= *max_vf)
 	{
 	  /* Dependence distance does not create dependence, as far as
 	     vectorization is concerned, in this case.  */
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-	                     "dependence distance >= VF.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << optinfo_printf ("dependence distance >= VF");
 	  continue;
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	               "not vectorized, possible dependence "
-	               "between data-refs ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dra));
-	  dump_printf (MSG_NOTE,  " and ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (drb));
-	  dump_printf (MSG_NOTE,  "\n");
+	  OPTINFO_VECT_FAILURE
+	    << ("not vectorized, possible dependence "
+		"between data-refs ")
+	    << slim (DR_REF (dra))
+	    << " and "
+	    << slim (DR_REF (drb));
 	}
 
       return true;
@@ -554,9 +544,7 @@ vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,
   unsigned int i;
   struct data_dependence_relation *ddr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_ref_dependences ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_data_ref_dependences");
 
   LOOP_VINFO_DDRS (loop_vinfo)
     .create (LOOP_VINFO_DATAREFS (loop_vinfo).length ()
@@ -619,24 +607,22 @@ vect_slp_analyze_data_ref_dependence (struct data_dependence_relation *ddr)
   /* Unknown data dependence.  */
   if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)
     {
-      if  (dump_enabled_p ())
+      if  (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "can't determine dependence between ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, DR_REF (dra));
-	  dump_printf (MSG_MISSED_OPTIMIZATION,  " and ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, DR_REF (drb));
-	  dump_printf (MSG_MISSED_OPTIMIZATION,  "\n");
+	  OPTINFO_VECT_FAILURE
+	    << "can't determine dependence between "
+	    << slim (DR_REF (dra))
+	    << " and "
+	    << slim (DR_REF (drb));
 	}
     }
-  else if (dump_enabled_p ())
+  else if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "determined dependence between ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dra));
-      dump_printf (MSG_NOTE, " and ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (drb));
-      dump_printf (MSG_NOTE,  "\n");
+      OPTINFO_VECT_NOTE
+	<< "determined dependence between "
+	<< slim (DR_REF (dra))
+	<< " and "
+	<< slim (DR_REF (drb));
     }
 
   return true;
@@ -724,9 +710,7 @@ vect_slp_analyze_node_dependences (slp_instance instance, slp_tree node,
 bool
 vect_slp_analyze_instance_dependence (slp_instance instance)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_slp_analyze_instance_dependence ===\n");
+  VECT_OPTINFO_SCOPE ("vect_slp_analyze_instance_dependence");
 
   /* The stores of this instance are at the root of the SLP tree.  */
   slp_tree store = SLP_INSTANCE_TREE (instance);
@@ -784,19 +768,16 @@ vect_record_base_alignment (vec_info *vinfo, gimple *stmt,
   if (!existed || entry->base_alignment < drb->base_alignment)
     {
       entry = drb;
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "recording new base alignment for ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, drb->base_address);
-	  dump_printf (MSG_NOTE, "\n");
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "  alignment:    %d\n", drb->base_alignment);
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "  misalignment: %d\n", drb->base_misalignment);
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "  based on:     ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  OPTINFO_VECT_NOTE
+	    << "recording new base alignment for "
+	    << slim (drb->base_address)
+	    << "\n"
+	    << optinfo_printf ("  alignment:    %d\n", drb->base_alignment)
+	    << optinfo_printf ("  misalignment: %d\n", drb->base_misalignment)
+	    << optinfo_printf ("  based on:     ")
+	    << stmt;
 	}
     }
 }
@@ -867,9 +848,9 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
   tree ref = DR_REF (dr);
   tree vectype = STMT_VINFO_VECTYPE (stmt_info);
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "vect_compute_data_ref_alignment:\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "vect_compute_data_ref_alignment:";
 
   if (loop_vinfo)
     loop = LOOP_VINFO_LOOP (loop_vinfo);
@@ -902,13 +883,13 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       step_preserves_misalignment_p
 	= (DR_STEP_ALIGNMENT (dr) % vector_alignment) == 0;
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
 	  if (step_preserves_misalignment_p)
-	    dump_printf_loc (MSG_NOTE, vect_location,
+	    OPTINFO_VECT_NOTE << optinfo_printf (
 			     "inner step divides the vector alignment.\n");
 	  else
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	    OPTINFO_VECT_FAILURE << (
 			     "inner step doesn't divide the vector"
 			     " alignment.\n");
 	}
@@ -924,8 +905,8 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       step_preserves_misalignment_p
 	= multiple_p (DR_STEP_ALIGNMENT (dr) * vf, vector_alignment);
 
-      if (!step_preserves_misalignment_p && dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (!step_preserves_misalignment_p && optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << (
 			 "step doesn't divide the vector alignment.\n");
     }
 
@@ -947,12 +928,11 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
 	 negative when computing the starting misalignment below.  */
       || TREE_CODE (drb->step) != INTEGER_CST)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	                   "Unknown alignment for access: ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, ref);
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	  OPTINFO_VECT_FAILURE
+	    << "Unknown alignment for access: "
+	    << slim (ref);
 	}
       return true;
     }
@@ -965,12 +945,11 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
 	  || !vect_can_force_dr_alignment_p (base,
 					     vector_alignment * BITS_PER_UNIT))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-	                       "can't force alignment of ref: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "can't force alignment of ref: "
+		<< slim (ref);
 	    }
 	  return true;
 	}
@@ -978,11 +957,11 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       /* Force the alignment of the decl.
 	 NOTE: This is the only change to the code we make during
 	 the analysis phase, before deciding to vectorize the loop.  */
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_NOTE, vect_location, "force alignment of ");
-          dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
-          dump_printf (MSG_NOTE, "\n");
+          OPTINFO_VECT_NOTE
+	    << "force alignment of "
+	    << slim (ref);
         }
 
       DR_VECT_AUX (dr)->base_decl = base;
@@ -1004,25 +983,19 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
   if (!known_misalignment (misalignment, vector_alignment,
 			   &const_misalignment))
     {
-      if (dump_enabled_p ())
-	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "Non-constant misalignment for access: ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, ref);
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
-	}
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "Non-constant misalignment for access: "
+	  << slim (ref);
       return true;
     }
 
   SET_DR_MISALIGNMENT (dr, const_misalignment);
 
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                       "misalign = %d bytes of ref ", DR_MISALIGNMENT (dr));
-      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, ref);
-      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
-    }
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_FAILURE
+      << optinfo_printf ("misalign = %d bytes of ref ", DR_MISALIGNMENT (dr))
+      << slim (ref);
 
   return true;
 }
@@ -1085,9 +1058,9 @@ vect_update_misalignment_for_peel (struct data_reference *dr,
       return;
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "Setting misalignment " \
-		     "to unknown (-1).\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "Setting misalignment to unknown (-1)";
   SET_DR_MISALIGNMENT (dr, DR_MISALIGNMENT_UNKNOWN);
 }
 
@@ -1103,26 +1076,20 @@ verify_data_ref_alignment (data_reference_p dr)
     = vect_supportable_dr_alignment (dr, false);
   if (!supportable_dr_alignment)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
+	  pending_optinfo info = OPTINFO_VECT_FAILURE;
 	  if (DR_IS_READ (dr))
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported unaligned load.");
+	    info << "not vectorized: unsupported unaligned load";
 	  else
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported unaligned "
-			     "store.");
-
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-			     DR_REF (dr));
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	    info << "not vectorized: unsupported unaligned store";
+	  info << slim (DR_REF (dr));
 	}
       return false;
     }
 
-  if (supportable_dr_alignment != dr_aligned && dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "Vectorizing an unaligned access.\n");
+  if (supportable_dr_alignment != dr_aligned && optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE << "Vectorizing an unaligned access";
 
   return true;
 }
@@ -1216,18 +1183,17 @@ vector_alignment_reachable_p (struct data_reference *dr)
     {
       HOST_WIDE_INT elmsize =
 		int_cst_value (TYPE_SIZE_UNIT (TREE_TYPE (vectype)));
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-	                   "data size =" HOST_WIDE_INT_PRINT_DEC, elmsize);
-	  dump_printf (MSG_NOTE,
-	               ". misalignment = %d.\n", DR_MISALIGNMENT (dr));
+	  OPTINFO_VECT_NOTE
+	    << optinfo_printf ("data size =" HOST_WIDE_INT_PRINT_DEC, elmsize)
+	    << optinfo_printf (". misalignment = %d.\n", DR_MISALIGNMENT (dr));
 	}
       if (DR_MISALIGNMENT (dr) % elmsize)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	                     "data size does not divide the misalignment.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("data size does not divide the misalignment");
 	  return false;
 	}
     }
@@ -1236,10 +1202,10 @@ vector_alignment_reachable_p (struct data_reference *dr)
     {
       tree type = TREE_TYPE (DR_REF (dr));
       bool is_packed = not_size_aligned (DR_REF (dr));
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	                 "Unknown misalignment, %snaturally aligned\n",
-			 is_packed ? "not " : "");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << optinfo_printf ("Unknown misalignment, %snaturally aligned\n",
+			     is_packed ? "not " : "");
       return targetm.vectorize.vector_alignment_reachable (type, is_packed);
     }
 
@@ -1271,10 +1237,10 @@ vect_get_data_access_cost (struct data_reference *dr,
   else
     vect_get_store_cost (dr, ncopies, inside_cost, body_cost_vec);
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "vect_get_data_access_cost: inside_cost = %d, "
-                     "outside_cost = %d.\n", *inside_cost, *outside_cost);
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("vect_get_data_access_cost: inside_cost = %d, "
+			 "outside_cost = %d.\n", *inside_cost, *outside_cost);
 }
 
 
@@ -1662,9 +1628,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
   unsigned int mis, same_align_drs_max = 0;
   hash_table<peel_info_hasher> peeling_htab (1);
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_enhance_data_refs_alignment ===\n");
+  VECT_OPTINFO_SCOPE ("vect_enhance_data_refs_alignment");
 
   /* Reset data so we can safely be called multiple times.  */
   LOOP_VINFO_MAY_MISALIGN_STMTS (loop_vinfo).truncate (0);
@@ -1828,8 +1792,8 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
         {
           if (!aligned_access_p (dr))
             {
-              if (dump_enabled_p ())
-                dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+              if (optinfo_enabled_p ())
+                OPTINFO_VECT_FAILURE << (
                                  "vector alignment may not be reachable\n");
               break;
             }
@@ -2022,8 +1986,8 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
 	  if (STMT_VINFO_GROUPED_ACCESS (stmt_info))
 	    npeel /= GROUP_SIZE (stmt_info);
 
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_NOTE, vect_location,
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_NOTE << optinfo_printf (
                              "Try peeling by %d\n", npeel);
         }
 
@@ -2057,8 +2021,8 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
               if (max_peel > max_allowed_peel)
                 {
                   do_peeling = false;
-                  if (dump_enabled_p ())
-                    dump_printf_loc (MSG_NOTE, vect_location,
+                  if (optinfo_enabled_p ())
+                    OPTINFO_VECT_NOTE << optinfo_printf (
                         "Disable peeling, max peels reached: %d\n", max_peel);
                 }
             }
@@ -2107,11 +2071,11 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
             LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo)
 	      = DR_MISALIGNMENT (dr0);
 	  SET_DR_MISALIGNMENT (dr0, 0);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_NOTE, vect_location,
+              OPTINFO_VECT_NOTE << optinfo_printf (
                                "Alignment of access forced using peeling.\n");
-              dump_printf_loc (MSG_NOTE, vect_location,
+              OPTINFO_VECT_NOTE << optinfo_printf (
                                "Peeling for alignment will be applied.\n");
             }
 
@@ -2232,13 +2196,13 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
           stmt_vec_info stmt_info = vinfo_for_stmt (stmt);
           dr = STMT_VINFO_DATA_REF (stmt_info);
 	  SET_DR_MISALIGNMENT (dr, 0);
-	  if (dump_enabled_p ())
-            dump_printf_loc (MSG_NOTE, vect_location,
+	  if (optinfo_enabled_p ())
+            OPTINFO_VECT_NOTE << optinfo_printf (
                              "Alignment of access forced using versioning.\n");
         }
 
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE << optinfo_printf (
                          "Versioning for alignment will be applied.\n");
 
       /* Peeling and versioning can't be done together at this time.  */
@@ -2300,14 +2264,13 @@ vect_find_same_alignment_drs (struct data_dependence_relation *ddr)
 
   STMT_VINFO_SAME_ALIGN_REFS (stmtinfo_a).safe_push (drb);
   STMT_VINFO_SAME_ALIGN_REFS (stmtinfo_b).safe_push (dra);
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "accesses have the same alignment: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dra));
-      dump_printf (MSG_NOTE,  " and ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (drb));
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "accesses have the same alignment: "
+	<< slim (DR_REF (dra))
+	<< " and "
+	<< slim (DR_REF (drb));
     }
 }
 
@@ -2320,9 +2283,7 @@ vect_find_same_alignment_drs (struct data_dependence_relation *ddr)
 bool
 vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_refs_alignment ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_data_refs_alignment");
 
   /* Mark groups of data references with same alignment using
      data dependence information.  */
@@ -2349,10 +2310,10 @@ vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 	      && !STMT_VINFO_GROUPED_ACCESS (stmt_info))
 	    continue;
 
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: can't calculate alignment "
-			     "for data ref.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("not vectorized: can't calculate alignment "
+		  "for data ref");
 
 	  return false;
 	}
@@ -2383,10 +2344,10 @@ vect_slp_analyze_and_verify_node_alignment (slp_tree node)
 	  && ! vect_compute_data_ref_alignment (first_dr))
       || ! verify_data_ref_alignment (dr))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: bad data alignment in basic "
-			 "block.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("not vectorized: bad data alignment in basic "
+	      "block");
       return false;
     }
 
@@ -2401,9 +2362,7 @@ vect_slp_analyze_and_verify_node_alignment (slp_tree node)
 bool
 vect_slp_analyze_and_verify_instance_alignment (slp_instance instance)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_slp_analyze_and_verify_instance_alignment ===\n");
+  VECT_OPTINFO_SCOPE ("vect_slp_analyze_and_verify_instance_alignment");
 
   slp_tree node;
   unsigned i;
@@ -2455,15 +2414,13 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	 simply not include that gap.  */
       if ((dr_step % type_size) != 0)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-	                       "Step ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, step);
-	      dump_printf (MSG_NOTE,
-			   " is not a multiple of the element size for ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr));
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "Step "
+		<< slim (step)
+		<< " is not a multiple of the element size for "
+		<< slim (DR_REF (dr));
 	    }
 	  return false;
 	}
@@ -2487,24 +2444,23 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	  GROUP_FIRST_ELEMENT (vinfo_for_stmt (stmt)) = stmt;
 	  GROUP_SIZE (vinfo_for_stmt (stmt)) = groupsize;
 	  GROUP_GAP (stmt_info) = groupsize - 1;
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-	                       "Detected single element interleaving ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr));
-	      dump_printf (MSG_NOTE, " step ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, step);
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "Detected single element interleaving "
+		<< slim (DR_REF (dr))
+		<< " step "
+		<< slim (step);
 	    }
 
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
- 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-	                   "not consecutive access ");
-	  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+ 	  OPTINFO_VECT_FAILURE
+	    << "not consecutive access "
+	    << stmt;
         }
 
       if (bb_vinfo)
@@ -2514,7 +2470,8 @@ vect_analyze_group_access_1 (struct data_reference *dr)
           return true;
         }
 
-      dump_printf_loc (MSG_NOTE, vect_location, "using strided accesses\n");
+      OPTINFO_VECT_NOTE
+	<< "using strided accesses";
       STMT_VINFO_STRIDED_P (stmt_info) = true;
       return true;
     }
@@ -2542,15 +2499,15 @@ vect_analyze_group_access_1 (struct data_reference *dr)
             {
               if (DR_IS_WRITE (data_ref))
                 {
-                  if (dump_enabled_p ())
-                    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                     "Two store stmts share the same dr.\n");
+                  if (optinfo_enabled_p ())
+                    OPTINFO_VECT_FAILURE
+		      << "Two store stmts share the same dr";
                   return false;
                 }
 
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "Two or more load stmts share the same dr.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "Two or more load stmts share the same dr";
 
               /* For load use the same data-ref load.  */
               GROUP_SAME_DR_STMT (vinfo_for_stmt (next)) = prev;
@@ -2576,9 +2533,9 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	      slp_impossible = true;
 	      if (DR_IS_WRITE (data_ref))
 		{
-                  if (dump_enabled_p ())
-                    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                     "interleaved store with gaps\n");
+                  if (optinfo_enabled_p ())
+                    OPTINFO_VECT_FAILURE
+		      << "interleaved store with gaps";
 		  return false;
 		}
 
@@ -2604,8 +2561,8 @@ vect_analyze_group_access_1 (struct data_reference *dr)
          inefficient way we have to cap earlier.  See PR78699 for example.  */
       if (groupsize > 4096)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "group is too large\n");
 	  return false;
 	}
@@ -2615,8 +2572,8 @@ vect_analyze_group_access_1 (struct data_reference *dr)
       if (groupsize != count
 	  && !DR_IS_READ (dr))
         {
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "interleaved store with gaps\n");
 	  return false;
 	}
@@ -2628,9 +2585,9 @@ vect_analyze_group_access_1 (struct data_reference *dr)
       GROUP_GAP (vinfo_for_stmt (stmt)) = groupsize - last_accessed_element;
 
       GROUP_SIZE (vinfo_for_stmt (stmt)) = groupsize;
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  OPTINFO_VECT_NOTE << optinfo_printf (
 			   "Detected interleaving ");
 	  if (DR_IS_READ (dr))
 	    dump_printf (MSG_NOTE, "load ");
@@ -2640,7 +2597,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 		       (unsigned)groupsize);
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
 	  if (GROUP_GAP (vinfo_for_stmt (stmt)) != 0)
-	    dump_printf_loc (MSG_NOTE, vect_location,
+	    OPTINFO_VECT_NOTE << optinfo_printf (
 			     "There is a gap of %u elements after the group\n",
 			     GROUP_GAP (vinfo_for_stmt (stmt)));
 	}
@@ -2707,8 +2664,8 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 
   if (loop_vinfo && !step)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << (
 	                 "bad data-ref access in loop\n");
       return false;
     }
@@ -2724,8 +2681,8 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 	 loop-carried dependencies between inner loop iterations.  */
       if (loop->safelen < 2)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE << optinfo_printf (
 			     "zero step in inner loop of nest\n");
 	  return false;
 	}
@@ -2741,8 +2698,8 @@ vect_analyze_data_ref_access (struct data_reference *dr)
       step = STMT_VINFO_DR_STEP (stmt_info);
       if (integer_zerop (step))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE << optinfo_printf (
 	                     "zero step in outer loop.\n");
 	  return DR_IS_READ (dr);
 	}
@@ -2764,8 +2721,8 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 
   if (loop && nested_in_vect_loop_p (loop, stmt))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE << optinfo_printf (
 	                 "grouped access in outer loop.\n");
       return false;
     }
@@ -2906,9 +2863,7 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
   vec<data_reference_p> datarefs = vinfo->datarefs;
   struct data_reference *dr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_ref_accesses ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_data_ref_accesses");
 
   if (datarefs.is_empty ())
     return true;
@@ -3030,18 +2985,17 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
 		break;
 	    }
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "Detected interleaving ");
+	      pending_optinfo info = OPTINFO_VECT_NOTE
+		<< "Detected interleaving ";
 	      if (DR_IS_READ (dra))
-		dump_printf (MSG_NOTE, "load ");
+		info << "load ";
 	      else
-		dump_printf (MSG_NOTE, "store ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dra));
-	      dump_printf (MSG_NOTE,  " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (drb));
-	      dump_printf (MSG_NOTE, "\n");
+		info << "store ";
+	      info << slim (DR_REF (dra));
+	      info << " and ";
+	      info << slim (DR_REF (drb));
 	    }
 
 	  /* Link the found element into the group list.  */
@@ -3060,8 +3014,8 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
     if (STMT_VINFO_VECTORIZABLE (vinfo_for_stmt (DR_STMT (dr))) 
         && !vect_analyze_data_ref_access (dr))
       {
-	if (dump_enabled_p ())
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	if (optinfo_enabled_p ())
+	  OPTINFO_VECT_FAILURE << (
 	                   "not vectorized: complicated access pattern.\n");
 
         if (is_a <bb_vec_info> (vinfo))
@@ -3213,28 +3167,30 @@ dependence_distance_ge_vf (data_dependence_relation *ddr,
 	return false;
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "dependence distance between ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (DDR_A (ddr)));
-      dump_printf (MSG_NOTE,  " and ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (DDR_B (ddr)));
-      dump_printf (MSG_NOTE,  " is >= VF\n");
+      OPTINFO_VECT_NOTE
+	<< "dependence distance between "
+	<< slim (DR_REF (DDR_A (ddr)))
+	<< " and "
+	<< slim (DR_REF (DDR_B (ddr)))
+	<< " is >= VF";
     }
 
   return true;
 }
 
-/* Dump LOWER_BOUND using flags DUMP_KIND.  Dumps are known to be enabled.  */
+/* Add a description of LOWER_BOUND to INFO.  */
 
-static void
-dump_lower_bound (dump_flags_t dump_kind, const vec_lower_bound &lower_bound)
+static pending_optinfo &
+operator<< (pending_optinfo &info, const vec_lower_bound &lower_bound)
 {
-  dump_printf (dump_kind, "%s (", lower_bound.unsigned_p ? "unsigned" : "abs");
-  dump_generic_expr (dump_kind, TDF_SLIM, lower_bound.expr);
-  dump_printf (dump_kind, ") >= ");
-  dump_dec (dump_kind, lower_bound.min_value);
+  return info
+    << optinfo_printf ("%s (",
+		       lower_bound.unsigned_p ? "unsigned" : "abs")
+    << slim (lower_bound.expr)
+    << ") >= "
+    << lower_bound.min_value;
 }
 
 /* Record that the vectorized loop requires the vec_lower_bound described
@@ -3255,23 +3211,22 @@ vect_check_lower_bound (loop_vec_info loop_vinfo, tree expr, bool unsigned_p,
 	  {
 	    lower_bounds[i].unsigned_p = unsigned_p;
 	    lower_bounds[i].min_value = min_value;
-	    if (dump_enabled_p ())
+	    if (optinfo_enabled_p ())
 	      {
-		dump_printf_loc (MSG_NOTE, vect_location,
-				 "updating run-time check to ");
-		dump_lower_bound (MSG_NOTE, lower_bounds[i]);
-		dump_printf (MSG_NOTE, "\n");
+		OPTINFO_VECT_NOTE
+		  << "updating run-time check to "
+		  << lower_bounds[i];
 	      }
 	  }
 	return;
       }
 
   vec_lower_bound lower_bound (expr, unsigned_p, min_value);
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "need a run-time check that ");
-      dump_lower_bound (MSG_NOTE, lower_bound);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "need a run-time check that "
+	<< lower_bound;
     }
   LOOP_VINFO_LOWER_BOUNDS (loop_vinfo).safe_push (lower_bound);
 }
@@ -3353,9 +3308,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
   unsigned int i;
   tree length_factor;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_prune_runtime_alias_test_list ===\n");
+  VECT_OPTINFO_SCOPE ("vect_prune_runtime_alias_test_list");
 
   /* Step values are irrelevant for aliasing if the number of vector
      iterations is equal to the number of scalar iterations (which can
@@ -3401,13 +3354,14 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	  vec_object_pair new_pair (DDR_OBJECT_A (ddr), DDR_OBJECT_B (ddr));
 	  if (!compared_objects.add (new_pair))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location, "checking that ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, new_pair.first);
-		  dump_printf (MSG_NOTE, " and ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, new_pair.second);
-		  dump_printf (MSG_NOTE, " have different addresses\n");
+		  OPTINFO_VECT_NOTE
+		    << "checking that "
+		    << slim (new_pair.first)
+		    << " and "
+		    << slim (new_pair.second)
+		    << " have different addresses";
 		}
 	      LOOP_VINFO_CHECK_UNEQUAL_ADDRS (loop_vinfo).safe_push (new_pair);
 	    }
@@ -3426,14 +3380,14 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	  && (vect_preserves_scalar_order_p (stmt_a, stmt_b)
 	      || vectorizable_with_step_bound_p (dr_a, dr_b, &lower_bound)))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "no need for alias check between ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
-	      dump_printf (MSG_NOTE, " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b));
-	      dump_printf (MSG_NOTE, " when VF is 1\n");
+	      OPTINFO_VECT_NOTE
+		<< "no need for alias check between "
+		<< slim (DR_REF (dr_a))
+		<< " and "
+		<< slim (DR_REF (dr_b))
+		<< " when VF is 1";
 	    }
 	  continue;
 	}
@@ -3449,25 +3403,21 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	      || vect_small_gap_p (loop_vinfo, dr_b, lower_bound)))
 	{
 	  bool unsigned_p = dr_known_forward_stride_p (dr_a);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location, "no alias between ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
-	      dump_printf (MSG_NOTE, " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b));
-	      dump_printf (MSG_NOTE, " when the step ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_STEP (dr_a));
-	      dump_printf (MSG_NOTE, " is outside ");
+	      pending_optinfo info = OPTINFO_VECT_NOTE
+		<< optinfo_printf ( "no alias between ")
+		<< slim (DR_REF (dr_a))
+		<< " and "
+		<< slim (DR_REF (dr_b))
+		<< " when the step "
+		<< slim (DR_STEP (dr_a))
+		<< " is outside ";
 	      if (unsigned_p)
-		dump_printf (MSG_NOTE, "[0");
+		info << "[0";
 	      else
-		{
-		  dump_printf (MSG_NOTE, "(");
-		  dump_dec (MSG_NOTE, poly_int64 (-lower_bound));
-		}
-	      dump_printf (MSG_NOTE, ", ");
-	      dump_dec (MSG_NOTE, lower_bound);
-	      dump_printf (MSG_NOTE, ")\n");
+		info << "(" << poly_int64 (-lower_bound);
+	      info << ", " << lower_bound << ")";
 	    }
 	  vect_check_lower_bound (loop_vinfo, DR_STEP (dr_a), unsigned_p,
 				  lower_bound);
@@ -3525,17 +3475,17 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 					     segment_length_b,
 					     access_size_a,
 					     access_size_b);
-	  if (res >= 0 && dump_enabled_p ())
+	  if (res >= 0 && optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "can tell at compile time that ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
-	      dump_printf (MSG_NOTE, " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b));
+	      pending_optinfo info = OPTINFO_VECT_NOTE
+		<< "can tell at compile time that "
+		<< slim (DR_REF (dr_a))
+		<< " and "
+		<< slim (DR_REF (dr_b));
 	      if (res == 0)
-		dump_printf (MSG_NOTE, " do not alias\n");
+		info << " do not alias";
 	      else
-		dump_printf (MSG_NOTE, " alias\n");
+		info << " alias";
 	    }
 
 	  if (res == 0)
@@ -3543,9 +3493,9 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 
 	  if (res == 1)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_NOTE, vect_location,
-				 "not vectorized: compilation time alias.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_NOTE
+		  << "not vectorized: compilation time alias";
 	      return false;
 	    }
 	}
@@ -3566,17 +3516,19 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
   unsigned int count = (comp_alias_ddrs.length ()
 			+ check_unequal_addrs.length ());
 
-  dump_printf_loc (MSG_NOTE, vect_location,
+  OPTINFO_VECT_NOTE << optinfo_printf (
 		   "improved number of alias checks from %d to %d\n",
 		   may_alias_ddrs.length (), count);
   if ((int) count > PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "number of versioning for alias "
-			 "run-time tests exceeds %d "
-			 "(--param vect-max-version-for-alias-checks)\n",
-			 PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));
+      if (optinfo_enabled_p ())
+	// FIXME: would be nice to have special highlighting for
+	// command-line options (e.g. params)
+	OPTINFO_VECT_FAILURE
+	  << optinfo_printf ("number of versioning for alias "
+			     "run-time tests exceeds %d "
+			     "(--param vect-max-version-for-alias-checks)",
+			     PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS));
       return false;
     }
 
@@ -3951,9 +3903,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
   struct data_reference *dr;
   tree scalar_type;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_data_refs ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_data_refs");
 
   if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
     loop = LOOP_VINFO_LOOP (loop_vinfo);
@@ -3974,8 +3924,8 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 again:
       if (!dr || !DR_REF (dr))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 	                     "not vectorized: unhandled data-ref\n");
           return false;
         }
@@ -4094,12 +4044,12 @@ again:
 
 	  if (gatherscatter == SG_NONE && !simd_lane_access)
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "not vectorized: data ref analysis "
-                                   "failed ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << ("not vectorized: data ref analysis "
+			"failed ")
+		    << stmt;
 		}
 
 	      if (is_a <bb_vec_info> (vinfo))
@@ -4111,8 +4061,8 @@ again:
 
       if (TREE_CODE (DR_BASE_ADDRESS (dr)) == INTEGER_CST)
         {
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_FAILURE << (
                              "not vectorized: base addr of dr is a "
                              "constant\n");
 
@@ -4126,11 +4076,11 @@ again:
 
       if (TREE_THIS_VOLATILE (DR_REF (dr)))
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: volatile type ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< "not vectorized: volatile type "
+		<< stmt;
             }
 
           if (is_a <bb_vec_info> (vinfo))
@@ -4141,12 +4091,11 @@ again:
 
       if (stmt_can_throw_internal (stmt))
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: statement can throw an "
-                               "exception ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< "not vectorized: statement can throw an exception "
+		<< stmt;
             }
 
           if (is_a <bb_vec_info> (vinfo))
@@ -4160,12 +4109,12 @@ again:
       if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF
 	  && DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1)))
 	{
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: statement is bitfield "
-                               "access ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< ("not vectorized: statement is bitfield "
+		    "access ")
+		<< stmt;
             }
 
           if (is_a <bb_vec_info> (vinfo))
@@ -4185,11 +4134,11 @@ again:
 	      || (gimple_call_internal_fn (stmt) != IFN_MASK_LOAD
 		  && gimple_call_internal_fn (stmt) != IFN_MASK_STORE)))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION,  vect_location,
-	                       "not vectorized: dr in a call ");
-	      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< "not vectorized: dr in a call "
+		<< stmt;
 	    }
 
 	  if (is_a <bb_vec_info> (vinfo))
@@ -4219,12 +4168,11 @@ again:
 	  tree init_addr = fold_build_pointer_plus (base, init_offset);
 	  tree init_ref = build_fold_indirect_ref (init_addr);
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-                               "analyze in outer loop: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, init_ref);
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "analyze in outer loop: "
+		<< slim (init_ref);
 	    }
 
 	  if (!dr_analyze_innermost (&STMT_VINFO_DR_WRT_VEC_LOOP (stmt_info),
@@ -4232,41 +4180,36 @@ again:
 	    /* dr_analyze_innermost already explained the failure.  */
 	    return false;
 
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-                               "\touter base_address: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM,
-                                 STMT_VINFO_DR_BASE_ADDRESS (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter offset from base address: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM,
-                                 STMT_VINFO_DR_OFFSET (stmt_info));
-	      dump_printf (MSG_NOTE,
-                           "\n\touter constant offset from base address: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM,
-                                 STMT_VINFO_DR_INIT (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter step: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM,
-                                 STMT_VINFO_DR_STEP (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter base alignment: %d\n",
-			   STMT_VINFO_DR_BASE_ALIGNMENT (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter base misalignment: %d\n",
-			   STMT_VINFO_DR_BASE_MISALIGNMENT (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter offset alignment: %d\n",
-			   STMT_VINFO_DR_OFFSET_ALIGNMENT (stmt_info));
-	      dump_printf (MSG_NOTE, "\n\touter step alignment: %d\n",
-			   STMT_VINFO_DR_STEP_ALIGNMENT (stmt_info));
+	      OPTINFO_VECT_NOTE
+		<< "\touter base_address: "
+		<< slim (STMT_VINFO_DR_BASE_ADDRESS (stmt_info))
+		<< "\n\touter offset from base address: "
+		<< slim (STMT_VINFO_DR_OFFSET (stmt_info))
+		<< "\n\touter constant offset from base address: "
+		<< slim (STMT_VINFO_DR_INIT (stmt_info))
+		<< "\n\touter step: "
+		<< slim (STMT_VINFO_DR_STEP (stmt_info))
+		<< optinfo_printf ("\n\touter base alignment: %d\n",
+				   STMT_VINFO_DR_BASE_ALIGNMENT (stmt_info))
+		<< optinfo_printf ("\n\touter base misalignment: %d\n",
+				   STMT_VINFO_DR_BASE_MISALIGNMENT (stmt_info))
+		<< optinfo_printf ("\n\touter offset alignment: %d\n",
+				   STMT_VINFO_DR_OFFSET_ALIGNMENT (stmt_info))
+		<< optinfo_printf ("\n\touter step alignment: %d\n",
+				   STMT_VINFO_DR_STEP_ALIGNMENT (stmt_info));
 	    }
 	}
 
       if (STMT_VINFO_DATA_REF (stmt_info))
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: more than one data ref "
-                               "in stmt: ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< ("not vectorized: more than one data ref "
+		    "in stmt: ")
+		<< stmt;
             }
 
           if (is_a <bb_vec_info> (vinfo))
@@ -4289,12 +4232,12 @@ again:
 	  && VAR_P (TREE_OPERAND (DR_BASE_ADDRESS (dr), 0))
 	  && DECL_NONALIASED (TREE_OPERAND (DR_BASE_ADDRESS (dr), 0)))
 	{
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: base object not addressable "
-			       "for stmt: ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< ("not vectorized: base object not addressable "
+		    "for stmt: ")
+		<< stmt;
             }
           if (is_a <bb_vec_info> (vinfo))
 	    {
@@ -4312,15 +4255,13 @@ again:
 	= get_vectype_for_scalar_type (scalar_type);
       if (!STMT_VINFO_VECTYPE (stmt_info))
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                               "not vectorized: no vectype for stmt: ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
-              dump_printf (MSG_MISSED_OPTIMIZATION, " scalar_type: ");
-              dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_DETAILS,
-                                 scalar_type);
-              dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+              OPTINFO_VECT_FAILURE
+		<< "not vectorized: no vectype for stmt: "
+		<< stmt
+		<< " scalar_type: "
+		<< details (scalar_type);
             }
 
           if (is_a <bb_vec_info> (vinfo))
@@ -4341,14 +4282,12 @@ again:
         }
       else
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "got vectype for stmt: ");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM,
-				 STMT_VINFO_VECTYPE (stmt_info));
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "got vectype for stmt: "
+		<< stmt
+		<< slim (STMT_VINFO_VECTYPE (stmt_info));
 	    }
 	}
 
@@ -4366,15 +4305,15 @@ again:
 	    {
 	      STMT_VINFO_DATA_REF (stmt_info) = NULL;
 	      free_data_ref (dr);
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   (gatherscatter == GATHER) ?
-				   "not vectorized: not suitable for gather "
-				   "load " :
-				   "not vectorized: not suitable for scatter "
-				   "store ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << ((gatherscatter == GATHER) ?
+			"not vectorized: not suitable for gather "
+			"load " :
+			"not vectorized: not suitable for scatter "
+			"store ")
+		    << stmt;
 		}
 	      return false;
 	    }
@@ -4389,12 +4328,12 @@ again:
 	{
 	  if (nested_in_vect_loop_p (loop, stmt))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-                                   "not vectorized: not suitable for strided "
-                                   "load ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << ("not vectorized: not suitable for strided "
+			"load ")
+		    << stmt;
 		}
 	      return false;
 	    }
@@ -4621,11 +4560,11 @@ vect_create_addr_base_for_vector_ref (gimple *stmt,
 	mark_ptr_info_alignment_unknown (SSA_NAME_PTR_INFO (addr_base));
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "created ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, addr_base);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "created "
+	<< slim (addr_base);
     }
 
   return addr_base;
@@ -4748,23 +4687,22 @@ vect_create_data_ref_ptr (gimple *stmt, tree aggr_type, struct loop *at_loop,
      in LOOP.  */
   base_name = get_name (DR_BASE_ADDRESS (dr));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
-      dump_printf_loc (MSG_NOTE, vect_location,
-                       "create %s-pointer variable to type: ",
-		       get_tree_code_name (TREE_CODE (aggr_type)));
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
+      pending_optinfo info = OPTINFO_VECT_NOTE
+	<< optinfo_printf ("create %s-pointer variable to type: ",
+			   get_tree_code_name (TREE_CODE (aggr_type)))
+	<< slim (aggr_type);
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
-        dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
+	info << "  vectorizing an array ref: ";
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
-        dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
+	info << "  vectorizing a vector ref: ";
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
-        dump_printf (MSG_NOTE, "  vectorizing a record based array ref: ");
+	info << "  vectorizing a record based array ref: ";
       else
-        dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
-      dump_printf (MSG_NOTE, "\n");
+	info << "  vectorizing a pointer ref: ";
+      info << slim (DR_BASE_OBJECT (dr));
     }
 
   /* (1) Create the new aggregate-pointer variable.
@@ -5078,10 +5016,10 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
      be a power of two.  */
   if (count != 3 && exact_log2 (count) == -1)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "the size of the group of accesses"
-			 " is not a power of 2 or not eqaul to 3\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("the size of the group of accesses"
+	      " is not a power of 2 or not equal to 3");
       return false;
     }
 
@@ -5097,10 +5035,10 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	  unsigned int nelt;
 	  if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "cannot handle groups of 3 stores for"
-				 " variable-length vectors\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("cannot handle groups of 3 stores for"
+		      " variable-length vectors");
 	      return false;
 	    }
 
@@ -5124,9 +5062,9 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf (MSG_MISSED_OPTIMIZATION,
-				 "permutation op not supported by target.\n");
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE
+		      << "permutation op not supported by target";
 		  return false;
 		}
 
@@ -5142,9 +5080,9 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf (MSG_MISSED_OPTIMIZATION,
-				 "permutation op not supported by target.\n");
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE
+		      << "permutation op not supported by target";
 		  return false;
 		}
 	    }
@@ -5176,9 +5114,9 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	}
     }
 
-  if (dump_enabled_p ())
-    dump_printf (MSG_MISSED_OPTIMIZATION,
-		 "permutaion op not supported by target.\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_FAILURE
+      << "permutation op not supported by target";
   return false;
 }
 
@@ -5689,8 +5627,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
      see PR65518).  */
   if (single_element_p && maybe_gt (count, TYPE_VECTOR_SUBPARTS (vectype)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << (
 			 "single-element interleaving not supported "
 			 "for not adjacent vector loads\n");
       return false;
@@ -5700,8 +5638,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
      be a power of two.  */
   if (count != 3 && exact_log2 (count) == -1)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << (
 			 "the size of the group of accesses"
 			 " is not a power of 2 or not equal to 3\n");
       return false;
@@ -5716,8 +5654,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	  unsigned int nelt;
 	  if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << (
 				 "cannot handle groups of 3 loads for"
 				 " variable-length vectors\n");
 	      return false;
@@ -5737,8 +5675,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE << (
 				     "shuffle of 3 loads is not supported by"
 				     " target\n");
 		  return false;
@@ -5751,8 +5689,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE << (
 				     "shuffle of 3 loads is not supported by"
 				     " target\n");
 		  return false;
@@ -5783,8 +5721,8 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
         }
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_FAILURE << (
 		     "extract even/odd not supported by target\n");
   return false;
 }
@@ -6126,8 +6064,8 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       vec_perm_indices indices (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "shuffle of 2 fields structure is not \
 			      supported by target\n");
 	  return false;
@@ -6141,8 +6079,8 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "shuffle of 2 fields structure is not \
 			      supported by target\n");
 	  return false;
@@ -6156,8 +6094,8 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "shift permutation is not supported by target\n");
 	  return false;
 	}
@@ -6172,8 +6110,8 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << (
 			     "select is not supported by target\n");
 	  return false;
 	}
@@ -6236,10 +6174,9 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       vec_perm_indices indices (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "shuffle of 3 fields structure is not \
-			      supported by target\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "shuffle of 3 fields structure is not supported by target";
 	  return false;
 	}
       perm3_mask = vect_gen_perm_mask_checked (vectype, indices);
@@ -6251,9 +6188,9 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "shift permutation is not supported by target\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "shift permutation is not supported by target";
 	  return false;
 	}
       shift1_mask = vect_gen_perm_mask_checked (vectype, indices);
@@ -6265,9 +6202,9 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "shift permutation is not supported by target\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "shift permutation is not supported by target";
 	  return false;
 	}
       shift2_mask = vect_gen_perm_mask_checked (vectype, indices);
@@ -6279,9 +6216,9 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "shift permutation is not supported by target\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "shift permutation is not supported by target";
 	  return false;
 	}
       shift3_mask = vect_gen_perm_mask_checked (vectype, indices);
@@ -6293,9 +6230,9 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "shift permutation is not supported by target\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "shift permutation is not supported by target";
 	  return false;
 	}
       shift4_mask = vect_gen_perm_mask_checked (vectype, indices);
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index e82c1fe..fb4b71a 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -938,10 +938,11 @@ vect_set_loop_condition (struct loop *loop, loop_vec_info loop_vinfo,
   gsi_remove (&loop_cond_gsi, true);
   free_stmt_vec_info (orig_cond);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "New loop exit condition: ");
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, cond_stmt, 0);
+      OPTINFO_VECT_NOTE
+	<< "New loop exit condition: "
+	<< cond_stmt;
     }
 }
 
@@ -1370,17 +1371,16 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
   /* Analyze phi functions of the loop header.  */
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location, "vect_can_advance_ivs_p:\n");
   for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     {
       tree evolution_part;
 
       gphi *phi = gsi.phi ();
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-          dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
-          dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+	  OPTINFO_VECT_NOTE << "Analyze phi: " << phi;
 	}
 
       /* Skip virtual phi's. The data dependences that are associated with
@@ -1389,9 +1389,8 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 	 Skip reduction phis.  */
       if (!iv_phi_p (phi))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "reduc or virtual phi. skip.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE <<"reduc or virtual phi. skip";
 	  continue;
 	}
 
@@ -1401,9 +1400,8 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 	= STMT_VINFO_LOOP_PHI_EVOLUTION_PART (vinfo_for_stmt (phi));
       if (evolution_part == NULL_TREE)
         {
-	  if (dump_enabled_p ())
-	    dump_printf (MSG_MISSED_OPTIMIZATION,
-			 "No access function or evolution.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << "No access function or evolution";
 	  return false;
         }
 
@@ -1412,9 +1410,8 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
       if (!expr_invariant_in_loop_p (loop, evolution_part))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "evolution not invariant in loop.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << "evolution not invariant in loop";
 	  return false;
 	}
 
@@ -1423,9 +1420,8 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
       if (tree_is_chrec (evolution_part))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "evolution is chrec.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << "evolution is chrec";
 	  return false;
 	}
     }
@@ -1500,19 +1496,18 @@ vect_update_ivs_after_vectorizer (loop_vec_info loop_vinfo,
 
       gphi *phi = gsi.phi ();
       gphi *phi1 = gsi1.phi ();
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "vect_update_ivs_after_vectorizer: phi: ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+	  OPTINFO_VECT_NOTE
+	    << "vect_update_ivs_after_vectorizer: phi: "
+	    << phi;
 	}
 
       /* Skip reduction and virtual phis.  */
       if (!iv_phi_p (phi))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "reduc or virtual phi. skip.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE << "reduc or virtual phi. skip.";
 	  continue;
 	}
 
@@ -1640,9 +1635,9 @@ vect_gen_prolog_loop_niters (loop_vec_info loop_vinfo,
     {
       int npeel = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
 
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "known peeling = %d.\n", npeel);
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << optinfo_printf ("known peeling = %d", npeel);
 
       iters = build_int_cst (niters_type, npeel);
       *bound = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
@@ -1671,12 +1666,11 @@ vect_gen_prolog_loop_niters (loop_vec_info loop_vinfo,
       *bound = align_in_elems - 1;
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-                       "niters for prolog loop: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, iters);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "niters for prolog loop: "
+	<< slim (iters);
     }
 
   var = create_tmp_var (niters_type, "prolog_loop_niters");
@@ -1733,9 +1727,7 @@ vect_update_inits_of_drs (loop_vec_info loop_vinfo, tree niters,
   vec<data_reference_p> datarefs = LOOP_VINFO_DATAREFS (loop_vinfo);
   struct data_reference *dr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_update_inits_of_dr ===\n");
+  VECT_OPTINFO_SCOPE ("vect_update_inits_of_dr");
 
   /* Adjust niters to sizetype and insert stmts on loop preheader edge.  */
   if (!types_compatible_p (sizetype, TREE_TYPE (niters)))
@@ -1793,12 +1785,11 @@ vect_prepare_for_masked_peels (loop_vec_info loop_vinfo)
 	}
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "misalignment for fully-masked loop: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, misalign_in_elems);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "misalignment for fully-masked loop: "
+	<< slim (misalign_in_elems);
     }
 
   LOOP_VINFO_MASK_SKIP_NITERS (loop_vinfo) = misalign_in_elems;
@@ -2932,10 +2923,10 @@ vect_create_cond_for_alias_checks (loop_vec_info loop_vinfo, tree * cond_expr)
 
   create_runtime_alias_checks (LOOP_VINFO_LOOP (loop_vinfo),
 			       &comp_alias_ddrs, cond_expr);
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "created %u versioning for alias checks.\n",
-		     comp_alias_ddrs.length ());
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("created %u versioning for alias checks",
+			 comp_alias_ddrs.length ());
 }
 
 
@@ -3070,17 +3061,16 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       loop_constraint_set (loop, LOOP_C_INFINITE);
     }
 
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
-      && dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       if (version_alias)
-        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                         "loop versioned for vectorization because of "
-			 "possible aliasing\n");
+	OPTINFO_VECT_SUCCESS
+	  << ("loop versioned for vectorization because of "
+	      "possible aliasing");
       if (version_align)
-        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                         "loop versioned for vectorization to enhance "
-			 "alignment\n");
+        OPTINFO_VECT_SUCCESS
+	  << ("loop versioned for vectorization to enhance "
+	      "alignment");
 
     }
   free_original_copy_tables ();
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 4ce721ed..f5a3afc 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-vector-builder.h"
 #include "vec-perm-indices.h"
 #include "tree-eh.h"
+#include "optinfo.h"
 
 /* Loop Vectorization Pass.
 
@@ -155,6 +156,15 @@ along with GCC; see the file COPYING3.  If not see
 
 static void vect_estimate_min_profitable_iters (loop_vec_info, int *, int *);
 
+/* Generate a pending_optinfo at the current vect_optinfo_location.  */
+
+pending_optinfo
+emit_optinfo_at_vect_location (const optinfo_impl_location &impl_location,
+			       enum optinfo_kind kind)
+{
+  return pending_optinfo (impl_location, kind, vect_optinfo_location, false);
+}
+
 /* Function vect_determine_vectorization_factor
 
    Determine the vectorization factor (VF).  VF is the number of data elements
@@ -200,9 +210,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
   bool bool_result;
   auto_vec<stmt_vec_info> mask_producers;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_determine_vectorization_factor ===\n");
+  VECT_OPTINFO_SCOPE ("vect_determine_vectorization_factor");
 
   for (i = 0; i < nbbs; i++)
     {
@@ -213,11 +221,10 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	{
 	  phi = si.phi ();
 	  stmt_info = vinfo_for_stmt (phi);
-	  if (dump_enabled_p ())
-	    {
-	      dump_printf_loc (MSG_NOTE, vect_location, "==> examining phi: ");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
-	    }
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "==> examining phi: "
+	      << phi;
 
 	  gcc_assert (stmt_info);
 
@@ -227,42 +234,36 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      gcc_assert (!STMT_VINFO_VECTYPE (stmt_info));
               scalar_type = TREE_TYPE (PHI_RESULT (phi));
 
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location,
-                                   "get vectype for scalar type:  ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, scalar_type);
-                  dump_printf (MSG_NOTE, "\n");
+		  OPTINFO_VECT_NOTE
+		    << "get vectype for scalar type:  "
+		    << slim (scalar_type);
 		}
 
 	      vectype = get_vectype_for_scalar_type (scalar_type);
 	      if (!vectype)
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                       "not vectorized: unsupported "
-                                       "data-type ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                                         scalar_type);
-                      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		      OPTINFO_VECT_FAILURE
+			<< "not vectorized: unsupported data-type "
+			<< slim (scalar_type);
 		    }
 		  return false;
 		}
 	      STMT_VINFO_VECTYPE (stmt_info) = vectype;
 
-	      if (dump_enabled_p ())
-		{
-		  dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, vectype);
-                  dump_printf (MSG_NOTE, "\n");
-		}
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_NOTE
+		  << "vectype: "
+		  << slim (vectype);
 
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location, "nunits = ");
-		  dump_dec (MSG_NOTE, TYPE_VECTOR_SUBPARTS (vectype));
-		  dump_printf (MSG_NOTE, "\n");
+		  OPTINFO_VECT_NOTE
+		    << "nunits = "
+		    << TYPE_VECTOR_SUBPARTS (vectype);
 		}
 
 	      vect_update_max_nunits (&vectorization_factor, vectype);
@@ -281,11 +282,11 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 
           stmt_info = vinfo_for_stmt (stmt);
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-                               "==> examining statement: ");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	      OPTINFO_VECT_NOTE
+		<< "==> examining statement: "
+		<< stmt;
 	    }
 
 	  gcc_assert (stmt_info);
@@ -302,17 +303,18 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
                 {
                   stmt = pattern_stmt;
                   stmt_info = vinfo_for_stmt (pattern_stmt);
-                  if (dump_enabled_p ())
+                  if (optinfo_enabled_p ())
                     {
-                      dump_printf_loc (MSG_NOTE, vect_location,
-                                       "==> examining pattern statement: ");
-                      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+                      OPTINFO_VECT_NOTE
+			<< "==> examining pattern statement: "
+			<< stmt;
                     }
                 }
               else
 	        {
-	          if (dump_enabled_p ())
-	            dump_printf_loc (MSG_NOTE, vect_location, "skip.\n");
+	          if (optinfo_enabled_p ())
+	            OPTINFO_VECT_NOTE
+		      << "skip";
                   gsi_next (&si);
 	          continue;
                 }
@@ -351,12 +353,11 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 
 		  if (!gsi_end_p (pattern_def_si))
 		    {
-		      if (dump_enabled_p ())
+		      if (optinfo_enabled_p ())
 			{
-			  dump_printf_loc (MSG_NOTE, vect_location,
-                                           "==> examining pattern def stmt: ");
-			  dump_gimple_stmt (MSG_NOTE, TDF_SLIM,
-                                            pattern_def_stmt, 0);
+			  OPTINFO_VECT_NOTE
+			    << "==> examining pattern def stmt: "
+			    << pattern_def_stmt;
 			}
 
 		      stmt = pattern_def_stmt;
@@ -391,23 +392,20 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 		    }
 		  continue;
 		}
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-	          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "not vectorized: irregular stmt.");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION,  TDF_SLIM, stmt,
-                                    0);
+	          OPTINFO_VECT_FAILURE
+		    << "not vectorized: irregular stmt: " << stmt;
 		}
 	      return false;
 	    }
 
 	  if (VECTOR_MODE_P (TYPE_MODE (gimple_expr_type (stmt))))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 	        {
-	          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "not vectorized: vector stmt in loop:");
-	          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+	          OPTINFO_VECT_FAILURE
+		    << "not vectorized: vector stmt in loop: " << stmt;
 	        }
 	      return false;
 	    }
@@ -461,24 +459,20 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 		    }
 		}
 
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location,
-                                   "get vectype for scalar type:  ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, scalar_type);
-                  dump_printf (MSG_NOTE, "\n");
+		  OPTINFO_VECT_NOTE
+		    << "get vectype for scalar type:  "
+		    << slim (scalar_type);
 		}
 	      vectype = get_vectype_for_scalar_type (scalar_type);
 	      if (!vectype)
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                       "not vectorized: unsupported "
-                                       "data-type ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                                         scalar_type);
-                      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		      OPTINFO_VECT_FAILURE
+			<< "not vectorized: unsupported data-type: "
+			<< slim (scalar_type);
 		    }
 		  return false;
 		}
@@ -486,11 +480,11 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      if (!bool_result)
 		STMT_VINFO_VECTYPE (stmt_info) = vectype;
 
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, vectype);
-                  dump_printf (MSG_NOTE, "\n");
+		  OPTINFO_VECT_NOTE
+		    << "vectype: "
+		    << slim (vectype);
 		}
             }
 
@@ -506,24 +500,21 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      if (!bool_result)
 		scalar_type = vect_get_smallest_scalar_type (stmt, &dummy,
 							     &dummy);
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location,
-				   "get vectype for scalar type:  ");
-		  dump_generic_expr (MSG_NOTE, TDF_SLIM, scalar_type);
-		  dump_printf (MSG_NOTE, "\n");
+		  OPTINFO_VECT_NOTE
+		    << "get vectype for scalar type:  "
+		    << slim (scalar_type);
 		}
 	      vf_vectype = get_vectype_for_scalar_type (scalar_type);
 	    }
 	  if (!vf_vectype)
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "not vectorized: unsupported data-type ");
-		  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                                     scalar_type);
-                  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		  OPTINFO_VECT_FAILURE
+		    << "not vectorized: unsupported data-type "
+		    << slim (scalar_type);
 		}
 	      return false;
 	    }
@@ -531,33 +522,29 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	  if (maybe_ne (GET_MODE_SIZE (TYPE_MODE (vectype)),
 			GET_MODE_SIZE (TYPE_MODE (vf_vectype))))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                                   "not vectorized: different sized vector "
-                                   "types in statement, ");
-		  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                                     vectype);
-		  dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-		  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                                     vf_vectype);
-                  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		  OPTINFO_VECT_FAILURE
+		    << ("not vectorized: different sized vector "
+			"types in statement, ")
+		    << slim (vectype)
+		    << " and "
+		    << slim (vf_vectype);
 		}
 	      return false;
 	    }
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, vf_vectype);
-              dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "vectype: " << slim (vf_vectype);
 	    }
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location, "nunits = ");
-	      dump_dec (MSG_NOTE, TYPE_VECTOR_SUBPARTS (vf_vectype));
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "nunits = "
+		<< TYPE_VECTOR_SUBPARTS (vf_vectype);
 	    }
 
 	  vect_update_max_nunits (&vectorization_factor, vf_vectype);
@@ -571,18 +558,18 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
     }
 
   /* TODO: Analyze cost. Decide if worth while to vectorize.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "vectorization factor = ");
-      dump_dec (MSG_NOTE, vectorization_factor);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "vectorization factor = "
+	<< vectorization_factor;
     }
 
   if (known_le (vectorization_factor, 1U))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "not vectorized: unsupported data-type\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE
+	  << "not vectorized: unsupported data-type";
       return false;
     }
   LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;
@@ -603,9 +590,9 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 
 	  if (!mask_type)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: unsupported mask\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "not vectorized: unsupported mask";
 	      return false;
 	    }
 	}
@@ -621,13 +608,12 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      if (!vect_is_simple_use (rhs, mask_producers[i]->vinfo,
 				       &def_stmt, &dt, &vectype))
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				       "not vectorized: can't compute mask type "
-				       "for statement, ");
-		      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION,  TDF_SLIM, stmt,
-					0);
+		      OPTINFO_VECT_FAILURE
+			<< ("not vectorized: can't compute mask type "
+			    "for statement, ")
+			<< stmt;
 		    }
 		  return false;
 		}
@@ -643,34 +629,28 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      else if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_type),
 				 TYPE_VECTOR_SUBPARTS (vectype)))
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				       "not vectorized: different sized masks "
-				       "types in statement, ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					 mask_type);
-		      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					 vectype);
-		      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		      OPTINFO_VECT_FAILURE
+			<< ("not vectorized: different sized masks "
+			    "types in statement, ")
+			<< slim (mask_type)
+			<< " and "
+			<< slim (vectype);
 		    }
 		  return false;
 		}
 	      else if (VECTOR_BOOLEAN_TYPE_P (mask_type)
 		       != VECTOR_BOOLEAN_TYPE_P (vectype))
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				       "not vectorized: mixed mask and "
-				       "nonmask vector types in statement, ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					 mask_type);
-		      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
-		      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					 vectype);
-		      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		      OPTINFO_VECT_FAILURE
+			<< ("not vectorized: mixed mask and "
+			    "nonmask vector types in statement, ")
+			<< slim (mask_type)
+			<< " and "
+			<< slim (vectype);
 		    }
 		  return false;
 		}
@@ -690,13 +670,12 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	 if-conversion.  */
       if (!mask_type)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "not vectorized: can't compute mask type "
-			       "for statement, ");
-	      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION,  TDF_SLIM, stmt,
-				0);
+	      OPTINFO_VECT_FAILURE
+		<< ("not vectorized: can't compute mask type "
+		    "for statement, ")
+		<< stmt;
 	    }
 	  return false;
 	}
@@ -735,13 +714,11 @@ vect_is_simple_iv_evolution (unsigned loop_nb, tree access_fn, tree * init,
   step_expr = evolution_part;
   init_expr = unshare_expr (initial_condition_in_loop_num (access_fn, loop_nb));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "step: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, step_expr);
-      dump_printf (MSG_NOTE, ",  init: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, init_expr);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "step: " << slim (step_expr)
+	<< ",  init: " << slim (init_expr);
     }
 
   *init = init_expr;
@@ -757,9 +734,9 @@ vect_is_simple_iv_evolution (unsigned loop_nb, tree access_fn, tree * init,
       && (TREE_CODE (step_expr) != REAL_CST
 	  || !flag_associative_math))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "step unknown.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE
+	  << "step unknown";
       return false;
     }
 
@@ -782,9 +759,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
   gphi_iterator gsi;
   bool double_reduc;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_scalar_cycles ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_scalar_cycles");
 
   /* First - identify all inductions.  Reduction detection assumes that all the
      inductions have been identified, therefore, this order must not be
@@ -796,10 +771,10 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       tree def = PHI_RESULT (phi);
       stmt_vec_info stmt_vinfo = vinfo_for_stmt (phi);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+	  OPTINFO_VECT_NOTE
+	    << "Analyze phi: " << phi;
 	}
 
       /* Skip virtual phi's.  The data dependences that are associated with
@@ -814,12 +789,10 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       if (access_fn)
 	{
 	  STRIP_NOPS (access_fn);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-                               "Access function of PHI: ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, access_fn);
-              dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "Access function of PHI: " << slim (access_fn);
 	    }
 	  STMT_VINFO_LOOP_PHI_EVOLUTION_BASE_UNCHANGED (stmt_vinfo)
 	    = initial_condition_in_loop_num (access_fn, loop->num);
@@ -840,8 +813,9 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
 		  != NULL_TREE);
       gcc_assert (STMT_VINFO_LOOP_PHI_EVOLUTION_PART (stmt_vinfo) != NULL_TREE);
 
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location, "Detected induction.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "Detected induction";
       STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_induction_def;
     }
 
@@ -854,10 +828,10 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       stmt_vec_info stmt_vinfo = vinfo_for_stmt (phi);
       gimple *reduc_stmt;
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
-          dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+          OPTINFO_VECT_NOTE
+	    << "Analyze phi: " << phi;
         }
 
       gcc_assert (!virtual_operand_p (def)
@@ -869,9 +843,9 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
         {
           if (double_reduc)
             {
-              if (dump_enabled_p ())
-                dump_printf_loc (MSG_NOTE, vect_location,
-				 "Detected double reduction.\n");
+              if (optinfo_enabled_p ())
+                OPTINFO_VECT_NOTE
+		  << "Detected double reduction";
 
               STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_double_reduction_def;
               STMT_VINFO_DEF_TYPE (vinfo_for_stmt (reduc_stmt)) =
@@ -881,9 +855,9 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
             {
               if (loop != LOOP_VINFO_LOOP (loop_vinfo))
                 {
-                  if (dump_enabled_p ())
-                    dump_printf_loc (MSG_NOTE, vect_location,
-				     "Detected vectorizable nested cycle.\n");
+                  if (optinfo_enabled_p ())
+                    OPTINFO_VECT_NOTE
+		      << "Detected vectorizable nested cycle";
 
                   STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_nested_cycle;
                   STMT_VINFO_DEF_TYPE (vinfo_for_stmt (reduc_stmt)) =
@@ -891,9 +865,9 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
                 }
               else
                 {
-                  if (dump_enabled_p ())
-                    dump_printf_loc (MSG_NOTE, vect_location,
-				     "Detected reduction.\n");
+                  if (optinfo_enabled_p ())
+                    OPTINFO_VECT_NOTE
+		      << "Detected reduction";
 
                   STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_reduction_def;
                   STMT_VINFO_DEF_TYPE (vinfo_for_stmt (reduc_stmt)) =
@@ -907,9 +881,9 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
             }
         }
       else
-        if (dump_enabled_p ())
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "Unknown def-use cycle pattern.\n");
+        if (optinfo_enabled_p ())
+          OPTINFO_VECT_FAILURE
+	    << "Unknown def-use cycle pattern";
     }
 }
 
@@ -1029,9 +1003,8 @@ vect_get_loop_niters (struct loop *loop, tree *assumptions,
   *assumptions = boolean_true_node;
   *number_of_iterationsm1 = chrec_dont_know;
   *number_of_iterations = chrec_dont_know;
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== get_loop_niters ===\n");
+
+  VECT_OPTINFO_SCOPE ("get_loop_niters");
 
   if (!exit)
     return cond;
@@ -1470,9 +1443,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 			  tree *assumptions, tree *number_of_iterationsm1,
 			  tree *number_of_iterations, gcond **inner_loop_cond)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_loop_form ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_loop_form");
 
   /* Different restrictions apply when we are considering an inner-most loop,
      vs. an outer (nested) loop.
@@ -1494,17 +1465,17 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if (loop->num_nodes != 2)
         {
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: control flow in loop.\n");
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_FAILURE
+	      << "not vectorized: control flow in loop";
           return false;
         }
 
       if (empty_block_p (loop->header))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: empty loop.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: empty loop";
 	  return false;
 	}
     }
@@ -1532,17 +1503,17 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if ((loop->inner)->inner || (loop->inner)->next)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: multiple nested loops.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: multiple nested loops";
 	  return false;
 	}
 
       if (loop->num_nodes != 5)
         {
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: control flow in loop.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: control flow in loop";
 	  return false;
         }
 
@@ -1551,9 +1522,9 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 	  || !single_exit (innerloop)
 	  || single_exit (innerloop)->dest != EDGE_PRED (loop->latch, 0)->src)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: unsupported outerloop form.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: unsupported outerloop form";
 	  return false;
 	}
 
@@ -1566,37 +1537,36 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 	     loop.  */
 	  || !integer_onep (inner_assumptions))
 	{
-	  if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: Bad inner loop.\n");
+	  if (optinfo_enabled_p ())
+            OPTINFO_VECT_FAILURE
+	      << "not vectorized: bad inner loop";
 	  return false;
 	}
 
       if (!expr_invariant_in_loop_p (loop, inner_niter))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: inner-loop count not"
-                             " invariant.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: inner-loop count not invariant";
 	  return false;
 	}
 
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-			 "Considering outer-loop vectorization.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << "Considering outer-loop vectorization";
     }
 
   if (!single_exit (loop)
       || EDGE_COUNT (loop->header->preds) != 2)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
           if (!single_exit (loop))
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: multiple exits.\n");
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: multiple exits";
           else if (EDGE_COUNT (loop->header->preds) != 2)
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: too many incoming edges.\n");
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: too many incoming edges";
         }
       return false;
     }
@@ -1608,9 +1578,9 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   if (!empty_block_p (loop->latch)
       || !gimple_seq_empty_p (phi_nodes (loop->latch)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: latch block not empty.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: latch block not empty";
       return false;
     }
 
@@ -1618,9 +1588,9 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   edge e = single_exit (loop);
   if (e->flags & EDGE_ABNORMAL)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: abnormal loop exit edge.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: abnormal loop exit edge";
       return false;
     }
 
@@ -1628,9 +1598,9 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 				     number_of_iterationsm1);
   if (!*loop_cond)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: complicated exit condition.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: complicated exit condition";
       return false;
     }
 
@@ -1638,18 +1608,17 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
       || !*number_of_iterations
       || chrec_contains_undetermined (*number_of_iterations))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: number of iterations cannot be "
-			 "computed.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: number of iterations cannot be computed";
       return false;
     }
 
   if (integer_zerop (*number_of_iterations))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: number of iterations = 0.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: number of iterations = 0";
       return false;
     }
 
@@ -1688,15 +1657,10 @@ vect_analyze_loop_form (struct loop *loop)
     }
 
   if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
-    {
-      if (dump_enabled_p ())
-        {
-          dump_printf_loc (MSG_NOTE, vect_location,
-			   "Symbolic number of iterations is ");
-	  dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
-          dump_printf (MSG_NOTE, "\n");
-        }
-    }
+    if (optinfo_enabled_p ())
+      OPTINFO_VECT_NOTE
+	<< "symbolic number of iterations is "
+	<< details (number_of_iterations);
 
   STMT_VINFO_TYPE (vinfo_for_stmt (loop_cond)) = loop_exit_ctrl_vec_info_type;
   if (inner_loop_cond)
@@ -1722,9 +1686,7 @@ vect_update_vf_for_slp (loop_vec_info loop_vinfo)
   poly_uint64 vectorization_factor;
   int i;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_update_vf_for_slp ===\n");
+  VECT_OPTINFO_SCOPE ("vect_update_vf_for_slp");
 
   vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   gcc_assert (known_ne (vectorization_factor, 0U));
@@ -1759,14 +1721,14 @@ vect_update_vf_for_slp (loop_vec_info loop_vinfo)
 
   if (only_slp_in_loop)
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "Loop contains only SLP stmts\n");
+      OPTINFO_VECT_NOTE
+	<< "Loop contains only SLP stmts";
       vectorization_factor = LOOP_VINFO_SLP_UNROLLING_FACTOR (loop_vinfo);
     }
   else
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "Loop contains SLP and non-SLP stmts\n");
+      OPTINFO_VECT_NOTE
+	<< "Loop contains SLP and non-SLP stmts";
       /* Both the vectorization factor and unroll factor have the form
 	 current_vector_size * X for some rational X, so they must have
 	 a common multiple.  */
@@ -1776,12 +1738,11 @@ vect_update_vf_for_slp (loop_vec_info loop_vinfo)
     }
 
   LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "Updating vectorization factor to ");
-      dump_dec (MSG_NOTE, vectorization_factor);
-      dump_printf (MSG_NOTE, ".\n");
+      OPTINFO_VECT_NOTE
+	<< "Updating vectorization factor to "
+	<< vectorization_factor;
     }
 }
 
@@ -1827,9 +1788,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
   bool need_to_vectorize = false;
   bool ok;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_loop_operations ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_loop_operations");
 
   for (i = 0; i < nbbs; i++)
     {
@@ -1842,10 +1801,11 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
           ok = true;
 
           stmt_info = vinfo_for_stmt (phi);
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_NOTE, vect_location, "examining phi: ");
-              dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+              OPTINFO_VECT_NOTE
+		<< "examining phi: "
+		<< phi;
             }
 	  if (virtual_operand_p (gimple_phi_result (phi)))
 	    continue;
@@ -1861,10 +1821,9 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
               if (STMT_VINFO_LIVE_P (stmt_info)
 		  && !vect_active_double_reduction_p (stmt_info))
                 {
-                  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				     "Unsupported loop-closed phi in "
-				     "outer-loop.\n");
+                  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_FAILURE
+		      << "Unsupported loop-closed phi in outer-loop";
                   return false;
                 }
 
@@ -1905,9 +1864,9 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
               && STMT_VINFO_DEF_TYPE (stmt_info) != vect_induction_def)
             {
               /* A scalar-dependence cycle that we don't support.  */
-              if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "not vectorized: scalar dependence cycle.\n");
+              if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "not vectorized: scalar dependence cycle";
               return false;
             }
 
@@ -1931,12 +1890,11 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
 
           if (!ok)
             {
-              if (dump_enabled_p ())
+              if (optinfo_enabled_p ())
                 {
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "not vectorized: relevant phi not "
-				   "supported: ");
-                  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, phi, 0);
+		  OPTINFO_VECT_FAILURE
+		    << "not vectorized: relevant phi not supported: "
+		    << phi;
                 }
 	      return false;
             }
@@ -1959,13 +1917,12 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
      touching this loop.  */
   if (!need_to_vectorize)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-			 "All the computation can be taken out of the loop.\n");
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: redundant loop. no profit to "
-			 "vectorize.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << "All the computation can be taken out of the loop";
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: redundant loop. no profit to vectorize";
       return false;
     }
 
@@ -1996,10 +1953,10 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
       if (max_niter != -1
 	  && (unsigned HOST_WIDE_INT) max_niter < assumed_vf)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: iteration count smaller than "
-			     "vectorization factor.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("not vectorized: iteration count smaller than "
+		  "vectorization factor");
 	  return 0;
 	}
     }
@@ -2010,13 +1967,12 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
 
   if (min_profitable_iters < 0)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: vectorization not profitable.\n");
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: vector version will never be "
-			 "profitable.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: vectorization not profitable";
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: vector version will never be profitable";
       return -1;
     }
 
@@ -2033,14 +1989,14 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
   if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo)
       && LOOP_VINFO_INT_NITERS (loop_vinfo) < th)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: vectorization not profitable.\n");
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "not vectorized: iteration count smaller than user "
-			 "specified loop bound parameter or minimum profitable "
-			 "iterations (whichever is more conservative).\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: vectorization not profitable";
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << ("not vectorized: iteration count smaller than user "
+	      "specified loop bound parameter or minimum profitable "
+	      "iterations (whichever is more conservative)");
       return 0;
     }
 
@@ -2051,16 +2007,15 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
       && ((unsigned HOST_WIDE_INT) estimated_niter
 	  < MAX (th, (unsigned) min_profitable_estimate)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: estimated iteration count too "
-			 "small.\n");
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "not vectorized: estimated iteration count smaller "
-			 "than specified loop bound parameter or minimum "
-			 "profitable iterations (whichever is more "
-			 "conservative).\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not vectorized: estimated iteration count too small";
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << ("not vectorized: estimated iteration count smaller "
+	      "than specified loop bound parameter or minimum "
+	      "profitable iterations (whichever is more "
+	      "conservative)");
       return -1;
     }
 
@@ -2093,11 +2048,11 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   loop_p loop = LOOP_VINFO_LOOP (loop_vinfo);
   if (!find_loop_nest (loop, &LOOP_VINFO_LOOP_NEST (loop_vinfo)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not vectorized: loop nest containing two "
-			 "or more consecutive inner loops cannot be "
-			 "vectorized\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("not vectorized: loop nest containing two "
+	      "or more consecutive inner loops cannot be "
+	      "vectorized");
       return false;
     }
 
@@ -2142,11 +2097,11 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
 		      }
 		  }
 	      }
-	    if (dump_enabled_p ())
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "not vectorized: loop contains function "
-			       "calls or data references that cannot "
-			       "be analyzed\n");
+	    if (optinfo_enabled_p ())
+	      OPTINFO_VECT_FAILURE
+		<< ("not vectorized: loop contains function "
+		    "calls or data references that cannot "
+		    "be analyzed");
 	    return false;
 	  }
       }
@@ -2157,9 +2112,9 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_analyze_data_refs (loop_vinfo, &min_vf);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data references.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "bad data references";
       return false;
     }
 
@@ -2177,9 +2132,9 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_analyze_data_ref_accesses (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data access.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "bad data access";
       return false;
     }
 
@@ -2188,9 +2143,9 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_mark_stmts_to_be_vectorized (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "unexpected pattern.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "unexpected pattern";
       return false;
     }
 
@@ -2207,9 +2162,9 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
       || (max_vf != MAX_VECTORIZATION_FACTOR
 	  && maybe_lt (max_vf, min_vf)))
     {
-      if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "bad data dependence.\n");
+      if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "bad data dependence";
       return false;
     }
   LOOP_VINFO_MAX_VECT_FACTOR (loop_vinfo) = max_vf;
@@ -2217,17 +2172,17 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_determine_vectorization_factor (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "can't determine vectorization factor.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "can't determine vectorization factor";
       return false;
     }
   if (max_vf != MAX_VECTORIZATION_FACTOR
       && maybe_lt (max_vf, LOOP_VINFO_VECT_FACTOR (loop_vinfo)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data dependence.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "bad data dependence";
       return false;
     }
 
@@ -2266,13 +2221,13 @@ start_over:
   poly_uint64 vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   gcc_assert (known_ne (vectorization_factor, 0U));
 
-  if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) && dump_enabled_p ())
+  if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) && optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "vectorization_factor = ");
-      dump_dec (MSG_NOTE, vectorization_factor);
-      dump_printf (MSG_NOTE, ", niters = " HOST_WIDE_INT_PRINT_DEC "\n",
-		   LOOP_VINFO_INT_NITERS (loop_vinfo));
+      OPTINFO_VECT_NOTE
+	<< "vectorization_factor = "
+	<< vectorization_factor
+	<< optinfo_printf (", niters = " HOST_WIDE_INT_PRINT_DEC,
+			   LOOP_VINFO_INT_NITERS (loop_vinfo));
     }
 
   HOST_WIDE_INT max_niter
@@ -2284,9 +2239,9 @@ start_over:
   ok = vect_analyze_data_refs_alignment (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad data alignment.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "bad data alignment";
       return false;
     }
 
@@ -2306,9 +2261,9 @@ start_over:
     ok = vect_enhance_data_refs_alignment (loop_vinfo);
     if (!ok)
       {
-	if (dump_enabled_p ())
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "bad data alignment.\n");
+	if (optinfo_enabled_p ())
+	  OPTINFO_VECT_FAILURE
+	    << "bad data alignment";
         return false;
       }
     }
@@ -2329,9 +2284,9 @@ start_over:
   ok = vect_analyze_loop_operations (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "bad operation or unsupported loop bound.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "bad operation or unsupported loop bound";
       return false;
     }
 
@@ -2340,14 +2295,14 @@ start_over:
   LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
     = (LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo)
        && vect_verify_full_masking (loop_vinfo));
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       if (LOOP_VINFO_FULLY_MASKED_P (loop_vinfo))
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "using a fully-masked loop.\n");
+	OPTINFO_VECT_NOTE
+	  << "using a fully-masked loop";
       else
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "not using a fully-masked loop.\n");
+	OPTINFO_VECT_NOTE
+	  << "not using a fully-masked loop";
     }
 
   /* If epilog loop is required because of data accesses with gaps,
@@ -2362,10 +2317,10 @@ start_over:
 
       if (known_lt (wi::to_widest (scalar_niters), vf))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "loop has no enough iterations to support"
-			     " peeling for gaps.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << ("loop has not enough iterations to support"
+		  " peeling for gaps");
 	  return false;
 	}
     }
@@ -2376,9 +2331,9 @@ start_over:
     goto again;
   if (!res)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "Loop costings not worthwhile.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "Loop costings not worthwhile";
       return false;
     }
 
@@ -2414,17 +2369,17 @@ start_over:
   if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo)
       || LOOP_VINFO_PEELING_FOR_NITER (loop_vinfo))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location, "epilog loop required\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << "epilog loop required";
       if (!vect_can_advance_ivs_p (loop_vinfo)
 	  || !slpeel_can_duplicate_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
 					   single_exit (LOOP_VINFO_LOOP
 							 (loop_vinfo))))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not vectorized: can't create required "
-			     "epilog loop\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not vectorized: can't create required epilog loop";
           goto again;
         }
     }
@@ -2509,9 +2464,9 @@ again:
 	}
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "re-trying with SLP disabled\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "re-trying with SLP disabled";
 
   /* Roll back state appropriately.  No SLP this time.  */
   slp = false;
@@ -2587,17 +2542,15 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
   targetm.vectorize.autovectorize_vector_sizes (&vector_sizes);
   unsigned int next_size = 0;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "===== analyze_loop_nest =====\n");
+  VECT_OPTINFO_SCOPE ("analyze_loop_nest");
 
   if (loop_outer (loop)
       && loop_vec_info_for_loop (loop_outer (loop))
       && LOOP_VINFO_VECTORIZABLE_P (loop_vec_info_for_loop (loop_outer (loop))))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "outer-loop already vectorized.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "outer-loop already vectorized";
       return NULL;
     }
 
@@ -2608,9 +2561,9 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
       loop_vinfo = vect_analyze_loop_form (loop);
       if (!loop_vinfo)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "bad loop form.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "bad loop form";
 	  return NULL;
 	}
 
@@ -2642,13 +2595,11 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
 
       /* Try the next biggest vector size.  */
       current_vector_size = vector_sizes[next_size++];
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "***** Re-trying analysis with "
-			   "vector size ");
-	  dump_dec (MSG_NOTE, current_vector_size);
-	  dump_printf (MSG_NOTE, "\n");
+	  OPTINFO_VECT_NOTE
+	    << "***** Re-trying analysis with vector size "
+	    << current_vector_size;
 	}
     }
 }
@@ -2775,10 +2726,10 @@ neutral_op_for_slp_reduction (slp_tree slp_node, tree_code code,
    STMT is printed with a message MSG. */
 
 static void
-report_vect_op (dump_flags_t msg_type, gimple *stmt, const char *msg)
+report_vect_op (enum optinfo_kind kind, gimple *stmt, const char *msg)
 {
-  dump_printf_loc (msg_type, vect_location, "%s", msg);
-  dump_gimple_stmt (msg_type, TDF_SLIM, stmt, 0);
+  OPTINFO_VECT (kind)
+    << msg << stmt;
 }
 
 
@@ -2945,10 +2896,10 @@ vect_is_slp_reduction (loop_vec_info loop_info, gimple *phi,
                                   == vect_internal_def
                       && !is_loop_header_bb_p (gimple_bb (def_stmt)))))
   	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_NOTE, vect_location, "swapping oprnds: ");
-		  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, next_stmt, 0);
+		  OPTINFO_VECT_NOTE
+		    << "swapping oprnds: " << next_stmt;
 		}
 
 	      swap_ssa_operands (next_stmt,
@@ -3074,17 +3025,15 @@ pop:
 	}
     }
   while (1);
-  if (dump_file && (dump_flags & TDF_DETAILS))
+  // FIXME: TDF_DETAILS!
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, loc, "reduction path: ");
+      pending_optinfo info = OPTINFO_NOTE (loop) // FIXME: loop vs loc
+	<< "reduction path: ";
       unsigned i;
       std::pair<ssa_op_iter, use_operand_p> *x;
       FOR_EACH_VEC_ELT (path, i, x)
-	{
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, USE_FROM_PTR (x->second));
-	  dump_printf (MSG_NOTE, " ");
-	}
-      dump_printf (MSG_NOTE, "\n");
+	info << slim (USE_FROM_PTR (x->second)) << " ";
     }
 
   /* Check whether the reduction path detected is valid.  */
@@ -3201,9 +3150,9 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 
       if (!flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "intermediate value used outside loop.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "intermediate value used outside loop";
 
           return NULL;
         }
@@ -3211,9 +3160,9 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       nloop_uses++;
       if (nloop_uses > 1)
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "reduction value used in loop.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "reduction value used in loop";
           return NULL;
         }
 
@@ -3224,13 +3173,10 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
   tree loop_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_e);
   if (TREE_CODE (loop_arg) != SSA_NAME)
     {
-      if (dump_enabled_p ())
-	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "reduction: not ssa_name: ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, loop_arg);
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
-	}
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "reduction: not ssa_name: "
+	  << slim (loop_arg);
       return NULL;
     }
 
@@ -3247,11 +3193,11 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "reduction: unhandled reduction operation: ");
-	  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, def_stmt, 0);
+	  OPTINFO_VECT_FAILURE
+	    << "reduction: unhandled reduction operation: "
+	    << def_stmt;
 	}
       return NULL;
     }
@@ -3273,9 +3219,9 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 	lcphis.safe_push (as_a <gphi *> (use_stmt));
       if (nloop_uses > 1)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "reduction used in loop.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "reduction used in loop";
 	  return NULL;
 	}
     }
@@ -3289,9 +3235,9 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       if (gimple_phi_num_args (def_stmt) != 1
           || TREE_CODE (op1) != SSA_NAME)
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "unsupported phi node definition.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "unsupported phi node definition";
 
           return NULL;
         }
@@ -3304,8 +3250,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
           && is_gimple_assign (def1)
 	  && flow_bb_inside_loop_p (loop->inner, gimple_bb (phi_use_stmt)))
         {
-          if (dump_enabled_p ())
-            report_vect_op (MSG_NOTE, def_stmt,
+          if (optinfo_enabled_p ())
+            report_vect_op (OPTINFO_KIND_NOTE, def_stmt,
 			    "detected double reduction: ");
 
           *double_reduc = true;
@@ -3358,8 +3304,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
         }
       if (op3 == phi_name || op4 == phi_name)
 	{
-	  if (dump_enabled_p ())
-	    report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
+	  if (optinfo_enabled_p ())
+	    report_vect_op (OPTINFO_KIND_FAILURE, def_stmt,
 			    "reduction: condition depends on previous"
 			    " iteration: ");
 	  return NULL;
@@ -3370,8 +3316,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else if (!commutative_tree_code (code) || !associative_tree_code (code))
     {
-      if (dump_enabled_p ())
-	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
+      if (optinfo_enabled_p ())
+	report_vect_op (OPTINFO_KIND_FAILURE, def_stmt,
 			"reduction: not commutative/associative: ");
       return NULL;
     }
@@ -3382,16 +3328,16 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else
     {
-      if (dump_enabled_p ())
-	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
+      if (optinfo_enabled_p ())
+	report_vect_op (OPTINFO_KIND_FAILURE, def_stmt,
 			"reduction: not handled operation: ");
       return NULL;
     }
 
   if (TREE_CODE (op1) != SSA_NAME && TREE_CODE (op2) != SSA_NAME)
     {
-      if (dump_enabled_p ())
-	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
+      if (optinfo_enabled_p ())
+	report_vect_op (OPTINFO_KIND_FAILURE, def_stmt,
 			"reduction: both uses not ssa_names: ");
 
       return NULL;
@@ -3407,31 +3353,21 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       || (op4 && TREE_CODE (op4) == SSA_NAME
           && !types_compatible_p (type, TREE_TYPE (op4))))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_NOTE, vect_location,
-			   "reduction: multiple types: operation type: ");
-          dump_generic_expr (MSG_NOTE, TDF_SLIM, type);
-          dump_printf (MSG_NOTE, ", operands types: ");
-          dump_generic_expr (MSG_NOTE, TDF_SLIM,
-			     TREE_TYPE (op1));
-          dump_printf (MSG_NOTE, ",");
-          dump_generic_expr (MSG_NOTE, TDF_SLIM,
-			     TREE_TYPE (op2));
+	  pending_optinfo info
+	    = OPTINFO_VECT_NOTE
+	        << "reduction: multiple types: operation type: "
+		<< slim (type)
+		<< ", operands types: "
+		<< slim (TREE_TYPE (op1))
+		<< ","
+		<< slim (TREE_TYPE (op2));
           if (op3)
-            {
-              dump_printf (MSG_NOTE, ",");
-              dump_generic_expr (MSG_NOTE, TDF_SLIM,
-				 TREE_TYPE (op3));
-            }
+	    info << "," << slim (TREE_TYPE (op3));
 
           if (op4)
-            {
-              dump_printf (MSG_NOTE, ",");
-              dump_generic_expr (MSG_NOTE, TDF_SLIM,
-				 TREE_TYPE (op4));
-            }
-          dump_printf (MSG_NOTE, "\n");
+	    info << "," << slim (TREE_TYPE (op4));
         }
 
       return NULL;
@@ -3463,8 +3399,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
   if (code != COND_EXPR
       && ((!def1 || gimple_nop_p (def1)) && (!def2 || gimple_nop_p (def2))))
     {
-      if (dump_enabled_p ())
-	report_vect_op (MSG_NOTE, def_stmt, "reduction: no defs for operands: ");
+      if (optinfo_enabled_p ())
+	report_vect_op (OPTINFO_KIND_NOTE, def_stmt, "reduction: no defs for operands: ");
       return NULL;
     }
 
@@ -3486,8 +3422,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
                           == vect_internal_def
  	              && !is_loop_header_bb_p (gimple_bb (def1)))))))
     {
-      if (dump_enabled_p ())
-	report_vect_op (MSG_NOTE, def_stmt, "detected reduction: ");
+      if (optinfo_enabled_p ())
+	report_vect_op (OPTINFO_KIND_NOTE, def_stmt, "detected reduction: ");
       return def_stmt;
     }
 
@@ -3531,8 +3467,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 		}
 	      else
 		{
-		  if (dump_enabled_p ())
-		    report_vect_op (MSG_NOTE, def_stmt,
+		  if (optinfo_enabled_p ())
+		    report_vect_op (OPTINFO_KIND_NOTE, def_stmt,
 				    "detected reduction: cannot swap operands "
 				    "for cond_expr");
 		  return NULL;
@@ -3542,8 +3478,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 	    swap_ssa_operands (def_stmt, gimple_assign_rhs1_ptr (def_stmt),
 			       gimple_assign_rhs2_ptr (def_stmt));
 
-	  if (dump_enabled_p ())
-	    report_vect_op (MSG_NOTE, def_stmt,
+	  if (optinfo_enabled_p ())
+	    report_vect_op (OPTINFO_KIND_NOTE, def_stmt,
 			    "detected reduction: need to swap operands: ");
 
 	  if (CONSTANT_CLASS_P (gimple_assign_rhs1 (def_stmt)))
@@ -3551,8 +3487,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
         }
       else
         {
-          if (dump_enabled_p ())
-            report_vect_op (MSG_NOTE, def_stmt, "detected reduction: ");
+          if (optinfo_enabled_p ())
+            report_vect_op (OPTINFO_KIND_NOTE, def_stmt, "detected reduction: ");
         }
 
       return def_stmt;
@@ -3564,8 +3500,8 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       && orig_code != MINUS_EXPR
       && vect_is_slp_reduction (loop_info, phi, def_stmt))
     {
-      if (dump_enabled_p ())
-        report_vect_op (MSG_NOTE, def_stmt,
+      if (optinfo_enabled_p ())
+        report_vect_op (OPTINFO_KIND_NOTE, def_stmt,
 			"reduction: detected reduction chain: ");
 
       return def_stmt;
@@ -3586,9 +3522,9 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 			    code))
     return def_stmt;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
+      report_vect_op (OPTINFO_KIND_FAILURE, def_stmt,
 		      "reduction: unknown pattern: ");
     }
 
@@ -3634,10 +3570,10 @@ vect_get_known_peeling_cost (loop_vec_info loop_vinfo, int peel_iters_prologue,
   if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
     {
       *peel_iters_epilogue = assumed_vf / 2;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-			 "cost model: epilogue peel iters set to vf/2 "
-			 "because loop iterations are unknown .\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << ("cost model: epilogue peel iters set to vf/2 "
+	      "because loop iterations are unknown");
 
       /* If peeled iterations are known but number of scalar loop
          iterations are unknown, count a taken branch per peeled loop.  */
@@ -3720,7 +3656,8 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
   /* Cost model disabled.  */
   if (unlimited_cost_model (LOOP_VINFO_LOOP (loop_vinfo)))
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "cost model disabled.\n");
+      OPTINFO_VECT_NOTE
+	<< "cost model disabled";
       *ret_min_profitable_niters = 0;
       *ret_min_profitable_estimate = 0;
       return;
@@ -3980,25 +3917,28 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
 
   vec_outside_cost = (int)(vec_prologue_cost + vec_epilogue_cost);
   
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_NOTE, vect_location, "Cost model analysis: \n");
-      dump_printf (MSG_NOTE, "  Vector inside of loop cost: %d\n",
-                   vec_inside_cost);
-      dump_printf (MSG_NOTE, "  Vector prologue cost: %d\n",
-                   vec_prologue_cost);
-      dump_printf (MSG_NOTE, "  Vector epilogue cost: %d\n",
-                   vec_epilogue_cost);
-      dump_printf (MSG_NOTE, "  Scalar iteration cost: %d\n",
-                   scalar_single_iter_cost);
-      dump_printf (MSG_NOTE, "  Scalar outside cost: %d\n",
-                   scalar_outside_cost);
-      dump_printf (MSG_NOTE, "  Vector outside cost: %d\n",
-                   vec_outside_cost);
-      dump_printf (MSG_NOTE, "  prologue iterations: %d\n",
-                   peel_iters_prologue);
-      dump_printf (MSG_NOTE, "  epilogue iterations: %d\n",
-                   peel_iters_epilogue);
+  if (optinfo_enabled_p ())
+    {
+      /* FIXME: do we want to do something special for such a table of
+	 data?  */
+      OPTINFO_VECT_NOTE
+	<< "Cost model analysis:\n"
+	<< optinfo_printf ("  Vector inside of loop cost: %d\n",
+			   vec_inside_cost)
+	<< optinfo_printf ("  Vector prologue cost: %d\n",
+			   vec_prologue_cost)
+	<< optinfo_printf ("  Vector epilogue cost: %d\n",
+			   vec_epilogue_cost)
+	<< optinfo_printf ("  Scalar iteration cost: %d\n",
+			   scalar_single_iter_cost)
+	<< optinfo_printf ("  Scalar outside cost: %d\n",
+			   scalar_outside_cost)
+	<< optinfo_printf ("  Vector outside cost: %d\n",
+			   vec_outside_cost)
+	<< optinfo_printf ("  prologue iterations: %d\n",
+			   peel_iters_prologue)
+	<< optinfo_printf ("  epilogue iterations: %d",
+			   peel_iters_epilogue);
     }
 
   /* Calculate number of iterations required to make the vector version
@@ -4038,13 +3978,12 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
 	warning_at (vect_location, OPT_Wopenmp_simd, "vectorization "
 		    "did not happen for a simd loop");
 
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "cost model: the vector iteration cost = %d "
-			 "divided by the scalar iteration cost = %d "
-			 "is greater or equal to the vectorization factor = %d"
-                         ".\n",
-			 vec_inside_cost, scalar_single_iter_cost, assumed_vf);
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE
+	  << optinfo_printf ("cost model: the vector iteration cost = %d "
+			     "divided by the scalar iteration cost = %d "
+			     "is greater or equal to the vectorization factor = %d",
+			     vec_inside_cost, scalar_single_iter_cost, assumed_vf);
       *ret_min_profitable_niters = -1;
       *ret_min_profitable_estimate = -1;
       return;
@@ -4059,10 +3998,10 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
     /* We want the vectorized loop to execute at least once.  */
     min_profitable_iters = assumed_vf + peel_iters_prologue;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "  Runtime profitability threshold = %d\n",
-                     min_profitable_iters);
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("  Runtime profitability threshold = %d",
+			 min_profitable_iters);
 
   *ret_min_profitable_niters = min_profitable_iters;
 
@@ -4085,10 +4024,10 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
 				   - vec_inside_cost);
     }
   min_profitable_estimate = MAX (min_profitable_estimate, min_profitable_iters);
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "  Static estimate profitability threshold = %d\n",
-		     min_profitable_estimate);
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("  Static estimate profitability threshold = %d",
+			 min_profitable_estimate);
 
   *ret_min_profitable_estimate = min_profitable_estimate;
 }
@@ -4307,7 +4246,7 @@ vect_model_reduction_cost (stmt_vec_info stmt_info, internal_fn reduc_fn,
 	}
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf (MSG_NOTE, 
                  "vect_model_reduction_cost: inside_cost = %d, "
                  "prologue_cost = %d, epilogue_cost = %d .\n", inside_cost,
@@ -4337,10 +4276,11 @@ vect_model_induction_cost (stmt_vec_info stmt_info, int ncopies)
   prologue_cost = add_stmt_cost (target_cost_data, 2, scalar_to_vec,
 				 stmt_info, 0, vect_prologue);
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "vect_model_induction_cost: inside_cost = %d, "
-                     "prologue_cost = %d .\n", inside_cost, prologue_cost);
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("vect_model_induction_cost: inside_cost = %d, "
+			 "prologue_cost = %d",
+			 inside_cost, prologue_cost);
 }
 
 
@@ -4930,12 +4870,11 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
           add_phi_arg (as_a <gphi *> (phi), def, loop_latch_edge (loop),
 		       UNKNOWN_LOCATION);
 
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_NOTE, vect_location,
-			       "transform reduction: created def-use cycle: ");
-              dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
-              dump_gimple_stmt (MSG_NOTE, TDF_SLIM, SSA_NAME_DEF_STMT (def), 0);
+              OPTINFO_VECT_NOTE
+		<< "transform reduction: created def-use cycle: "
+		<< phi << SSA_NAME_DEF_STMT (def);
             }
         }
     }
@@ -5419,9 +5358,9 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
       /* Case 1:  Create:
          v_out2 = reduc_expr <v_out1>  */
 
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-			 "Reduce using direct vector reduction.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_NOTE
+	  << "Reduce using direct vector reduction";
 
       vec_elem_type = TREE_TYPE (TREE_TYPE (new_phi_result));
       if (!useless_type_conversion_p (scalar_type, vec_elem_type))
@@ -5690,9 +5629,9 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
 
           tree rhs;
 
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_NOTE, vect_location,
-			     "Reduce using vector shifts\n");
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_NOTE
+	      << "Reduce using vector shifts";
 
 	  mode1 = TYPE_MODE (vectype1);
           vec_dest = vect_create_destination_var (scalar_dest, vectype1);
@@ -5719,9 +5658,9 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
 	  /* 2.4  Extract the final scalar result.  Create:
 	     s_out3 = extract_field <v_out2, bitpos>  */
 
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "extract scalar result\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "extract scalar result";
 
 	  rhs = build3 (BIT_FIELD_REF, scalar_type, new_temp,
 			bitsize, bitsize_zero_node);
@@ -5743,9 +5682,9 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
                  Create:  s = op <s, s'>  // For non SLP cases
                }  */
 
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_NOTE, vect_location,
-			     "Reduce using scalar code.\n");
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_NOTE
+	      << "Reduce using scalar code";
 
 	  int vec_size_in_bits = tree_to_uhwi (TYPE_SIZE (vectype1));
 	  int element_bitsize = tree_to_uhwi (bitsize);
@@ -6066,11 +6005,11 @@ vect_finalize_reduction:
                                UNKNOWN_LOCATION);
                   add_phi_arg (vect_phi, PHI_RESULT (inner_phi),
                                loop_latch_edge (outer_loop), UNKNOWN_LOCATION);
-                  if (dump_enabled_p ())
+                  if (optinfo_enabled_p ())
                     {
-                      dump_printf_loc (MSG_NOTE, vect_location,
-				       "created double reduction phi node: ");
-                      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, vect_phi, 0);
+                      OPTINFO_VECT_NOTE
+			<< "created double reduction phi node: "
+			<< vect_phi;
                     }
 
                   vect_phi_res = PHI_RESULT (vect_phi);
@@ -6749,9 +6688,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
     {
       if (STMT_VINFO_REDUC_TYPE (stmt_info) == FOLD_LEFT_REDUCTION)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "in-order reduction chain without SLP.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "in-order reduction chain without SLP";
 	  return false;
 	}
 
@@ -6803,9 +6742,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	 as a reduction operation.  */
       if (reduc_index == -1)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "conditional reduction chains not supported\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "conditional reduction chains not supported";
 	  return false;
 	}
 
@@ -6828,10 +6767,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       else if (direct_internal_fn_supported_p (IFN_FOLD_EXTRACT_LAST,
 					       vectype_in, OPTIMIZE_FOR_SPEED))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "optimizing condition reduction with"
-			     " FOLD_EXTRACT_LAST.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "optimizing condition reduction with FOLD_EXTRACT_LAST";
 	  STMT_VINFO_VEC_REDUCTION_TYPE (stmt_info) = EXTRACT_LAST_REDUCTION;
 	}
       else if (cond_reduc_dt == vect_induction_def)
@@ -6869,10 +6807,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	    }
 	  if (cond_reduc_val)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_NOTE, vect_location,
-				 "condition expression based on "
-				 "integer induction.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_NOTE
+		  << "condition expression based on integer induction";
 	      STMT_VINFO_VEC_REDUCTION_TYPE (stmt_info)
 		= INTEGER_INDUC_COND_REDUCTION;
 	    }
@@ -6895,10 +6832,10 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 				    cond_initial_val, cond_reduc_val);
 	      if (e && (integer_onep (e) || integer_zerop (e)))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_NOTE, vect_location,
-				     "condition expression based on "
-				     "compile time constant.\n");
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_NOTE
+		      << ("condition expression based on "
+			  "compile time constant");
 		  /* Record reduction code at analysis stage.  */
 		  STMT_VINFO_VEC_CONST_COND_REDUC_CODE (stmt_info)
 		    = integer_onep (e) ? MAX_EXPR : MIN_EXPR;
@@ -6938,9 +6875,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       if (!vec_stmt && !vectorizable_condition (stmt, gsi, NULL,
 						ops[reduc_index], 0, NULL))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "unsupported condition in reduction\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "unsupported condition in reduction";
 	  return false;
         }
     }
@@ -6953,9 +6890,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  /* Shifts and rotates are only supported by vectorizable_shifts,
 	     not vectorizable_reduction.  */
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "unsupported shift or rotation.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "unsupported shift or rotation";
 	  return false;
 	}
 
@@ -6963,23 +6900,23 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       optab = optab_for_tree_code (code, vectype_in, optab_default);
       if (!optab)
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "no optab.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "no optab";
 
           return false;
         }
 
       if (optab_handler (optab, vec_mode) == CODE_FOR_nothing)
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             dump_printf (MSG_NOTE, "op not supported by target.\n");
 
 	  if (maybe_ne (GET_MODE_SIZE (vec_mode), UNITS_PER_WORD)
 	      || !vect_worthwhile_without_simd_p (loop_vinfo, code))
             return false;
 
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
   	    dump_printf (MSG_NOTE, "proceeding using word mode.\n");
         }
 
@@ -6987,9 +6924,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       if (!VECTOR_MODE_P (TYPE_MODE (vectype_in))
 	  && !vect_worthwhile_without_simd_p (loop_vinfo, code))
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "not worthwhile without SIMD support.\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "not worthwhile without SIMD support";
 
           return false;
         }
@@ -7093,9 +7030,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	      && !direct_internal_fn_supported_p (reduc_fn, vectype_out,
 						  OPTIMIZE_FOR_SPEED))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "reduc op not supported by target.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "reduc op not supported by target";
 
 	      reduc_fn = IFN_LAST;
 	    }
@@ -7104,9 +7041,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  if (!nested_cycle || double_reduc)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "no reduc code for scalar code.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << "no reduc code for scalar code";
 
 	      return false;
 	    }
@@ -7129,20 +7066,20 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       && reduc_fn == IFN_LAST
       && !nunits_out.is_constant ())
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "missing target support for reduction on"
-			 " variable-length vectors.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("missing target support for reduction on"
+	      " variable-length vectors");
       return false;
     }
 
   if ((double_reduc || reduction_type != TREE_CODE_REDUCTION)
       && ncopies > 1)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "multiple types in double reduction or condition "
-			 "reduction.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("multiple types in double reduction or condition "
+	      "reduction");
       return false;
     }
 
@@ -7169,9 +7106,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 		 l += a[j];
 
 	 which is a reassociation of the original operation.  */
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "in-order double reduction not supported.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "in-order double reduction not supported";
 
       return false;
     }
@@ -7182,9 +7119,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
     {
       /* We cannot use in-order reductions in this case because there is
 	 an implicit reassociation of the operations involved.  */
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "in-order unchained SLP reductions not supported.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "in-order unchained SLP reductions not supported";
       return false;
     }
 
@@ -7197,11 +7134,11 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       && !direct_internal_fn_supported_p (IFN_VEC_SHL_INSERT,
 					  vectype_out, OPTIMIZE_FOR_SPEED))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "reduction on variable-length vectors requires"
-			 " target support for a vector-shift-and-insert"
-			 " operation.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("reduction on variable-length vectors requires"
+	      " target support for a vector-shift-and-insert"
+	      " operation");
       return false;
     }
 
@@ -7220,11 +7157,11 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       if (!neutral_op
 	  && !can_duplicate_and_interleave_p (group_size, elt_mode))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "unsupported form of SLP reduction for"
-			     " variable-length vectors: cannot build"
-			     " initial vector.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("unsupported form of SLP reduction for"
+		  " variable-length vectors: cannot build"
+		  " initial vector");
 	  return false;
 	}
       /* The epilogue code relies on the number of elements being a multiple
@@ -7232,11 +7169,11 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	 up the the initial vector does too.  */
       if (!multiple_p (nunits_out, group_size))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "unsupported form of SLP reduction for"
-			     " variable-length vectors: the vector size"
-			     " is not a multiple of the number of results.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("unsupported form of SLP reduction for"
+		  " variable-length vectors: the vector size"
+		  " is not a multiple of the number of results");
 	  return false;
 	}
     }
@@ -7253,9 +7190,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
         ops[1] = fold_convert (TREE_TYPE (ops[0]), ops[1]);
       else
         {
-          if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "invalid types in dot-prod\n");
+          if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "invalid types in dot-prod";
 
           return false;
         }
@@ -7267,10 +7204,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 
       if (! max_loop_iterations (loop, &ni))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "loop count not known, cannot create cond "
-			     "reduction.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "loop count not known, cannot create cond reduction";
 	  return false;
 	}
       /* Convert backedges to iterations.  */
@@ -7282,9 +7218,9 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       tree max_index = TYPE_MAX_VALUE (cr_index_scalar_type);
       if (wi::geu_p (ni, wi::to_widest (max_index)))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "loop size is greater than data size.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "loop size is greater than data size";
 	  return false;
 	}
     }
@@ -7340,10 +7276,10 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	  || code == WIDEN_SUM_EXPR
 	  || code == SAD_EXPR))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "multi def-use cycle not possible for lane-reducing "
-			 "reduction operation\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("multi def-use cycle not possible for lane-reducing "
+	      "reduction operation");
       return false;
     }
 
@@ -7366,36 +7302,37 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 		  || !direct_internal_fn_supported_p (cond_fn, vectype_in,
 						      OPTIMIZE_FOR_SPEED)))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "can't use a fully-masked loop because no"
-				 " conditional operation is available.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("can't use a fully-masked loop because no"
+		      " conditional operation is available");
 	      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
 	    }
 	  else if (reduc_index == -1)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "can't use a fully-masked loop for chained"
-				 " reductions.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("can't use a fully-masked loop for chained"
+		      " reductions");
 	      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
 	    }
 	  else
 	    vect_record_loop_mask (loop_vinfo, masks, ncopies * vec_num,
 				   vectype_in);
 	}
-      if (dump_enabled_p ()
+      if (optinfo_enabled_p ()
 	  && reduction_type == FOLD_LEFT_REDUCTION)
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "using an in-order (fold-left) reduction.\n");
+	OPTINFO_VECT_NOTE
+	  << "using an in-order (fold-left) reduction";
       STMT_VINFO_TYPE (stmt_info) = reduc_vec_info_type;
       return true;
     }
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "transform reduction.\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "transform reduction";
 
   /* FORNOW: Multiple types are not supported for condition.  */
   if (code == COND_EXPR)
@@ -7689,9 +7626,9 @@ vectorizable_induction (gimple *phi,
 
       if (ncopies > 1)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "multiple types in nested loop.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << "multiple types in nested loop";
 	  return false;
 	}
 
@@ -7720,10 +7657,10 @@ vectorizable_induction (gimple *phi,
 	  if (!(STMT_VINFO_RELEVANT_P (exit_phi_vinfo)
 		&& !STMT_VINFO_LIVE_P (exit_phi_vinfo)))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "inner-loop induction only used outside "
-				 "of the outer vectorized loop.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("inner-loop induction only used outside "
+		      "of the outer vectorized loop");
 	      return false;
 	    }
 	}
@@ -7738,19 +7675,17 @@ vectorizable_induction (gimple *phi,
   if (slp_node && !nunits.is_constant ())
     {
       /* The current SLP code creates the initial value element-by-element.  */
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "SLP induction not supported for variable-length"
-			 " vectors.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << ("SLP induction not supported for variable-length"
+	      " vectors.\n");
       return false;
     }
 
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = induc_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_induction ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_induction");
       vect_model_induction_cost (stmt_info, ncopies);
       return true;
     }
@@ -7762,8 +7697,9 @@ vectorizable_induction (gimple *phi,
      evolution S, for a vector of 4 units, we want to compute:
      [X, X + S, X + 2*S, X + 3*S].  */
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "transform induction phi.\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "transform induction phi";
 
   latch_e = loop_latch_edge (iv_loop);
   loop_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_e);
@@ -8154,23 +8090,20 @@ vectorizable_induction (gimple *phi,
 		      && !STMT_VINFO_LIVE_P (stmt_vinfo));
 
 	  STMT_VINFO_VEC_STMT (stmt_vinfo) = new_stmt;
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "vector of inductions after inner-loop:");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, new_stmt, 0);
+	      OPTINFO_VECT_NOTE
+		<< "vector of inductions after inner-loop:" << new_stmt;
 	    }
 	}
     }
 
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "transform induction: created def-use cycle: ");
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, induction_phi, 0);
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM,
-			SSA_NAME_DEF_STMT (vec_def), 0);
+      OPTINFO_VECT_NOTE
+	<< "transform induction: created def-use cycle: "
+	<< induction_phi << SSA_NAME_DEF_STMT (vec_def);
     }
 
   return true;
@@ -8215,10 +8148,9 @@ vectorizable_live_operation (gimple *stmt,
   if (!STMT_VINFO_RELEVANT_P (stmt_info))
     {
       gcc_assert (is_simple_and_all_uses_invariant (stmt, loop_vinfo));
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "statement is simple and uses invariant.  Leaving in "
-			 "place.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "statement is simple and uses invariant.  Leaving in place";
       return true;
     }
 
@@ -8243,10 +8175,10 @@ vectorizable_live_operation (gimple *stmt,
 	 that vector we need.  */
       if (!can_div_trunc_p (pos, nunits, &vec_entry, &vec_index))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "Cannot determine which vector holds the"
-			     " final result.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("Cannot determine which vector holds the"
+		  " final result");
 	  return false;
 	}
     }
@@ -8259,27 +8191,27 @@ vectorizable_live_operation (gimple *stmt,
 	  if (!direct_internal_fn_supported_p (IFN_EXTRACT_LAST, vectype,
 					       OPTIMIZE_FOR_SPEED))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "can't use a fully-masked loop because "
-				 "the target doesn't support extract last "
-				 "reduction.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("can't use a fully-masked loop because "
+		      "the target doesn't support extract last "
+		      "reduction");
 	      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
 	    }
 	  else if (slp_node)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "can't use a fully-masked loop because an "
-				 "SLP statement is live after the loop.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("can't use a fully-masked loop because an "
+		      "SLP statement is live after the loop");
 	      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
 	    }
 	  else if (ncopies > 1)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "can't use a fully-masked loop because"
-				 " ncopies is greater than 1.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE
+		  << ("can't use a fully-masked loop because"
+		      " ncopies is greater than 1.\n");
 	      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
 	    }
 	  else
@@ -8424,9 +8356,9 @@ vect_loop_kill_debug_uses (struct loop *loop, gimple *stmt)
 	    {
 	      if (gimple_debug_bind_p (ustmt))
 		{
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_NOTE, vect_location,
-                                     "killing debug use\n");
+		  if (optinfo_enabled_p ())
+		    OPTINFO_VECT_NOTE
+		      << "killing debug use";
 
 		  gimple_debug_bind_reset_value (ustmt);
 		  update_stmt (ustmt);
@@ -8625,8 +8557,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   bool check_profitability = false;
   unsigned int th;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vec_transform_loop ===\n");
+  VECT_OPTINFO_SCOPE ("vec_transform_loop");
 
   /* Use the more conservative vectorization threshold.  If the number
      of iterations is constant assume the cost check has been performed
@@ -8637,10 +8568,10 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   if (th >= vect_vf_for_cost (loop_vinfo)
       && !LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "Profitability threshold is %d loop iterations.\n",
-                         th);
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << optinfo_printf ("Profitability threshold is %d loop iterations",
+			     th);
       check_profitability = true;
     }
 
@@ -8650,7 +8581,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   if (! single_pred_p (e->dest))
     {
       split_loop_exit_edge (e);
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf (MSG_NOTE, "split exit edge\n");
     }
 
@@ -8684,7 +8615,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
       if (! single_pred_p (e->dest))
 	{
 	  split_loop_exit_edge (e);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    dump_printf (MSG_NOTE, "split exit edge of scalar loop\n");
 	}
     }
@@ -8739,11 +8670,10 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	   gsi_next (&si))
         {
 	  gphi *phi = si.phi ();
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-                               "------>vectorizing phi: ");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
+	      OPTINFO_VECT_NOTE
+		<< "------>vectorizing phi: " << phi;
 	    }
 	  stmt_info = vinfo_for_stmt (phi);
 	  if (!stmt_info)
@@ -8759,16 +8689,18 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	  if (STMT_VINFO_VECTYPE (stmt_info)
 	      && (maybe_ne
 		  (TYPE_VECTOR_SUBPARTS (STMT_VINFO_VECTYPE (stmt_info)), vf))
-	      && dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
+	      && optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "multiple-types";
 
 	  if ((STMT_VINFO_DEF_TYPE (stmt_info) == vect_induction_def
 	       || STMT_VINFO_DEF_TYPE (stmt_info) == vect_reduction_def
 	       || STMT_VINFO_DEF_TYPE (stmt_info) == vect_nested_cycle)
 	      && ! PURE_SLP_STMT (stmt_info))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_NOTE, vect_location, "transform phi.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_NOTE
+		  << "transform phi";
 	      vect_transform_stmt (phi, NULL, NULL, NULL, NULL);
 	    }
 	}
@@ -8794,11 +8726,10 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 		}
 	    }
 
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "------>vectorizing statement: ");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	      OPTINFO_VECT_NOTE
+		<< "------>vectorizing statement: " << stmt;
 	    }
 
 	  stmt_info = vinfo_for_stmt (stmt);
@@ -8866,13 +8797,11 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 
 		  if (!gsi_end_p (pattern_def_si))
 		    {
-		      if (dump_enabled_p ())
+		      if (optinfo_enabled_p ())
 			{
-			  dump_printf_loc (MSG_NOTE, vect_location,
-					   "==> vectorizing pattern def "
-					   "stmt: ");
-			  dump_gimple_stmt (MSG_NOTE, TDF_SLIM,
-					    pattern_def_stmt, 0);
+			  OPTINFO_VECT_NOTE
+			    << "==> vectorizing pattern def stmt: "
+			    << pattern_def_stmt;
 			}
 
 		      stmt = pattern_def_stmt;
@@ -8894,10 +8823,11 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 		= TYPE_VECTOR_SUBPARTS (STMT_VINFO_VECTYPE (stmt_info));
 	      if (!STMT_SLP_TYPE (stmt_info)
 		  && maybe_ne (nunits, vf)
-		  && dump_enabled_p ())
+		  && optinfo_enabled_p ())
 		  /* For SLP VF is set according to unrolling factor, and not
 		     to vector size, hence for SLP this print is not valid.  */
-		dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
+		OPTINFO_VECT_NOTE
+		  << "multiple-types";
 	    }
 
 	  /* SLP. Schedule all the SLP instances when the first SLP stmt is
@@ -8908,9 +8838,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 		{
 		  slp_scheduled = true;
 
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_NOTE, vect_location,
-				     "=== scheduling SLP instances ===\n");
+		  VECT_OPTINFO_SCOPE ("scheduling SLP instances");
 
 		  vect_schedule_slp (loop_vinfo);
 		}
@@ -8928,8 +8856,9 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	    }
 
 	  /* -------- vectorize statement ------------ */
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location, "transform statement.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "transform statement";
 
 	  grouped_store = false;
 	  is_store = vect_transform_stmt (stmt, &si, &grouped_store, NULL, NULL);
@@ -9040,23 +8969,23 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	 : wi::udiv_floor (loop->nb_iterations_estimate + bias_for_assumed,
 			   assumed_vf) - 1);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
+      /* FIXME: I've converted these from "notes" to "successes".  */
       if (!LOOP_VINFO_EPILOGUE_P (loop_vinfo))
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "LOOP VECTORIZED\n");
+	  OPTINFO_VECT_SUCCESS
+	    << "LOOP VECTORIZED";
 	  if (loop->inner)
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "OUTER LOOP VECTORIZED\n");
-	  dump_printf (MSG_NOTE, "\n");
+	    OPTINFO_VECT_SUCCESS
+	      << "OUTER LOOP VECTORIZED";
 	}
       else
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "LOOP EPILOGUE VECTORIZED (VS=");
-	  dump_dec (MSG_NOTE, current_vector_size);
-	  dump_printf (MSG_NOTE, ")\n");
+	  OPTINFO_VECT_SUCCESS
+	    << "LOOP EPILOGUE VECTORIZED (VS="
+	    << current_vector_size
+	    << ")";
 	}
     }
 
@@ -9159,6 +9088,8 @@ optimize_mask_stores (struct loop *loop)
   auto_vec<gimple *> worklist;
 
   vect_location = find_loop_location (loop);
+  vect_optinfo_location = loop;
+
   /* Pick up all masked stores in loop if any.  */
   for (i = 0; i < nbbs; i++)
     {
@@ -9210,10 +9141,10 @@ optimize_mask_stores (struct loop *loop)
       make_single_succ_edge (store_bb, join_bb, EDGE_FALLTHRU);
       if (dom_info_available_p (CDI_DOMINATORS))
 	set_immediate_dominator (CDI_DOMINATORS, store_bb, bb);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "Create new block %d to sink mask stores.",
-			 store_bb->index);
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << optinfo_printf ("Create new block %d to sink mask stores.",
+			     store_bb->index);
       /* Create vector comparison with boolean result.  */
       vectype = TREE_TYPE (mask);
       zero = build_zero_cst (vectype);
@@ -9249,11 +9180,10 @@ optimize_mask_stores (struct loop *loop)
 	  gsi_move_before (&gsi_from, &gsi_to);
 	  /* Setup GSI_TO to the non-empty block start.  */
 	  gsi_to = gsi_start_bb (store_bb);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "Move stmt to created bb\n");
-	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, last, 0);
+	      OPTINFO_VECT_NOTE
+		<< "Move stmt to created bb " << last;
 	    }
 	  /* Move all stored value producers if possible.  */
 	  while (!gsi_end_p (gsi))
@@ -9317,12 +9247,10 @@ optimize_mask_stores (struct loop *loop)
 		break;
 
 	      /* Can move STMT1 to STORE_BB.  */
-	      if (dump_enabled_p ())
-		{
-		  dump_printf_loc (MSG_NOTE, vect_location,
-				   "Move stmt to created bb\n");
-		  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt1, 0);
-		}
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_NOTE
+		  << "move stmt to created bb"
+		  << stmt1;
 	      gsi_move_before (&gsi_from, &gsi_to);
 	      /* Shift GSI_TO for further insertion.  */
 	      gsi_prev (&gsi_to);
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c
index 621ed07..f38efac 100644
--- a/gcc/tree-vect-patterns.c
+++ b/gcc/tree-vect-patterns.c
@@ -448,7 +448,7 @@ vect_recog_dot_prod_pattern (vec<gimple *> *stmts, tree *type_in,
   pattern_stmt = gimple_build_assign (var, DOT_PROD_EXPR,
 				      oprnd00, oprnd01, oprnd1);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_dot_prod_pattern: detected: ");
@@ -682,7 +682,7 @@ vect_recog_sad_pattern (vec<gimple *> *stmts, tree *type_in,
   gimple *pattern_stmt = gimple_build_assign (var, SAD_EXPR, sad_oprnd0,
 					      sad_oprnd1, plus_oprnd1);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_sad_pattern: detected: ");
@@ -964,7 +964,7 @@ vect_recog_widen_mult_pattern (vec<gimple *> *stmts,
 	       TYPE_UNSIGNED (type));
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_widen_mult_pattern: detected:\n");
 
@@ -1015,7 +1015,7 @@ vect_recog_widen_mult_pattern (vec<gimple *> *stmts,
 					  gimple_assign_lhs (pattern_stmt));
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -1285,7 +1285,7 @@ vect_recog_widen_sum_pattern (vec<gimple *> *stmts, tree *type_in,
   var = vect_recog_temp_ssa_var (type, NULL);
   pattern_stmt = gimple_build_assign (var, WIDEN_SUM_EXPR, oprnd0, oprnd1);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_widen_sum_pattern: detected: ");
@@ -1584,7 +1584,7 @@ vect_recog_over_widening_pattern (vec<gimple *> *stmts,
       STMT_VINFO_RELATED_STMT (vinfo_for_stmt (stmt)) = pattern_stmt;
       new_pattern_def_seq (vinfo_for_stmt (stmt), new_def_stmt);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "created pattern stmt: ");
@@ -1651,7 +1651,7 @@ vect_recog_over_widening_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_over_widening_pattern: detected: ");
@@ -1788,7 +1788,7 @@ vect_recog_widen_shift_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_widen_shift_pattern: detected:\n");
 
@@ -1821,7 +1821,7 @@ vect_recog_widen_shift_pattern (vec<gimple *> *stmts,
       STMT_VINFO_VECTYPE (new_stmt_info) = vectype;
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2058,7 +2058,7 @@ vect_recog_rotate_pattern (vec<gimple *> *stmts, tree *type_in, tree *type_out)
   append_pattern_def_seq (stmt_vinfo, def_stmt);
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "vect_recog_rotate_pattern: detected:\n");
 
@@ -2066,7 +2066,7 @@ vect_recog_rotate_pattern (vec<gimple *> *stmts, tree *type_in, tree *type_out)
   var = vect_recog_temp_ssa_var (type, NULL);
   pattern_stmt = gimple_build_assign (var, BIT_IOR_EXPR, var1, var2);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2197,7 +2197,7 @@ vect_recog_vector_vector_shift_pattern (vec<gimple *> *stmts,
     }
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_vector_vector_shift_pattern: detected:\n");
 
@@ -2205,7 +2205,7 @@ vect_recog_vector_vector_shift_pattern (vec<gimple *> *stmts,
   var = vect_recog_temp_ssa_var (TREE_TYPE (oprnd0), NULL);
   pattern_stmt = gimple_build_assign (var, rhs_code, oprnd0, def);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2574,11 +2574,11 @@ vect_recog_mult_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "vect_recog_mult_pattern: detected:\n");
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM,
 			  pattern_stmt,0);
 
@@ -2692,7 +2692,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
 	return NULL;
 
       /* Pattern detected.  */
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_divmod_pattern: detected:\n");
 
@@ -2778,7 +2778,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
 				   signmask);
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt,
                               0);
 
@@ -3032,7 +3032,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
     }
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_divmod_pattern: detected: ");
@@ -3197,7 +3197,7 @@ vect_recog_mixed_size_cond_pattern (vec<gimple *> *stmts, tree *type_in,
   *type_in = vecitype;
   *type_out = vectype;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_mixed_size_cond_pattern: detected:\n");
 
@@ -3778,9 +3778,9 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vect_recog_bool_pattern: detected:\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vect_recog_bool_pattern: detected";
 
       return pattern_stmt;
     }
@@ -3819,9 +3819,9 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vect_recog_bool_pattern: detected:\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vect_recog_bool_pattern: detected";
 
       return pattern_stmt;
     }
@@ -3879,9 +3879,9 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vect_recog_bool_pattern: detected:\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vect_recog_bool_pattern: detected:";
       return pattern_stmt;
     }
   else
@@ -4017,9 +4017,9 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype1;
       *type_in = vectype1;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vect_recog_mask_conversion_pattern: detected:\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vect_recog_mask_conversion_pattern: detected";
 
       return pattern_stmt;
     }
@@ -4143,9 +4143,9 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype1;
       *type_in = vectype1;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vect_recog_mask_conversion_pattern: detected:\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vect_recog_mask_conversion_pattern: detected";
 
       return pattern_stmt;
     }
@@ -4191,9 +4191,9 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
   *type_out = vectype1;
   *type_in = vectype1;
   stmts->safe_push (last_stmt);
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "vect_recog_mask_conversion_pattern: detected:\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "vect_recog_mask_conversion_pattern: detected";
 
   return pattern_stmt;
 }
@@ -4377,9 +4377,9 @@ vect_try_gather_scatter_pattern (gimple *stmt, stmt_vec_info last_stmt_info,
   *type_out = vectype;
   *type_in = vectype;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "gather/scatter pattern detected:\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << "gather/scatter pattern detected";
 
   return pattern_stmt;
 }
@@ -4532,11 +4532,11 @@ vect_pattern_recog_1 (vect_recog_func *recog_func,
     }
 
   /* Found a vectorizable pattern.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-                       "%s pattern recognized: ", recog_func->name);
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, pattern_stmt, 0);
+      OPTINFO_VECT_NOTE
+	<< optinfo_printf ("%s pattern recognized: ", recog_func->name)
+	<< pattern_stmt;
     }
 
   /* Mark the stmts that are involved in the pattern. */
@@ -4558,11 +4558,11 @@ vect_pattern_recog_1 (vect_recog_func *recog_func,
     {
       stmt_info = vinfo_for_stmt (stmt);
       pattern_stmt = STMT_VINFO_RELATED_STMT (stmt_info);
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_NOTE, vect_location,
-                           "additional pattern stmt: ");
-          dump_gimple_stmt (MSG_NOTE, TDF_SLIM, pattern_stmt, 0);
+	  OPTINFO_VECT_NOTE
+	    << "additional pattern stmt: "
+	    << pattern_stmt;
         }
 
       vect_mark_pattern_stmts (stmt, pattern_stmt, NULL_TREE);
@@ -4660,9 +4660,7 @@ vect_pattern_recog (vec_info *vinfo)
   auto_vec<gimple *, 1> stmts_to_replace;
   gimple *stmt;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_pattern_recog ===\n");
+  VECT_OPTINFO_SCOPE ("vect_pattern_recog");
 
   if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
     {
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 73aa227..37fd3a3 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "vec-perm-indices.h"
 #include "gimple-fold.h"
 #include "internal-fn.h"
+#include "optinfo.h"
 
 
 /* Recursively free the memory allocated for the SLP tree rooted at NODE.  */
@@ -349,12 +350,11 @@ again:
 
       if (!vect_is_simple_use (oprnd, vinfo, &def_stmt, &dt))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "Build SLP failed: can't analyze def for ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, oprnd);
-              dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_FAILURE
+		<< "Build SLP failed: can't analyze def for "
+		<< slim (oprnd);
 	    }
 
 	  return -1;
@@ -385,13 +385,12 @@ again:
 		  goto again;
 		}
 
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "Build SLP failed: some of the stmts"
-				   " are in a pattern, and others are not ");
-		  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, oprnd);
-                  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		  OPTINFO_VECT_FAILURE
+		    << ("Build SLP failed: some of the stmts"
+			" are in a pattern, and others are not ")
+		    << slim (oprnd);
 		}
 
 	      return 1;
@@ -402,9 +401,9 @@ again:
 
           if (dt == vect_unknown_def_type)
             {
-              if (dump_enabled_p ())
-                dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "Unsupported pattern.\n");
+              if (optinfo_enabled_p ())
+                OPTINFO_VECT_FAILURE << optinfo_printf (
+				       "Unsupported pattern");
               return -1;
             }
 
@@ -415,8 +414,8 @@ again:
 	      break;
 
 	    default:
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "unsupported defining stmt:\n");
 	      return -1;
             }
@@ -457,8 +456,8 @@ again:
 		  goto again;
 		}
 
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "Build SLP failed: different types\n");
 
 	      return 1;
@@ -470,13 +469,12 @@ again:
 		  || !can_duplicate_and_interleave_p (stmts.length (),
 						      TYPE_MODE (type))))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "Build SLP failed: invalid type of def "
-				   "for variable-length SLP ");
-		  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, oprnd);
-		  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+		  OPTINFO_VECT_FAILURE
+		    << ("Build SLP failed: invalid type of def "
+			"for variable-length SLP ")
+		    << slim (oprnd);
 		}
 	      return -1;
 	    }
@@ -497,12 +495,11 @@ again:
 
 	default:
 	  /* FORNOW: Not supported.  */
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "Build SLP failed: illegal type of def ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, oprnd);
-              dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_FAILURE
+		<< "Build SLP failed: illegal type of def "
+		<< slim (oprnd);
 	    }
 
 	  return -1;
@@ -516,12 +513,12 @@ again:
          we've committed to the operand order and can't swap it.  */
       if (STMT_VINFO_NUM_SLP_USES (vinfo_for_stmt (stmt)) != 0)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "Build SLP failed: cannot swap operands of "
-			       "shared stmt ");
-	      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+	      OPTINFO_VECT_FAILURE 
+		<< ("Build SLP failed: cannot swap operands of "
+		    "shared stmt ")
+		<< stmt;
 	    }
 	  return -1;
 	}
@@ -552,11 +549,11 @@ again:
       else
 	swap_ssa_operands (stmt, gimple_assign_rhs1_ptr (stmt),
 			   gimple_assign_rhs2_ptr (stmt));
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "swapped operands to match def types in ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  OPTINFO_VECT_NOTE
+	    << "swapped operands to match def types in "
+	    << stmt;
 	}
     }
 
@@ -577,12 +574,11 @@ vect_record_max_nunits (vec_info *vinfo, gimple *stmt, unsigned int group_size,
 {
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "Build SLP failed: unsupported data-type in ");
-	  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
-	  dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	  OPTINFO_VECT_FAILURE
+	    << "Build SLP failed: unsupported data-type in "
+	    << stmt;
 	}
       /* Fatal mismatch.  */
       return false;
@@ -596,9 +592,9 @@ vect_record_max_nunits (vec_info *vinfo, gimple *stmt, unsigned int group_size,
       && (!nunits.is_constant (&const_nunits)
 	  || const_nunits > group_size))
     {
-      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-		       "Build SLP failed: unrolling required "
-		       "in basic block SLP\n");
+      OPTINFO_VECT_FAILURE
+	<< ("Build SLP failed: unrolling required "
+	    "in basic block SLP");
       /* Fatal mismatch.  */
       return false;
     }
@@ -650,20 +646,21 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       swap[i] = 0;
       matches[i] = false;
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location, "Build SLP for ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  OPTINFO_VECT_NOTE
+	    << "Build SLP for "
+	    << stmt;
 	}
 
       /* Fail to vectorize statements marked as unvectorizable.  */
       if (!STMT_VINFO_VECTORIZABLE (vinfo_for_stmt (stmt)))
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
-              dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "Build SLP failed: unvectorizable statement ");
-              dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+              OPTINFO_VECT_FAILURE
+		<< "Build SLP failed: unvectorizable statement "
+		<< stmt;
             }
 	  /* Fatal mismatch.  */
 	  matches[0] = false;
@@ -673,12 +670,12 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       lhs = gimple_get_lhs (stmt);
       if (lhs == NULL_TREE)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "Build SLP failed: not GIMPLE_ASSIGN nor "
-			       "GIMPLE_CALL ");
-	      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+	      OPTINFO_VECT_FAILURE
+		<< ("Build SLP failed: not GIMPLE_ASSIGN nor "
+		    "GIMPLE_CALL ")
+		<< stmt;
 	    }
 	  /* Fatal mismatch.  */
 	  matches[0] = false;
@@ -704,12 +701,11 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	      || !gimple_call_nothrow_p (call_stmt)
 	      || gimple_call_chain (call_stmt))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-				   "Build SLP failed: unsupported call type ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				    call_stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << "Build SLP failed: unsupported call type "
+		    << call_stmt;
 		}
 	      /* Fatal mismatch.  */
 	      matches[0] = false;
@@ -745,9 +741,9 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 
 		  if (!optab)
 		    {
-		      if (dump_enabled_p ())
-			dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-					 "Build SLP failed: no optab.\n");
+		      if (optinfo_enabled_p ())
+			OPTINFO_VECT_FAILURE
+			  << "Build SLP failed: no optab";
 		      /* Fatal mismatch.  */
 		      matches[0] = false;
 		      return false;
@@ -755,10 +751,10 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		  icode = (int) optab_handler (optab, vec_mode);
 		  if (icode == CODE_FOR_nothing)
 		    {
-		      if (dump_enabled_p ())
-			dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-					 "Build SLP failed: "
-					 "op not supported by target.\n");
+		      if (optinfo_enabled_p ())
+			OPTINFO_VECT_FAILURE
+			  << ("Build SLP failed: "
+			      "op not supported by target");
 		      /* Fatal mismatch.  */
 		      matches[0] = false;
 		      return false;
@@ -801,16 +797,14 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
                        || first_stmt_code == COMPONENT_REF
                        || first_stmt_code == MEM_REF)))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-				   "Build SLP failed: different operation "
-				   "in stmt ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "original stmt ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				    first_stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << ("Build SLP failed: different operation "
+			"in stmt ")
+		    << stmt
+		    << "original stmt "
+		    << first_stmt;
 		}
 	      /* Mismatch.  */
 	      continue;
@@ -819,12 +813,12 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	  if (need_same_oprnds
 	      && !operand_equal_p (first_op1, gimple_assign_rhs2 (stmt), 0))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-				   "Build SLP failed: different shift "
-				   "arguments in ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << ("Build SLP failed: different shift "
+			"arguments in ")
+		    << stmt;
 		}
 	      /* Mismatch.  */
 	      continue;
@@ -839,12 +833,11 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		  || gimple_call_fntype (first_stmt)
 		     != gimple_call_fntype (stmt))
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-				       "Build SLP failed: different calls in ");
-		      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					stmt, 0);
+		      OPTINFO_VECT_FAILURE
+			<< "Build SLP failed: different calls in "
+			<< stmt;
 		    }
 		  /* Mismatch.  */
 		  continue;
@@ -870,14 +863,12 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
                      chains in the same node.  */
                   if (prev_first_load != first_load)
                     {
-                      if (dump_enabled_p ())
+                      if (optinfo_enabled_p ())
                         {
-                          dump_printf_loc (MSG_MISSED_OPTIMIZATION,
-					   vect_location, 
-					   "Build SLP failed: different "
-					   "interleaving chains in one node ");
-                          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					    stmt, 0);
+			  OPTINFO_VECT_FAILURE
+			    <<  ("Build SLP failed: different "
+				 "interleaving chains in one node ")
+			    << stmt;
                         }
 		      /* Mismatch.  */
 		      continue;
@@ -892,11 +883,11 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	  if (TREE_CODE_CLASS (rhs_code) == tcc_reference)
 	    {
 	      /* Not grouped load.  */
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
-				   "Build SLP failed: not grouped load ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << "Build SLP failed: not grouped load "
+		    << stmt;
 		}
 
 	      /* FORNOW: Not grouped loads are not supported.  */
@@ -912,12 +903,11 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	      && TREE_CODE_CLASS (rhs_code) != tcc_comparison
 	      && rhs_code != CALL_EXPR)
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "Build SLP failed: operation");
-		  dump_printf (MSG_MISSED_OPTIMIZATION, " unsupported ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+		  OPTINFO_VECT_FAILURE
+		    << "Build SLP failed: operation unsupported "
+		    << stmt;
 		}
 	      /* Fatal mismatch.  */
 	      matches[0] = false;
@@ -950,13 +940,11 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		swap[i] = 2;
 	      else
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				       "Build SLP failed: different"
-				       " operation");
-		      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-					stmt, 0);
+		      OPTINFO_VECT_FAILURE
+			<< "Build SLP failed: different operation"
+			<< stmt;
 		    }
 		  /* Mismatch.  */
 		  continue;
@@ -980,10 +968,10 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       unsigned HOST_WIDE_INT count;
       if (!nunits.is_constant (&count))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "Build SLP failed: different operations "
-			     "not allowed with variable-length SLP.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE
+	      << ("Build SLP failed: different operations "
+		  "not allowed with variable-length SLP");
 	  return false;
 	}
       vec_perm_builder sel (count, count, 1);
@@ -1001,17 +989,14 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	    if (gimple_assign_rhs_code (stmts[i]) == alt_stmt_code)
 	      {
 		matches[i] = false;
-		if (dump_enabled_p ())
+		if (optinfo_enabled_p ())
 		  {
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				     "Build SLP failed: different operation "
-				     "in stmt ");
-		    dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				      stmts[i], 0);
-		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				     "original stmt ");
-		    dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				      first_stmt, 0);
+		    OPTINFO_VECT_FAILURE
+		      << ("Build SLP failed: different operation "
+			  "in stmt ")
+		      << stmts[i]
+		      << "original stmt "
+		      << first_stmt;
 		  }
 	      }
 	  return false;
@@ -1258,7 +1243,7 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 		    vect_free_slp_tree (grandchild);
 		  SLP_TREE_CHILDREN (child).truncate (0);
 
-		  dump_printf_loc (MSG_NOTE, vect_location,
+		  OPTINFO_VECT_NOTE << optinfo_printf (
 				   "Building parent vector operands from "
 				   "scalars instead\n");
 		  oprnd_info->def_stmts = vNULL;
@@ -1288,7 +1273,7 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 	     scalar version.  */
 	  && !is_pattern_stmt_p (vinfo_for_stmt (stmt)))
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  OPTINFO_VECT_NOTE << optinfo_printf (
 			   "Building vector operands from scalars\n");
 	  child = vect_create_new_slp_node (oprnd_info->def_stmts);
 	  SLP_TREE_DEF_TYPE (child) = vect_external_def;
@@ -1344,14 +1329,12 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 		    {
 		      if (!swap_not_matching)
 			{
-			  if (dump_enabled_p ())
+			  if (optinfo_enabled_p ())
 			    {
-			      dump_printf_loc (MSG_MISSED_OPTIMIZATION,
-					       vect_location,
-					       "Build SLP failed: cannot swap "
-					       "operands of shared stmt ");
-			      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION,
-						TDF_SLIM, stmts[j], 0);
+			      OPTINFO_VECT_FAILURE
+				<< ("Build SLP failed: cannot swap "
+				    "operands of shared stmt ")
+				<< stmts[j];
 			    }
 			  goto fail;
 			}
@@ -1363,7 +1346,7 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 	  while (j != group_size);
 
 	  /* Swap mismatched definition stmts.  */
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  OPTINFO_VECT_NOTE << optinfo_printf (
 			   "Re-trying with swapped operands of stmts ");
 	  for (j = 0; j < group_size; ++j)
 	    if (matches[j] == !swap_not_matching)
@@ -1438,7 +1421,7 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 			vect_free_slp_tree (grandchild);
 		      SLP_TREE_CHILDREN (child).truncate (0);
 
-		      dump_printf_loc (MSG_NOTE, vect_location,
+		      OPTINFO_VECT_NOTE << optinfo_printf (
 				       "Building parent vector operands from "
 				       "scalars instead\n");
 		      oprnd_info->def_stmts = vNULL;
@@ -1656,9 +1639,9 @@ vect_supported_load_permutation_p (slp_instance slp_instn)
   slp_tree node;
   gimple *stmt, *load, *next_load;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "Load permutation ");
+      OPTINFO_VECT_NOTE << optinfo_printf ( "Load permutation ");
       FOR_EACH_VEC_ELT (SLP_INSTANCE_LOADS (slp_instn), i, node)
 	if (node->load_permutation.exists ())
 	  FOR_EACH_VEC_ELT (node->load_permutation, j, next)
@@ -1733,9 +1716,9 @@ vect_supported_load_permutation_p (slp_instance slp_instn)
 	      if (!TYPE_VECTOR_SUBPARTS (vectype).is_constant (&nunits)
 		  || maxk >= (GROUP_SIZE (group_info) & ~(nunits - 1)))
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "BB vectorization with gaps at the end of "
-				   "a load is not supported\n");
+		  OPTINFO_VECT_FAILURE
+		    << ("BB vectorization with gaps at the end of "
+			"a load is not supported");
 		  return false;
 		}
 
@@ -1745,9 +1728,8 @@ vect_supported_load_permutation_p (slp_instance slp_instn)
 	      if (!vect_transform_slp_perm_load (node, tem, NULL,
 						 1, slp_instn, true, &n_perms))
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION,
-				   vect_location,
-				   "unsupported load permutation\n");
+		  OPTINFO_VECT_FAILURE
+		    << "unsupported load permutation";
 		  return false;
 		}
 	    }
@@ -2114,8 +2096,8 @@ vect_split_slp_store_group (gimple *first_stmt, unsigned group1_size)
   /* GROUP_GAP of the first group now has to skip over the second group too.  */
   GROUP_GAP (first_vinfo) += group2_size;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "Split group into %d and %d\n",
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE << optinfo_printf ( "Split group into %d and %d\n",
 		     group1_size, group2_size);
 
   return group2;
@@ -2172,12 +2154,11 @@ vect_analyze_slp_instance (vec_info *vinfo,
 
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "Build SLP failed: unsupported data-type ");
-          dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, scalar_type);
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+          OPTINFO_VECT_FAILURE
+	    << "Build SLP failed: unsupported data-type "
+	    << slim (scalar_type);
         }
 
       return false;
@@ -2238,8 +2219,8 @@ vect_analyze_slp_instance (vec_info *vinfo,
 	  if (!max_nunits.is_constant (&const_max_nunits)
 	      || const_max_nunits > group_size)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "Build SLP failed: store group "
 				 "size not a multiple of the vector size "
 				 "in basic block SLP\n");
@@ -2301,13 +2282,12 @@ vect_analyze_slp_instance (vec_info *vinfo,
         {
           if (!vect_supported_load_permutation_p (new_instance))
             {
-              if (dump_enabled_p ())
+              if (optinfo_enabled_p ())
                 {
-                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "Build SLP failed: unsupported load "
-				   "permutation ");
-		      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION,
-					TDF_SLIM, stmt, 0);
+                  OPTINFO_VECT_FAILURE
+		    << ("Build SLP failed: unsupported load "
+			"permutation ")
+		    << stmt;
                 }
               vect_free_slp_instance (new_instance);
               return false;
@@ -2336,8 +2316,8 @@ vect_analyze_slp_instance (vec_info *vinfo,
 	    }
 	  if (i == loads.length ())
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "Built SLP cancelled: can use "
 				 "load/store-lanes\n");
 	      vect_free_slp_instance (new_instance);
@@ -2347,9 +2327,9 @@ vect_analyze_slp_instance (vec_info *vinfo,
 
       vinfo->slp_instances.safe_push (new_instance);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  OPTINFO_VECT_NOTE << optinfo_printf (
 			   "Final SLP tree for instance:\n");
 	  vect_print_slp_tree (MSG_NOTE, vect_location, node);
 	}
@@ -2415,8 +2395,7 @@ vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)
   unsigned int i;
   gimple *first_element;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_analyze_slp ===\n");
+  VECT_OPTINFO_SCOPE ("vect_analyze_slp");
 
   /* Find SLP sequences starting from groups of grouped stores.  */
   FOR_EACH_VEC_ELT (vinfo->grouped_stores, i, first_element)
@@ -2469,9 +2448,7 @@ vect_make_slp_decision (loop_vec_info loop_vinfo)
   slp_instance instance;
   int decided_to_slp = 0;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_make_slp_decision ==="
-                     "\n");
+  VECT_OPTINFO_SCOPE ("vect_make_slp_decision");
 
   FOR_EACH_VEC_ELT (slp_instances, i, instance)
     {
@@ -2491,13 +2468,12 @@ vect_make_slp_decision (loop_vec_info loop_vinfo)
 
   LOOP_VINFO_SLP_UNROLLING_FACTOR (loop_vinfo) = unrolling_factor;
 
-  if (decided_to_slp && dump_enabled_p ())
+  if (decided_to_slp && optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "Decided to SLP %d instances. Unrolling factor ",
-		       decided_to_slp);
-      dump_dec (MSG_NOTE, unrolling_factor);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< optinfo_printf ("Decided to SLP %d instances. Unrolling factor ",
+			   decided_to_slp)
+	<< unrolling_factor;
     }
 
   return (decided_to_slp > 0);
@@ -2553,9 +2529,9 @@ vect_detect_hybrid_slp_stmts (slp_tree node, unsigned i, slp_vect_type stype)
 		&& !(gimple_code (use_stmt) == GIMPLE_PHI
 		     && STMT_VINFO_DEF_TYPE (use_vinfo) == vect_reduction_def))
 	      {
-		if (dump_enabled_p ())
+		if (optinfo_enabled_p ())
 		  {
-		    dump_printf_loc (MSG_NOTE, vect_location, "use of SLP "
+		    OPTINFO_VECT_NOTE << optinfo_printf ( "use of SLP "
 				     "def in non-SLP stmt: ");
 		    dump_gimple_stmt (MSG_NOTE, TDF_SLIM, use_stmt, 0);
 		  }
@@ -2567,9 +2543,9 @@ vect_detect_hybrid_slp_stmts (slp_tree node, unsigned i, slp_vect_type stype)
   if (stype == hybrid
       && !HYBRID_SLP_STMT (stmt_vinfo))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location, "marking hybrid: ");
+	  OPTINFO_VECT_NOTE << optinfo_printf ( "marking hybrid: ");
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
 	}
       STMT_SLP_TYPE (stmt_vinfo) = hybrid;
@@ -2598,9 +2574,9 @@ vect_detect_hybrid_slp_1 (tree *tp, int *, void *data)
       if (flow_bb_inside_loop_p (loopp, gimple_bb (def_stmt))
 	  && PURE_SLP_STMT (vinfo_for_stmt (def_stmt)))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location, "marking hybrid: ");
+	      OPTINFO_VECT_NOTE << optinfo_printf ( "marking hybrid: ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, def_stmt, 0);
 	    }
 	  STMT_SLP_TYPE (vinfo_for_stmt (def_stmt)) = hybrid;
@@ -2637,9 +2613,7 @@ vect_detect_hybrid_slp (loop_vec_info loop_vinfo)
   vec<slp_instance> slp_instances = LOOP_VINFO_SLP_INSTANCES (loop_vinfo);
   slp_instance instance;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_detect_hybrid_slp ==="
-                     "\n");
+  VECT_OPTINFO_SCOPE ("vect_detect_hybrid_slp");
 
   /* First walk all pattern stmt in the loop and mark defs of uses as
      hybrid because immediate uses in them are not recorded.  */
@@ -2762,33 +2736,30 @@ vect_slp_analyze_node_operations (vec_info *vinfo, slp_tree node,
       gcc_assert (PURE_SLP_STMT (stmt_info));
 
       tree scalar_type = TREE_TYPE (gimple_get_lhs (stmt));
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "get vectype for scalar type:  ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, scalar_type);
-	  dump_printf (MSG_NOTE, "\n");
+	  OPTINFO_VECT_NOTE
+	    << "get vectype for scalar type:  "
+	    << slim (scalar_type);
 	}
 
       tree vectype = get_vectype_for_scalar_type (scalar_type);
       if (!vectype)
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			       "not SLPed: unsupported data-type ");
-	      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				 scalar_type);
-	      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	      OPTINFO_VECT_FAILURE
+		<< "not SLPed: unsupported data-type "
+		<< slim (scalar_type);
 	    }
 	  return false;
 	}
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location, "vectype:  ");
-	  dump_generic_expr (MSG_NOTE, TDF_SLIM, vectype);
-	  dump_printf (MSG_NOTE, "\n");
+	  OPTINFO_VECT_NOTE
+	    << "vectype:  "
+	    << slim (vectype);
 	}
 
       gimple *sstmt;
@@ -2846,9 +2817,7 @@ vect_slp_analyze_operations (vec_info *vinfo)
   slp_instance instance;
   int i;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_slp_analyze_operations ===\n");
+  VECT_OPTINFO_SCOPE ("vect_slp_analyze_operations");
 
   for (i = 0; vinfo->slp_instances.iterate (i, &instance); )
     {
@@ -2856,11 +2825,9 @@ vect_slp_analyze_operations (vec_info *vinfo)
 					     SLP_INSTANCE_TREE (instance),
 					     instance))
         {
-	  dump_printf_loc (MSG_NOTE, vect_location,
-			   "removing SLP instance operations starting from: ");
-	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM,
-			    SLP_TREE_SCALAR_STMTS
-			      (SLP_INSTANCE_TREE (instance))[0], 0);
+	  OPTINFO_VECT_NOTE
+	    << "removing SLP instance operations starting from: "
+	    << SLP_TREE_SCALAR_STMTS (SLP_INSTANCE_TREE (instance))[0];
 	  vect_free_slp_instance (instance);
           vinfo->slp_instances.ordered_remove (i);
 	}
@@ -2868,15 +2835,15 @@ vect_slp_analyze_operations (vec_info *vinfo)
 	i++;
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_slp_cost ===\n");
+  {
+    VECT_OPTINFO_SCOPE ("vect_analyze_slp_cost");
 
-  /* Compute the costs of the SLP instances.  */
-  scalar_stmts_set_t *visited = new scalar_stmts_set_t ();
-  for (i = 0; vinfo->slp_instances.iterate (i, &instance); ++i)
-    vect_analyze_slp_cost (instance, vinfo->target_cost_data, visited);
-  delete visited;
+    /* Compute the costs of the SLP instances.  */
+    scalar_stmts_set_t *visited = new scalar_stmts_set_t ();
+    for (i = 0; vinfo->slp_instances.iterate (i, &instance); ++i)
+      vect_analyze_slp_cost (instance, vinfo->target_cost_data, visited);
+    delete visited;
+  }
 
   return !vinfo->slp_instances.is_empty ();
 }
@@ -2994,9 +2961,9 @@ vect_bb_vectorization_profitable_p (bb_vec_info bb_vinfo)
 
   vec_outside_cost = vec_prologue_cost + vec_epilogue_cost;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "Cost model analysis: \n");
+      OPTINFO_VECT_NOTE << optinfo_printf ( "Cost model analysis: \n");
       dump_printf (MSG_NOTE, "  Vector inside of basic block cost: %d\n",
 		   vec_inside_cost);
       dump_printf (MSG_NOTE, "  Vector prologue cost: %d\n", vec_prologue_cost);
@@ -3035,8 +3002,8 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (n_stmts > PARAM_VALUE (PARAM_SLP_MAX_INSNS_IN_BB))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: too many instructions in "
 			 "basic block.\n");
       free_data_refs (datarefs);
@@ -3053,10 +3020,10 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_analyze_data_refs (bb_vinfo, &min_vf))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: unhandled data-ref in basic "
-			 "block.\n");
+			 "block");
 
       delete bb_vinfo;
       return NULL;
@@ -3064,10 +3031,10 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (BB_VINFO_DATAREFS (bb_vinfo).length () < 2)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: not enough data-refs in "
-			 "basic block.\n");
+			 "basic block");
 
       delete bb_vinfo;
       return NULL;
@@ -3075,10 +3042,10 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_analyze_data_ref_accesses (bb_vinfo))
     {
-     if (dump_enabled_p ())
-       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+     if (optinfo_enabled_p ())
+       OPTINFO_VECT_FAILURE << optinfo_printf (
 			"not vectorized: unhandled data access in "
-			"basic block.\n");
+			"basic block");
 
       delete bb_vinfo;
       return NULL;
@@ -3089,8 +3056,8 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
      anyway.  */
   if (bb_vinfo->grouped_stores.is_empty ())
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: no grouped stores in "
 			 "basic block.\n");
 
@@ -3107,11 +3074,11 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
      trees.  */
   if (!vect_analyze_slp (bb_vinfo, n_stmts))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  OPTINFO_VECT_FAILURE << optinfo_printf (
 			   "Failed to SLP the basic block.\n");
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
+	  OPTINFO_VECT_FAILURE << optinfo_printf ( 
 			   "not vectorized: failed to find SLP opportunities "
 			   "in basic block.\n");
 	}
@@ -3129,7 +3096,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
       if (! vect_slp_analyze_and_verify_instance_alignment (instance)
 	  || ! vect_slp_analyze_instance_dependence (instance))
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  OPTINFO_VECT_NOTE << optinfo_printf (
 			   "removing SLP instance operations starting from: ");
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM,
 			    SLP_TREE_SCALAR_STMTS
@@ -3154,8 +3121,8 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_slp_analyze_operations (bb_vinfo))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: bad operation in basic block.\n");
 
       delete bb_vinfo;
@@ -3166,8 +3133,8 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
   if (!unlimited_cost_model (NULL)
       && !vect_bb_vectorization_profitable_p (bb_vinfo))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not vectorized: vectorization is not "
 			 "profitable.\n");
 
@@ -3175,8 +3142,8 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
       return NULL;
     }
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE << optinfo_printf (
 		     "Basic block will be vectorized using SLP\n");
 
   return bb_vinfo;
@@ -3194,8 +3161,7 @@ vect_slp_bb (basic_block bb)
   bool any_vectorized = false;
   auto_vector_sizes vector_sizes;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "===vect_slp_analyze_bb===\n");
+  OPTINFO_SCOPE ("vect_slp_analyze_bb", bb);
 
   /* Autodetect first vector size we try.  */
   current_vector_size = 0;
@@ -3222,7 +3188,10 @@ vect_slp_bb (basic_block bb)
 	  insns++;
 
 	  if (gimple_location (stmt) != UNKNOWN_LOCATION)
-	    vect_location = gimple_location (stmt);
+	    {
+	      vect_location = gimple_location (stmt);
+	      vect_optinfo_location = stmt;
+	    }
 
 	  if (!find_data_references_in_stmt (NULL, stmt, &datarefs))
 	    break;
@@ -3244,14 +3213,15 @@ vect_slp_bb (basic_block bb)
       if (bb_vinfo
 	  && dbg_cnt (vect_slp))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location, "SLPing BB part\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "SLPing BB part";
 
 	  vect_schedule_slp (bb_vinfo);
 
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location,
-			     "basic block part vectorized\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_NOTE
+	      << "basic block part vectorized";
 
 	  vectorized = true;
 	}
@@ -3287,13 +3257,11 @@ vect_slp_bb (basic_block bb)
 	{
 	  /* Try the next biggest vector size.  */
 	  current_vector_size = vector_sizes[next_size++];
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "***** Re-trying analysis with "
-			       "vector size ");
-	      dump_dec (MSG_NOTE, current_vector_size);
-	      dump_printf (MSG_NOTE, "\n");
+	      OPTINFO_VECT_NOTE
+		<< "***** Re-trying analysis with vector size "
+		<< current_vector_size;
 	    }
 
 	  /* Start over.  */
@@ -3951,13 +3919,12 @@ vect_transform_slp_perm_load (slp_tree node, vec<tree> dr_chain,
 	    }
 	  else
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				   "permutation requires at "
-				   "least three vectors ");
-		  dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-				    stmt, 0);
+		  // FIXME: would be better to use "stmt" for location here:
+                  OPTINFO_VECT_FAILURE
+		    << "permutation requires at least three vectors "
+		    << stmt;
 		}
 	      gcc_assert (analyze_only);
 	      return false;
@@ -3973,17 +3940,13 @@ vect_transform_slp_perm_load (slp_tree node, vec<tree> dr_chain,
 	      indices.new_vector (mask, 2, nunits);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  if (optinfo_enabled_p ())
 		    {
-		      dump_printf_loc (MSG_MISSED_OPTIMIZATION,
-				       vect_location, 
-				       "unsupported vect permute { ");
+		      pending_optinfo info = OPTINFO_VECT_FAILURE
+			<< "unsupported vect permute { ";
 		      for (i = 0; i < nunits; ++i)
-			{
-			  dump_dec (MSG_MISSED_OPTIMIZATION, mask[i]);
-			  dump_printf (MSG_MISSED_OPTIMIZATION, " ");
-			}
-		      dump_printf (MSG_MISSED_OPTIMIZATION, "}\n");
+			info << mask[i] << " ";
+		      info << "}";
 		    }
 		  gcc_assert (analyze_only);
 		  return false;
@@ -4093,7 +4056,7 @@ vect_schedule_slp_instance (slp_tree node, slp_instance instance,
   if (!SLP_TREE_VEC_STMTS (node).exists ())
     SLP_TREE_VEC_STMTS (node).create (SLP_TREE_NUMBER_OF_VEC_STMTS (node));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE,vect_location,
 		       "------>vectorizing SLP node starting from: ");
@@ -4255,9 +4218,9 @@ vect_schedule_slp (vec_info *vinfo)
       /* Schedule the tree of INSTANCE.  */
       is_store = vect_schedule_slp_instance (SLP_INSTANCE_TREE (instance),
                                              instance, bst_map);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "vectorizing stmts using SLP.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_NOTE
+	  << "vectorizing stmts using SLP";
     }
   delete bst_map;
 
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index 3e73118..55e5029 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -197,7 +197,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
   bool save_live_p = STMT_VINFO_LIVE_P (stmt_info);
   gimple *pattern_stmt;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "mark relevant %d, live %d: ", relevant, live_p);
@@ -217,7 +217,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
 
       pattern_stmt = STMT_VINFO_RELATED_STMT (stmt_info);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "last stmt in pattern. don't mark"
 			 " relevant/live.\n");
@@ -235,7 +235,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
   if (STMT_VINFO_RELEVANT (stmt_info) == save_relevant
       && STMT_VINFO_LIVE_P (stmt_info) == save_live_p)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "already marked relevant/live.\n");
       return;
@@ -265,9 +265,9 @@ is_simple_and_all_uses_invariant (gimple *stmt, loop_vec_info loop_vinfo)
 
       if (!vect_is_simple_use (op, loop_vinfo, &def_stmt, &dt))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "use not simple.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "use not simple");
 	  return false;
 	}
 
@@ -313,7 +313,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
     if (gimple_vdef (stmt)
 	&& !gimple_clobber_p (stmt))
       {
-	if (dump_enabled_p ())
+	if (optinfo_enabled_p ())
 	  dump_printf_loc (MSG_NOTE, vect_location,
                            "vec_stmt_relevant_p: stmt has vdefs.\n");
 	*relevant = vect_used_in_scope;
@@ -327,7 +327,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
 	  basic_block bb = gimple_bb (USE_STMT (use_p));
 	  if (!flow_bb_inside_loop_p (loop, bb))
 	    {
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		dump_printf_loc (MSG_NOTE, vect_location,
                                  "vec_stmt_relevant_p: used out of loop.\n");
 
@@ -347,7 +347,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
   if (*live_p && *relevant == vect_unused_in_scope
       && !is_simple_and_all_uses_invariant (stmt, loop_vinfo))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "vec_stmt_relevant_p: stmt live but not relevant.\n");
       *relevant = vect_used_only_live;
@@ -467,8 +467,8 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 
   if (!vect_is_simple_use (use, loop_vinfo, &def_stmt, &dt))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "not vectorized: unsupported use in stmt.\n");
       return false;
     }
@@ -479,7 +479,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
   def_bb = gimple_bb (def_stmt);
   if (!flow_bb_inside_loop_p (loop, def_bb))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location, "def_stmt is out of loop.\n");
       return true;
     }
@@ -497,7 +497,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
       && STMT_VINFO_DEF_TYPE (dstmt_vinfo) == vect_reduction_def
       && bb->loop_father == def_bb->loop_father)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "reduc-stmt defining reduc-phi in the same nest.\n");
       if (STMT_VINFO_IN_PATTERN_P (dstmt_vinfo))
@@ -517,7 +517,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 		...		  */
   if (flow_loop_nested_p (def_bb->loop_father, bb->loop_father))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "outer-loop def-stmt defining inner-loop stmt.\n");
 
@@ -555,7 +555,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 		stmt # use (d)		*/
   else if (flow_loop_nested_p (bb->loop_father, def_bb->loop_father))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "inner-loop def-stmt defining outer-loop stmt.\n");
 
@@ -590,7 +590,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 	   && (PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (bb->loop_father))
 	       == use))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "induction value on backedge.\n");
       return true;
@@ -633,9 +633,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
   bool live_p;
   enum vect_relevant relevant;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_mark_stmts_to_be_vectorized ===\n");
+  VECT_OPTINFO_SCOPE ("vect_mark_stmts_to_be_vectorized");
 
   auto_vec<gimple *, 64> worklist;
 
@@ -646,7 +644,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
 	{
 	  phi = gsi_stmt (si);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "init: phi relevant? ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -658,7 +656,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
 	{
 	  stmt = gsi_stmt (si);
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "init: stmt relevant? ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -676,7 +674,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       ssa_op_iter iter;
 
       stmt = worklist.pop ();
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
           dump_printf_loc (MSG_NOTE, vect_location, "worklist: examine stmt: ");
           dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -708,8 +706,8 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
 	      {
-		if (dump_enabled_p ())
-		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+		if (optinfo_enabled_p ())
+		  OPTINFO_VECT_FAILURE << optinfo_printf (
 				   "unsupported use of reduction.\n");
 		return false;
 	      }
@@ -720,8 +718,8 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_in_outer_by_reduction
 		&& relevant != vect_used_in_outer)
               {
-                if (dump_enabled_p ())
-                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                if (optinfo_enabled_p ())
+                  OPTINFO_VECT_FAILURE << optinfo_printf (
                                    "unsupported use of nested cycle.\n");
 
                 return false;
@@ -733,8 +731,8 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
               {
-                if (dump_enabled_p ())
-                  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                if (optinfo_enabled_p ())
+                  OPTINFO_VECT_FAILURE << optinfo_printf (
                                    "unsupported use of double reduction.\n");
 
                 return false;
@@ -840,7 +838,7 @@ vect_model_simple_cost (stmt_vec_info stmt_info, int ncopies,
   inside_cost = record_stmt_cost (body_cost_vec, ncopies, vector_stmt,
 				  stmt_info, 0, vect_body);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_simple_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -885,7 +883,7 @@ vect_model_promotion_demotion_cost (stmt_vec_info stmt_info,
       prologue_cost += add_stmt_cost (target_cost_data, 1, vector_stmt,
 				      stmt_info, 0, vect_prologue);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_promotion_demotion_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -939,7 +937,7 @@ vect_model_store_cost (stmt_vec_info stmt_info, int ncopies,
       inside_cost = record_stmt_cost (body_cost_vec, nstmts, vec_perm,
 				      stmt_info, 0, vect_body);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_model_store_cost: strided group_size = %d .\n",
                          group_size);
@@ -969,7 +967,7 @@ vect_model_store_cost (stmt_vec_info stmt_info, int ncopies,
 				       vec_to_scalar, stmt_info, 0, vect_body);
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_store_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -994,7 +992,7 @@ vect_get_store_cost (struct data_reference *dr, int ncopies,
 					  vector_store, stmt_info, 0,
 					  vect_body);
 
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_store_cost: aligned.\n");
         break;
@@ -1006,7 +1004,7 @@ vect_get_store_cost (struct data_reference *dr, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies,
 					  unaligned_store, stmt_info,
 					  DR_MISALIGNMENT (dr), vect_body);
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_store_cost: unaligned supported by "
                            "hardware.\n");
@@ -1017,8 +1015,8 @@ vect_get_store_cost (struct data_reference *dr, int ncopies,
       {
         *inside_cost = VECT_MAX_COST;
 
-        if (dump_enabled_p ())
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+        if (optinfo_enabled_p ())
+          OPTINFO_VECT_FAILURE << optinfo_printf (
                            "vect_model_store_cost: unsupported access.\n");
         break;
       }
@@ -1075,7 +1073,7 @@ vect_model_load_cost (stmt_vec_info stmt_info, int ncopies,
       inside_cost = record_stmt_cost (body_cost_vec, nstmts, vec_perm,
 				      stmt_info, 0, vect_body);
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_model_load_cost: strided group_size = %d .\n",
                          group_size);
@@ -1101,7 +1099,7 @@ vect_model_load_cost (stmt_vec_info stmt_info, int ncopies,
     inside_cost += record_stmt_cost (body_cost_vec, ncopies, vec_construct,
 				     stmt_info, 0, vect_body);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_load_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -1128,7 +1126,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies, vector_load,
 					  stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: aligned.\n");
 
@@ -1141,7 +1139,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
 					  unaligned_load, stmt_info,
 					  DR_MISALIGNMENT (dr), vect_body);
 
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: unaligned supported by "
                            "hardware.\n");
@@ -1162,7 +1160,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
 	  *inside_cost += record_stmt_cost (body_cost_vec, 1, vector_stmt,
 					    stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: explicit realign\n");
 
@@ -1170,7 +1168,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
       }
     case dr_explicit_realign_optimized:
       {
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: unaligned software "
                            "pipelined.\n");
@@ -1198,7 +1196,7 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies, vec_perm,
 					  stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        if (optinfo_enabled_p ())
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: explicit realign optimized"
                            "\n");
@@ -1210,8 +1208,8 @@ vect_get_load_cost (struct data_reference *dr, int ncopies,
       {
         *inside_cost = VECT_MAX_COST;
 
-        if (dump_enabled_p ())
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+        if (optinfo_enabled_p ())
+          OPTINFO_VECT_FAILURE << optinfo_printf (
                            "vect_model_load_cost: unsupported access.\n");
         break;
       }
@@ -1260,7 +1258,7 @@ vect_init_vector_1 (gimple *stmt, gimple *new_stmt, gimple_stmt_iterator *gsi)
        }
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "created new init_stmt: ");
@@ -1421,20 +1419,20 @@ vect_get_vec_def_for_operand (tree op, gimple *stmt, tree vectype)
   stmt_vec_info stmt_vinfo = vinfo_for_stmt (stmt);
   loop_vec_info loop_vinfo = STMT_VINFO_LOOP_VINFO (stmt_vinfo);
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-                       "vect_get_vec_def_for_operand: ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, op);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "vect_get_vec_def_for_operand: "
+	<< slim (op);
     }
 
   is_simple_use = vect_is_simple_use (op, loop_vinfo, &def_stmt, &dt);
   gcc_assert (is_simple_use);
-  if (def_stmt && dump_enabled_p ())
+  if (def_stmt && optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "  def_stmt =  ");
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, def_stmt, 0);
+      OPTINFO_VECT_NOTE
+	<< "  def_stmt =  "
+	<< def_stmt;
     }
 
   if (dt == vect_constant_def || dt == vect_external_def)
@@ -1612,7 +1610,7 @@ vect_finish_stmt_generation_1 (gimple *stmt, gimple *vec_stmt)
 
   set_vinfo_for_stmt (vec_stmt, new_stmt_vec_info (vec_stmt, vinfo));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location, "add new stmt: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, vec_stmt, 0);
@@ -1749,8 +1747,8 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 	  ? !vect_load_lanes_supported (vectype, group_size, true)
 	  : !vect_store_lanes_supported (vectype, group_size, true))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "can't use a fully-masked loop because the"
 			     " target doesn't have an appropriate masked"
 			     " load/store-lanes instruction.\n");
@@ -1773,8 +1771,8 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 						   TYPE_SIGN (offset_type),
 						   gs_info->scale))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "can't use a fully-masked loop because the"
 			     " target doesn't have an appropriate masked"
 			     " gather load or scatter store instruction.\n");
@@ -1791,8 +1789,8 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
     {
       /* Element X of the data must come from iteration i * VF + X of the
 	 scalar loop.  We need more work to support other mappings.  */
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "can't use a fully-masked loop because an access"
 			 " isn't contiguous.\n");
       LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
@@ -1805,8 +1803,8 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 	 GET_MODE_SIZE (vecmode)).exists (&mask_mode))
       || !can_vec_mask_load_store_p (vecmode, mask_mode, is_load))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "can't use a fully-masked loop because the target"
 			 " doesn't have the appropriate masked load or"
 			 " store.\n");
@@ -1871,7 +1869,7 @@ vect_truncate_gather_scatter_offset (gimple *stmt, loop_vec_info loop_vinfo,
   if (TREE_CODE (step) != INTEGER_CST)
     {
       /* ??? Perhaps we could use range information here?  */
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "cannot truncate variable step.\n");
       return false;
@@ -1937,7 +1935,7 @@ vect_truncate_gather_scatter_offset (gimple *stmt, loop_vec_info loop_vinfo,
       return true;
     }
 
-  if (overflow_p && dump_enabled_p ())
+  if (overflow_p && optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "truncating gather/scatter offset to %d bits"
 		     " might change its value.\n", element_bits);
@@ -1978,7 +1976,7 @@ vect_use_strided_gather_scatters_p (gimple *stmt, loop_vec_info loop_vinfo,
       gs_info->offset = fold_convert (offset_type, gs_info->offset);
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "using gather/scatter for strided/grouped access,"
 		     " scale = %d\n", gs_info->scale);
@@ -2101,7 +2099,7 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
 	  overrun_p = loop_vinfo && gap != 0;
 	  if (overrun_p && vls_type != VLS_LOAD)
 	    {
-	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      OPTINFO_VECT_FAILURE << optinfo_printf (
 			       "Grouped store with gaps requires"
 			       " non-consecutive accesses\n");
 	      return false;
@@ -2116,8 +2114,8 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
 	    overrun_p = false;
 	  if (overrun_p && !can_overrun_p)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "Peeling for outer loop is not supported\n");
 	      return false;
 	    }
@@ -2202,9 +2200,9 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
 	  enum vect_def_type dt;
 	  if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-				 "use not simple.\n");
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
+				       "use not simple");
 	      return false;
 	    }
 	  next_stmt = GROUP_NEXT_ELEMENT (vinfo_for_stmt (next_stmt));
@@ -2214,8 +2212,8 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
   if (overrun_p)
     {
       gcc_assert (can_overrun_p);
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "Data access with gaps requires scalar "
 			 "epilogue loop\n");
       LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo) = true;
@@ -2239,8 +2237,8 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
 
   if (ncopies > 1)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "multiple types with negative step.\n");
       return VMAT_ELEMENTWISE;
     }
@@ -2249,15 +2247,15 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
   if (alignment_support_scheme != dr_aligned
       && alignment_support_scheme != dr_unaligned_supported)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "negative step but alignment required.\n");
       return VMAT_ELEMENTWISE;
     }
 
   if (vls_type == VLS_STORE_INVARIANT)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "negative step with invariant source;"
 			 " no permute needed.\n");
@@ -2266,8 +2264,8 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
 
   if (!perm_mask_for_reverse (vectype))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "negative step and reversing not supported.\n");
       return VMAT_ELEMENTWISE;
     }
@@ -2305,8 +2303,8 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
 				    &gs_info->offset_dt,
 				    &gs_info->offset_vectype))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "%s index use not simple.\n",
 			     vls_type == VLS_LOAD ? "gather" : "scatter");
 	  return false;
@@ -2347,8 +2345,8 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
        || *memory_access_type == VMAT_STRIDED_SLP)
       && !nunits.is_constant ())
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "Not using elementwise accesses due to variable "
 			 "vectorization factor.\n");
       return false;
@@ -2363,8 +2361,8 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
 	   && !GROUP_NEXT_ELEMENT (stmt_info)
 	   && !pow2p_hwi (GROUP_SIZE (stmt_info))))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not falling back to elementwise accesses\n");
       return false;
     }
@@ -2383,16 +2381,16 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
 {
   if (!VECT_SCALAR_BOOLEAN_TYPE_P (TREE_TYPE (mask)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "mask argument is not a boolean.\n");
       return false;
     }
 
   if (TREE_CODE (mask) != SSA_NAME)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "mask argument is not an SSA name.\n");
       return false;
     }
@@ -2404,9 +2402,9 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
   if (!vect_is_simple_use (mask, stmt_info->vinfo, &def_stmt, &mask_dt,
 			   &mask_vectype))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "mask use not simple.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "mask use not simple");
       return false;
     }
 
@@ -2416,8 +2414,8 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
 
   if (!mask_vectype || !VECTOR_BOOLEAN_TYPE_P (mask_vectype))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "could not find an appropriate vector mask type.\n");
       return false;
     }
@@ -2425,15 +2423,13 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
   if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_vectype),
 		TYPE_VECTOR_SUBPARTS (vectype)))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			   "vector mask type ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, mask_vectype);
-	  dump_printf (MSG_MISSED_OPTIMIZATION,
-		       " does not match vector data type ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
-	  dump_printf (MSG_MISSED_OPTIMIZATION, ".\n");
+	  OPTINFO_VECT_FAILURE
+	    << "vector mask type "
+	    << slim (mask_vectype)
+	    << " does not match vector data type "
+	    << slim (vectype);
 	}
       return false;
     }
@@ -2456,9 +2452,9 @@ vect_check_store_rhs (gimple *stmt, tree rhs, vect_def_type *rhs_dt_out,
      native_encode_expr can handle it.  */
   if (CONSTANT_CLASS_P (rhs) && native_encode_expr (rhs, NULL, 64) == 0)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "cannot encode constant as a byte sequence.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "cannot encode constant as a byte sequence");
       return false;
     }
 
@@ -2469,18 +2465,18 @@ vect_check_store_rhs (gimple *stmt, tree rhs, vect_def_type *rhs_dt_out,
   if (!vect_is_simple_use (rhs, stmt_info->vinfo, &def_stmt, &rhs_dt,
 			   &rhs_vectype))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "use not simple.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "use not simple");
       return false;
     }
 
   tree vectype = STMT_VINFO_VECTYPE (stmt_info);
   if (rhs_vectype && !useless_type_conversion_p (vectype, rhs_vectype))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "incompatible vector types.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "incompatible vector types");
       return false;
     }
 
@@ -2886,9 +2882,8 @@ vectorizable_bswap (gimple *stmt, gimple_stmt_iterator *gsi,
   if (! vec_stmt)
     {
       STMT_VINFO_TYPE (stmt_info) = call_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location, "=== vectorizable_bswap ==="
-                         "\n");
+
+      VECT_OPTINFO_SCOPE ("vectorizable_bswap");
       if (! slp_node)
 	{
 	  add_stmt_cost (stmt_info->vinfo->target_cost_data,
@@ -3065,9 +3060,9 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       if (rhs_type
 	  && !types_compatible_p (rhs_type, TREE_TYPE (op)))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "argument types differ.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "argument types differ");
 	  return false;
 	}
       if (!rhs_type)
@@ -3075,9 +3070,9 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt[i], &opvectype))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "use not simple.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "use not simple");
 	  return false;
 	}
 
@@ -3086,9 +3081,9 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (opvectype
 	       && opvectype != vectype_in)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "argument vector types differ.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "argument vector types differ");
 	  return false;
 	}
     }
@@ -3100,12 +3095,11 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     gcc_assert (vectype_in);
   if (!vectype_in)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "no vectype for scalar type ");
-          dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, rhs_type);
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+          OPTINFO_VECT_FAILURE
+	    << "no vectype for scalar type "
+	    << slim (rhs_type);
         }
 
       return false;
@@ -3126,8 +3120,8 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* We only handle functions that do not read or clobber memory.  */
   if (gimple_vuse (stmt))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "function reads from or writes to memory.\n");
       return false;
     }
@@ -3184,8 +3178,8 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 				   vectype_in, dt);
       else
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "function is not vectorizable.\n");
 	  return false;
 	}
@@ -3205,9 +3199,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = call_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location, "=== vectorizable_call ==="
-                         "\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_call");
       if (!slp_node)
 	{
 	  vect_model_simple_cost (stmt_info, ncopies, dt, ndts, NULL, NULL);
@@ -3221,7 +3213,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location, "transform call.\n");
 
   /* Handle def.  */
@@ -3666,9 +3658,9 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
 			       &thisarginfo.vectype)
 	  || thisarginfo.dt == vect_uninitialized_def)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "use not simple.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "use not simple");
 	  return false;
 	}
 
@@ -3741,8 +3733,8 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
   unsigned HOST_WIDE_INT vf;
   if (!LOOP_VINFO_VECT_FACTOR (loop_vinfo).is_constant (&vf))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "not considering SIMD clones; not yet supported"
 			 " for variable-width vectors.\n");
       return NULL;
@@ -3884,16 +3876,14 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
 	    STMT_VINFO_SIMD_CLONE_INFO (stmt_info).safe_push (sll);
 	  }
       STMT_VINFO_TYPE (stmt_info) = call_simd_clone_vec_info_type;
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "=== vectorizable_simd_clone_call ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_simd_clone_call");
 /*      vect_model_simple_cost (stmt_info, ncopies, dt, NULL, NULL); */
       return true;
     }
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location, "transform call.\n");
 
   /* Handle def.  */
@@ -4524,19 +4514,17 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 	  || (INTEGRAL_TYPE_P (rhs_type)
 	      && !type_has_mode_precision_p (rhs_type))))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "type conversion to/from bit-precision unsupported."
-                         "\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "type conversion to/from bit-precision unsupported");
       return false;
     }
 
   /* Check the operands of the operation.  */
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype_in))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "use not simple.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << "use not simple";
       return false;
     }
   if (op_type == binary_op)
@@ -4554,9 +4542,8 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 
       if (!ok)
 	{
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "use not simple.\n");
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_FAILURE << "use not simple";
 	  return false;
 	}
     }
@@ -4569,12 +4556,11 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype_in);
   if (!vectype_in)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "no vectype for scalar type ");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, rhs_type);
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	  OPTINFO_VECT_FAILURE
+	    << "no vectype for scalar type "
+	    << slim (rhs_type);
 	}
 
       return false;
@@ -4583,13 +4569,12 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
   if (VECTOR_BOOLEAN_TYPE_P (vectype_out)
       && !VECTOR_BOOLEAN_TYPE_P (vectype_in))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	{
-	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "can't convert between boolean and non "
-			   "boolean vectors");
-	  dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, rhs_type);
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+	  OPTINFO_VECT_FAILURE
+	    << ("can't convert between boolean and non "
+		"boolean vectors")
+	    << slim (rhs_type);
 	}
 
       return false;
@@ -4637,8 +4622,8 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 	break;
       /* FALLTHRU */
     unsupported:
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
                          "conversion not supported by target.\n");
       return false;
 
@@ -4737,9 +4722,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!vec_stmt)		/* transformation not required.  */
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_conversion ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_conversion");
       if (code == FIX_TRUNC_EXPR || code == FLOAT_EXPR)
         {
 	  STMT_VINFO_TYPE (stmt_info) = type_conversion_vec_info_type;
@@ -4763,7 +4746,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
     }
 
   /* Transform.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform conversion. ncopies = %d.\n", ncopies);
 
@@ -5108,9 +5091,9 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt[0], &vectype_in))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "use not simple.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "use not simple");
       return false;
     }
 
@@ -5140,8 +5123,8 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
       && (!VECTOR_BOOLEAN_TYPE_P (vectype)
 	  || !VECTOR_BOOLEAN_TYPE_P (vectype_in)))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "type conversion to/from bit-precision "
                          "unsupported.\n");
       return false;
@@ -5150,16 +5133,14 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = assignment_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_assignment ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_assignment");
       if (!slp_node)
 	vect_model_simple_cost (stmt_info, ncopies, dt, ndts, NULL, NULL);
       return true;
     }
 
   /* Transform.  */
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location, "transform assignment.\n");
 
   /* Handle def.  */
@@ -5307,8 +5288,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   vectype_out = STMT_VINFO_VECTYPE (stmt_info);
   if (!type_has_mode_precision_p (TREE_TYPE (scalar_dest)))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "bit-precision shifts not supported.\n");
       return false;
     }
@@ -5316,9 +5297,9 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   op0 = gimple_assign_rhs1 (stmt);
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "use not simple.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "use not simple");
       return false;
     }
   /* If op0 is an external or constant def use a vector type with
@@ -5329,8 +5310,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype);
   if (!vectype)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "no vectype for scalar type\n");
       return false;
     }
@@ -5343,9 +5324,9 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   op1 = gimple_assign_rhs2 (stmt);
   if (!vect_is_simple_use (op1, vinfo, &def_stmt, &dt[1], &op1_vectype))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "use not simple.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "use not simple");
       return false;
     }
 
@@ -5395,8 +5376,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
     }
   else
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "operand mode requires invariant argument.\n");
       return false;
     }
@@ -5405,7 +5386,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!scalar_shift_arg)
     {
       optab = optab_for_tree_code (code, vectype, optab_vector);
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vector/vector shift/rotate found.\n");
 
@@ -5414,8 +5395,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       if (op1_vectype == NULL_TREE
 	  || TYPE_MODE (op1_vectype) != TYPE_MODE (vectype))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
                              "unusable type for last operand in"
                              " vector/vector shift/rotate.\n");
 	  return false;
@@ -5429,7 +5410,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       if (optab
           && optab_handler (optab, TYPE_MODE (vectype)) != CODE_FOR_nothing)
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             dump_printf_loc (MSG_NOTE, vect_location,
                              "vector/scalar shift/rotate found.\n");
         }
@@ -5442,7 +5423,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
             {
 	      scalar_shift_arg = false;
 
-              if (dump_enabled_p ())
+              if (optinfo_enabled_p ())
                 dump_printf_loc (MSG_NOTE, vect_location,
                                  "vector/vector shift/rotate found.\n");
 
@@ -5459,8 +5440,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
 		      && TYPE_MODE (TREE_TYPE (vectype))
 			 != TYPE_MODE (TREE_TYPE (op1)))
 		    {
-                      if (dump_enabled_p ())
-                        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                      if (optinfo_enabled_p ())
+                        OPTINFO_VECT_FAILURE << optinfo_printf (
                                          "unusable type for last operand in"
                                          " vector/vector shift/rotate.\n");
 		      return false;
@@ -5479,8 +5460,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   /* Supportable by target?  */
   if (!optab)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "no optab.\n");
       return false;
     }
@@ -5488,15 +5469,15 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   icode = (int) optab_handler (optab, vec_mode);
   if (icode == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "op not supported by target.\n");
       /* Check only during analysis.  */
       if (maybe_ne (GET_MODE_SIZE (vec_mode), UNITS_PER_WORD)
 	  || (!vec_stmt
 	      && !vect_worthwhile_without_simd_p (vinfo, code)))
         return false;
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "proceeding using word mode.\n");
     }
@@ -5506,8 +5487,8 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       && !VECTOR_MODE_P (TYPE_MODE (vectype))
       && !vect_worthwhile_without_simd_p (vinfo, code))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "not worthwhile without SIMD support.\n");
       return false;
     }
@@ -5515,9 +5496,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = shift_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_shift ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_shift");
       if (!slp_node)
 	vect_model_simple_cost (stmt_info, ncopies, dt, ndts, NULL, NULL);
       return true;
@@ -5525,7 +5504,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform binary/unary operation.\n");
 
@@ -5547,7 +5526,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
               optab_op2_mode = insn_data[icode].operand[2].mode;
               if (!VECTOR_MODE_P (optab_op2_mode))
                 {
-                  if (dump_enabled_p ())
+                  if (optinfo_enabled_p ())
                     dump_printf_loc (MSG_NOTE, vect_location,
                                      "operand 1 using scalar mode.\n");
                   vec_oprnd1 = op1;
@@ -5677,8 +5656,8 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   op_type = TREE_CODE_LENGTH (code);
   if (op_type != unary_op && op_type != binary_op && op_type != ternary_op)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "num. args = %d (not unary/binary/ternary op).\n",
                          op_type);
       return false;
@@ -5696,8 +5675,8 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       && code != BIT_XOR_EXPR
       && code != BIT_AND_EXPR)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "bit-precision arithmetic not supported.\n");
       return false;
     }
@@ -5705,9 +5684,9 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   op0 = gimple_assign_rhs1 (stmt);
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "use not simple.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
+			       "use not simple");
       return false;
     }
   /* If op0 is an external or constant def use a vector type with
@@ -5723,8 +5702,8 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  if (!VECT_SCALAR_BOOLEAN_TYPE_P (TREE_TYPE (scalar_dest)))
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "not supported operation on bool value.\n");
 	      return false;
 	    }
@@ -5737,13 +5716,11 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype);
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "no vectype for scalar type ");
-          dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
-                             TREE_TYPE (op0));
-          dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
+          OPTINFO_VECT_FAILURE
+	    << "no vectype for scalar type "
+	    << slim (TREE_TYPE (op0));
         }
 
       return false;
@@ -5759,9 +5736,9 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       op1 = gimple_assign_rhs2 (stmt);
       if (!vect_is_simple_use (op1, vinfo, &def_stmt, &dt[1]))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "use not simple.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "use not simple");
 	  return false;
 	}
     }
@@ -5770,9 +5747,9 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       op2 = gimple_assign_rhs3 (stmt);
       if (!vect_is_simple_use (op2, vinfo, &def_stmt, &dt[2]))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                             "use not simple.\n");
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
+				   "use not simple");
 	  return false;
 	}
     }
@@ -5802,8 +5779,8 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       optab = optab_for_tree_code (code, vectype, optab_default);
       if (!optab)
 	{
-          if (dump_enabled_p ())
-            dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+          if (optinfo_enabled_p ())
+            OPTINFO_VECT_FAILURE << optinfo_printf (
                              "no optab.\n");
 	  return false;
 	}
@@ -5813,14 +5790,14 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!target_support_p)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
                          "op not supported by target.\n");
       /* Check only during analysis.  */
       if (maybe_ne (GET_MODE_SIZE (vec_mode), UNITS_PER_WORD)
 	  || (!vec_stmt && !vect_worthwhile_without_simd_p (vinfo, code)))
         return false;
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "proceeding using word mode.\n");
     }
@@ -5830,8 +5807,8 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       && !vec_stmt
       && !vect_worthwhile_without_simd_p (vinfo, code))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "not worthwhile without SIMD support.\n");
       return false;
     }
@@ -5839,9 +5816,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = op_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_operation ===\n");
+      VECT_OPTINFO_SCOPE ("vectorizable_operation");
       if (!slp_node)
 	vect_model_simple_cost (stmt_info, ncopies, dt, ndts, NULL, NULL);
       return true;
@@ -5849,7 +5824,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform binary/unary operation.\n");
 
@@ -6029,7 +6004,7 @@ get_group_alias_ptr_type (gimple *first_stmt)
       if (get_alias_set (DR_REF (first_dr))
 	  != get_alias_set (DR_REF (next_dr)))
 	{
-	  if (dump_enabled_p ())
+	  if (optinfo_enabled_p ())
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "conflicting alias set types.\n");
 	  return ptr_type_node;
@@ -6128,8 +6103,8 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (slp_node != NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "SLP of masked stores not supported.\n");
 	  return false;
 	}
@@ -6174,8 +6149,8 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* FORNOW.  This restriction should be relaxed.  */
   if (loop && nested_in_vect_loop_p (loop, stmt) && ncopies > 1)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "multiple types in nested loop.\n");
       return false;
     }
@@ -6206,8 +6181,8 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (memory_access_type != VMAT_LOAD_STORE_LANES
 	       && (memory_access_type != VMAT_GATHER_SCATTER || gs_info.decl))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "unsupported access type for masked store.\n");
 	  return false;
 	}
@@ -6450,7 +6425,7 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   else
     ref_type = reference_alias_ptr_type (DR_REF (first_dr));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform store. ncopies = %d\n", ncopies);
 
@@ -7291,8 +7266,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (slp_node != NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "SLP of masked loads not supported.\n");
 	  return false;
 	}
@@ -7335,8 +7310,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* FORNOW. This restriction should be relaxed.  */
   if (nested_in_vect_loop && ncopies > 1)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "multiple types in nested loop.\n");
       return false;
     }
@@ -7348,8 +7323,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       && maybe_gt (LOOP_VINFO_VECT_FACTOR (loop_vinfo),
 		   STMT_VINFO_MIN_NEG_DIST (stmt_info)))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "cannot perform implicit CSE when unrolling "
 			 "with negative dependence distance\n");
       return false;
@@ -7362,8 +7337,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     (e.g. - data copies).  */
   if (optab_handler (mov_optab, mode) == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "Aligned load, but unsupported type.\n");
       return false;
     }
@@ -7389,8 +7364,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	  && maybe_gt (LOOP_VINFO_VECT_FACTOR (loop_vinfo),
 		       STMT_VINFO_MIN_NEG_DIST (stmt_info)))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "cannot perform implicit CSE when performing "
 			     "group loads with negative dependence distance\n");
 	  return false;
@@ -7404,8 +7379,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	      != STMT_SLP_TYPE (vinfo_for_stmt
 				 (STMT_VINFO_GROUP_SAME_DR_STMT (stmt_info)))))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "conflicting SLP types for CSEd load\n");
 	  return false;
 	}
@@ -7435,8 +7410,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
 	  if (TREE_CODE (masktype) == INTEGER_TYPE)
 	    {
-	      if (dump_enabled_p ())
-		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	      if (optinfo_enabled_p ())
+		OPTINFO_VECT_FAILURE << optinfo_printf (
 				 "masked gather with integer mask not"
 				 " supported.");
 	      return false;
@@ -7445,8 +7420,8 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (memory_access_type != VMAT_LOAD_STORE_LANES
 	       && memory_access_type != VMAT_GATHER_SCATTER)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "unsupported access type for masked load.\n");
 	  return false;
 	}
@@ -7474,7 +7449,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     gcc_assert (memory_access_type
 		== STMT_VINFO_MEMORY_ACCESS_TYPE (stmt_info));
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform load. ncopies = %d\n", ncopies);
 
@@ -8304,7 +8279,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 		      && !nested_in_vect_loop
 		      && hoist_defs_of_uses (stmt, loop))
 		    {
-		      if (dump_enabled_p ())
+		      if (optinfo_enabled_p ())
 			{
 			  dump_printf_loc (MSG_NOTE, vect_location,
 					   "hoisting out of the vectorized "
@@ -8566,8 +8541,8 @@ vectorizable_condition (gimple *stmt, gimple_stmt_iterator *gsi,
       /* FORNOW: not yet supported.  */
       if (STMT_VINFO_LIVE_P (stmt_info))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
 			     "value used after loop.\n");
 	  return false;
 	}
@@ -8968,8 +8943,8 @@ vectorizable_comparison (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (STMT_VINFO_LIVE_P (stmt_info))
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE << optinfo_printf (
 			 "value used after loop.\n");
       return false;
     }
@@ -9225,7 +9200,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
   gimple *pattern_stmt;
   gimple_seq pattern_def_seq;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
       dump_printf_loc (MSG_NOTE, vect_location, "==> examining statement: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -9233,8 +9208,8 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 
   if (gimple_has_volatile_ops (stmt))
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << optinfo_printf (
                          "not vectorized: stmt has volatile operands\n");
 
       return false;
@@ -9266,7 +9241,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
           /* Analyze PATTERN_STMT instead of the original stmt.  */
           stmt = pattern_stmt;
           stmt_info = vinfo_for_stmt (pattern_stmt);
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             {
               dump_printf_loc (MSG_NOTE, vect_location,
                                "==> examining pattern statement: ");
@@ -9275,7 +9250,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
         }
       else
         {
-          if (dump_enabled_p ())
+          if (optinfo_enabled_p ())
             dump_printf_loc (MSG_NOTE, vect_location, "irrelevant.\n");
 
           return true;
@@ -9288,7 +9263,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
                || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_stmt))))
     {
       /* Analyze PATTERN_STMT too.  */
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "==> examining pattern statement: ");
@@ -9313,7 +9288,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 	      || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_def_stmt)))
 	    {
 	      /* Analyze def stmt of STMT if it's a pattern stmt.  */
-	      if (dump_enabled_p ())
+	      if (optinfo_enabled_p ())
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location,
                                    "==> examining pattern def statement: ");
@@ -9402,12 +9377,11 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 
   if (!ok)
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "not vectorized: relevant stmt not ");
-          dump_printf (MSG_MISSED_OPTIMIZATION, "supported: ");
-          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+          OPTINFO_VECT_FAILURE
+	    << "not vectorized: relevant stmt not supported: "
+	    << stmt;
         }
 
       return false;
@@ -9421,11 +9395,11 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
   if (STMT_VINFO_TYPE (stmt_info) != reduc_vec_info_type
       && !can_vectorize_live_stmts (stmt, NULL, node, NULL))
     {
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         {
-          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                           "not vectorized: live stmt not supported: ");
-          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+          OPTINFO_VECT_FAILURE
+	    << "not vectorized: live stmt not supported: "
+	    << stmt;
         }
 
        return false;
@@ -9540,8 +9514,8 @@ vect_transform_stmt (gimple *stmt, gimple_stmt_iterator *gsi,
     default:
       if (!STMT_VINFO_LIVE_P (stmt_info))
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+	  if (optinfo_enabled_p ())
+	    OPTINFO_VECT_FAILURE << optinfo_printf (
                              "stmt not supported.\n");
 	  gcc_unreachable ();
 	}
@@ -9569,7 +9543,7 @@ vect_transform_stmt (gimple *stmt, gimple_stmt_iterator *gsi,
       tree scalar_dest;
       gimple *exit_phi;
 
-      if (dump_enabled_p ())
+      if (optinfo_enabled_p ())
         dump_printf_loc (MSG_NOTE, vect_location,
                          "Record the vdef for outer-loop vectorization.\n");
 
@@ -9907,12 +9881,11 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
   *def_stmt = NULL;
   *dt = vect_unknown_def_type;
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location,
-                       "vect_is_simple_use: operand ");
-      dump_generic_expr (MSG_NOTE, TDF_SLIM, operand);
-      dump_printf (MSG_NOTE, "\n");
+      OPTINFO_VECT_NOTE
+	<< "vect_is_simple_use: operand "
+	<< slim (operand);
     }
 
   if (CONSTANT_CLASS_P (operand))
@@ -9929,9 +9902,9 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
 
   if (TREE_CODE (operand) != SSA_NAME)
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "not ssa-name.\n");
+      if (optinfo_enabled_p ())
+	OPTINFO_VECT_FAILURE
+	  << "not ssa-name";
       return false;
     }
 
@@ -9942,10 +9915,10 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
     }
 
   *def_stmt = SSA_NAME_DEF_STMT (operand);
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "def_stmt: ");
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, *def_stmt, 0);
+      OPTINFO_VECT_NOTE
+	<< "def_stmt: " << *def_stmt;
     }
 
   if (! vect_stmt_in_region_p (vinfo, *def_stmt))
@@ -9956,46 +9929,45 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
       *dt = STMT_VINFO_DEF_TYPE (stmt_vinfo);
     }
 
-  if (dump_enabled_p ())
+  if (optinfo_enabled_p ())
     {
-      dump_printf_loc (MSG_NOTE, vect_location, "type of def: ");
+      pending_optinfo info = OPTINFO_VECT_NOTE << "type of def: ";
       switch (*dt)
 	{
 	case vect_uninitialized_def:
-	  dump_printf (MSG_NOTE, "uninitialized\n");
+	  info << "uninitialized";
 	  break;
 	case vect_constant_def:
-	  dump_printf (MSG_NOTE, "constant\n");
+	  info << "constant";
 	  break;
 	case vect_external_def:
-	  dump_printf (MSG_NOTE, "external\n");
+	  info << "external";
 	  break;
 	case vect_internal_def:
-	  dump_printf (MSG_NOTE, "internal\n");
+	  info << "internal";
 	  break;
 	case vect_induction_def:
-	  dump_printf (MSG_NOTE, "induction\n");
+	  info << "induction";
 	  break;
 	case vect_reduction_def:
-	  dump_printf (MSG_NOTE, "reduction\n");
+	  info << "reduction";
 	  break;
 	case vect_double_reduction_def:
-	  dump_printf (MSG_NOTE, "double reduction\n");
+	  info << "double reduction";
 	  break;
 	case vect_nested_cycle:
-	  dump_printf (MSG_NOTE, "nested cycle\n");
+	  info << "nested cycle";
 	  break;
 	case vect_unknown_def_type:
-	  dump_printf (MSG_NOTE, "unknown\n");
+	  info << "unknown";
 	  break;
 	}
     }
 
   if (*dt == vect_unknown_def_type)
     {
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "Unsupported pattern.\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << "Unsupported pattern";
       return false;
     }
 
@@ -10006,9 +9978,8 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
     case GIMPLE_CALL:
       break;
     default:
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-                         "unsupported defining stmt:\n");
+      if (optinfo_enabled_p ())
+        OPTINFO_VECT_FAILURE << "unsupported defining stmt:";
       return false;
     }
 
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index fb81b98..f31bc5d 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -78,10 +78,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-scalar-evolution.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 
 /* Loop or bb location.  */
 source_location vect_location;
+optinfo_location vect_optinfo_location;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> stmt_vec_info_vec;
@@ -649,17 +651,28 @@ vectorize_loops (void)
 	if (!((flag_tree_loop_vectorize
 	       && optimize_loop_nest_for_speed_p (loop))
 	      || loop->force_vectorize))
-	  continue;
+	  {
+	    /* FIXME: maybe break these out into separate failure messages.
+	       This can happen e.g. when the loop is not hot.  */
+	    if (optinfo_enabled_p ())
+	      {
+		vect_location = find_loop_location (loop);
+		OPTINFO_VECT_FAILURE << "not attempting to optimize loop nest";
+	      }
+	    continue;
+	  }
 	orig_loop_vinfo = NULL;
 	loop_vectorized_call = vect_loop_vectorized_call (loop);
 	loop_dist_alias_call = vect_loop_dist_alias_call (loop);
        vectorize_epilogue:
 	vect_location = find_loop_location (loop);
+	vect_optinfo_location = loop;
         if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
 	    && dump_enabled_p ())
 	  dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
                        LOCATION_FILE (vect_location),
 		       LOCATION_LINE (vect_location));
+	VECT_OPTINFO_SCOPE ("analyzing loop");
 
 	loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo);
 	loop->aux = loop_vinfo;
@@ -733,10 +746,8 @@ vectorize_loops (void)
 
 	if (loop_vectorized_call)
 	  set_uid_loop_bbs (loop_vinfo, loop_vectorized_call);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
-	    && dump_enabled_p ())
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                           "loop vectorized\n");
+        if (optinfo_enabled_p ())
+	  OPTINFO_VECT_SUCCESS << "loop vectorized";
 	new_loop = vect_transform_loop (loop_vinfo);
 	num_vectorized_loops++;
 	/* Now that the loop has been vectorized, allow it to be unrolled
@@ -778,14 +789,15 @@ vectorize_loops (void)
 	  }
       }
 
+  if (optinfo_enabled_p ())
+    OPTINFO_VECT_NOTE
+      << optinfo_printf ("vectorized %u loops in function",
+			 num_vectorized_loops);
+
   vect_location = UNKNOWN_LOCATION;
+  vect_optinfo_location = (gimple *)NULL;
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
-  if (dump_enabled_p ()
-      || (num_vectorized_loops > 0 && dump_enabled_p ()))
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "vectorized %u loops in function.\n",
-                     num_vectorized_loops);
 
   /*  ----------- Finalize. -----------  */
 
@@ -1120,6 +1132,7 @@ increase_alignment (void)
   varpool_node *vnode;
 
   vect_location = UNKNOWN_LOCATION;
+  vect_optinfo_location = (gimple *)NULL;
   type_align_map = new hash_map<tree, unsigned>;
 
   /* Increase the alignment of all global arrays for vectorization.  */
@@ -1137,9 +1150,12 @@ increase_alignment (void)
       if (alignment && vect_can_force_dr_alignment_p (decl, alignment))
         {
 	  vnode->increase_alignment (alignment);
-          dump_printf (MSG_NOTE, "Increasing alignment of decl: ");
-          dump_generic_expr (MSG_NOTE, TDF_SLIM, decl);
-          dump_printf (MSG_NOTE, "\n");
+	  if (optinfo_enabled_p ())
+	    {
+	      OPTINFO_VECT_NOTE
+		<< "Increasing alignment of decl: "
+		<< slim (decl);
+	    }
         }
     }
 
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 7e2b00f..45baaf3 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-data-ref.h"
 #include "tree-hash-traits.h"
 #include "target.h"
+#include "optinfo.h"
 
 /* Used for naming of new temporaries.  */
 enum vect_var_kind {
@@ -1383,6 +1384,42 @@ vect_get_scalar_dr_size (struct data_reference *dr)
 /* Source location */
 extern source_location vect_location;
 
+/* FIXME: update this whenever vect_location changes.
+   FIXME: or merge it all together into this class.  */
+extern GTY(()) optinfo_location vect_optinfo_location;
+
+extern pending_optinfo
+emit_optinfo_at_vect_location (const optinfo_impl_location &impl_location,
+			       enum optinfo_kind kind);
+
+/* Emit optimization information at the vect location.  */
+
+#define OPTINFO_VECT(KIND) \
+  (emit_optinfo_at_vect_location (OPTINFO_IMPL_LOCATION, (KIND)))
+
+/* Emit that a successful optimization happened at the vect location.  */
+
+#define OPTINFO_VECT_SUCCESS \
+  (OPTINFO_VECT (OPTINFO_KIND_SUCCESS))
+
+/* Emit that a failed optimization happened at the vect location.  */
+
+#define OPTINFO_VECT_FAILURE \
+  (OPTINFO_VECT (OPTINFO_KIND_FAILURE))
+
+/* Emit a remark relating to an optimization at the vect location.  */
+
+#define OPTINFO_VECT_NOTE \
+  (OPTINFO_VECT (OPTINFO_KIND_NOTE))
+
+/* A macro for emitting an optinfo note about entering a scope,
+   pushing and popping the scope, so that all optinfos "within"
+   the scope are nested within it.  Use the vect location.  */
+
+#define VECT_OPTINFO_SCOPE(NAME) \
+  OPTINFO_SCOPE ((NAME), vect_optinfo_location)
+
+
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
 /*-----------------------------------------------------------------*/
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis
@ 2018-05-29 20:32 David Malcolm
  2018-05-29 20:32 ` [PATCH 06/10] Experiments with using optinfo for inlining David Malcolm
                   ` (9 more replies)
  0 siblings, 10 replies; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

I want to provide more "actionable" information on how GCC optimizes
code, both for us (GCC developers), and for advanced end-users.

For us, I want to make it easier to get a sense of how an optimization
can be improved (bug-fixing).
For end-users, I want to make it easier to figure out which command-line
flags to try, or how to rework their code.

In particular, given a workload (or benchmark), how does the user figure
out:
  * "what did the optimizers do to the hottest code?"
  * "what *didn't* they do, and why not?"
  * "how can I make the code faster?" (either by editing the code, or the
     gcc options)
and as GCC developers:
  * "how can we make GCC do a better job on this code?"


The current situation
=====================

I believe we currently have four ways of emitting information on what the
optimizers are doing:

(a) -fopt-info and its variants (e.g. "-fopt-info-all").

    Consider this "-fopt-info" code (from "vect_analyze_loop_form"):

      if (dump_enabled_p ())
        {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "Symbolic number of iterations is ");
           dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
           dump_printf (MSG_NOTE, "\n");
        }

    This emits text to the destination(s) of "-fopt-info", e.g.:

       foo.c:15:3: note: Symbolic number of iterations is (unsigned int) n_9(D)

    With "-fopt-info-all" this would be emitted to stderr.
    If "details" or "note" was added when enabling the dumpfile
    (e.g. via "-fdump-tree-all-details"), it would also be added to the
    dumpfile for the current pass.

    Passes are tagged as being members of zero or more optimization groups
    ("ipa", "loop", "inline", "omp", "vec"), and these can be used to filter
    which messages are emitted.  For example, the "increase_alignment" IPA
    pass is tagged with both "loop" and "vec".

(b) other messages emitted to dumpfiles.  We have numerous other messages
    that are written just to the dumpfiles, and don't go through -fopt-info.
    Consider e.g. this from loop-unroll.c:unroll_loop_constant_iterations:

      if (dump_file)
        fprintf (dump_file,
                 ";; Unrolled loop %d times, constant # of iterations %i insns\n",
                 max_unroll, num_loop_insns (loop));

(c) IR dumps emitted to dumpfiles.  execute_one_pass and
    execute_one_ipa_transform_pass have this code:

      if (dump_file)
        do_per_function (execute_function_dump, pass);

    which captures a textual dump of the current state of each function.

(d) dot-format dumps emitted to the ".dot" dumpfiles (via the "-graph") suffix
    e.g. "-fdump-tree-all-graph".

All of these options are marked as "GCC Developer Options".

How does everyone go about investigating what GCC is doing on a given
workload or benchmark?  (FWIW in the past I've been using
  "-fdump-tree-all-graph -fdump-ipa-all-graph -fdump-rtl-all-graph"
and using a custom .dot viewer that I wrote:
  https://github.com/davidmalcolm/gcc-viewer
but it's something of an ad-hoc solution).


Problems with the current situation
===================================

-fopt-info:

* There's no integration with profile data: the report has a "wall of text"
  feel, where the few hot sites in the code are hidden amongst the many
  cold sites, with all of them treated as of having equal importance.

* -fopt-info emits lines of the form:

       foo.c:15:3: note: Symbolic number of iterations is (unsigned int) n_9(D)

  This is purely capturing the source location of the deepest point of the
  inlining chain: if there are multiple places where a function has been
  inlined, we'll get multiple reports, with no way to distinguish them.

* If a program attempts to parse this data, it can get source location
  plus a message, but there's no structure to the messages.  For example,
  we can't extract the source location of the expressions that are referred
  to in the messages.

* There are various places where we emit MSG_NOTES that look like sections
  and subsections, e.g.:

    if (dump_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
                      "=== vect_analyze_data_ref_dependences ===\n");

  This appears to be intended to express the start of a hierarchical
  section within the dump, but there's nothing to express the end of
  the section, and they're current printed in a flat way, again
  contributing to a "wall of text" feel.  In my prototype I've
  experimented with capturing the nesting, and highlighting it using
  indentation.

dumpfiles:

* These are per-pass-instance, capturing every function at each pass.
  There's no easy way to see how a particular function "evolved" through
  the passes.

* Some messages are written to dumpfiles, but don't go through -fopt-info.
  Some messages are gated with:

    (dump_flags & TDF_DETAILS)

  The divisions seems somewhat haphazard.

None of the messages are tagged with where they came from; tracking them
down requires grepping the GCC source code.


Aside: LLVM's experience with remarks
=====================================

LLVM added "remarks" as a kind of diagnostic for tracking optimization
information.  It has some similarities to our -fopt-info.

However, they ran into issues with them.  There's an excellent talk
given by Adam Nemet at the November 2016 LLVM Developers' Meeting
on "Compiler-assisted Performance Analysis":
  https://www.youtube.com/watch?v=qq0q1hfzidg
  http://llvm.org/devmtg/2016-11/Slides/Nemet-Compiler-assistedPerformanceAnalysis.pdf
  (part of http://www.llvm.org/devmtg/2016-11/ )

He ran into the "wall of text" issue: too much data, where you
can't see what's important.

His fix was to capture the data in a file format (as "optimization records")
and to write a HTML tool for visualizing them, showing the optimizations
prioritized by code hotness.

This seems like a great idea, so I've been experimenting with it for GCC.


Prototype of a GCC-based solution
=================================

I've spent some time experimenting with this for GCC.

I'm assuming the following constraints:

* Preserve existing "-fopt-info" overall (whilst allowing for minor changes
  to formatting *within* individual messages).

* Preserve existing dumpfile output, as it's what GCC contributors are
  used to.

* No extra time/memory cost for the default "disabled" case.  I assume it's
  acceptable to spend a little more time/memory when the user opts-in by
  enabling this instrumentation.

I experimented with a few approaches, and the current prototype is
something of a hybrid of them.  The prototype aims:

* to capture "optimization records", emitting them in a format
  that retains all pertinent information that can be easily parsed.
  This separates the data from its presentation.

* to associate the optimization records with code hotness,
  and to provide a report that prioritizes the records, starting with
  the hottest code.

* other metadata to capture include(s):

  * which pass is emitting this record (and data about the pass)

  * the inlining chain (rather than just the "deepest" source location)

  * as well as source location within the user's code, I'm capturing
    source location within *GCC* for where the optimization happens.
    For any message you can immediately go to the implementation
    that emitted it.

I've implemented an HTML report generator, inspired by Adam Nemet's LLVM
work, using the existing places where we're emitting "-fopt-info"
information.

Differences from Adam Nemet's work (as I understand it):

* I've added hierarchical records, so that there can be a nesting
  structure of optimization notes (otherwise there's still too much
  of a "wall of text").

* capture of GCC source location

* LLVM is using YAML for some reason; I used JSON.  Given that I'm
  capturing some different things, I didn't attempt to use the same
  file format as LLVM.

Example 1
*********

Consider this "-fopt-info" code (from "vect_analyze_loop_form")::

  if (dump_enabled_p ())
    {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "Symbolic number of iterations is ");
       dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
       dump_printf (MSG_NOTE, "\n");
    }

This purely emits text to any destination(s) of "-fopt-info", e.g.
by writing:

  foo.c:15:3: note: Symbolic number of iterations is (unsigned int) n_9(D)

to stderr, and/or the dumpfile for the current pass.

This can be rewritten for my optimization records prototype as:

  if (optinfo_enabled_p ())
    {
      OPTINFO_VECT_NOTE
        << "symbolic number of iterations is "
        << details (number_of_iterations);
    }

It can then be emitted in up to three ways:

(a) as before via "-fopt-info"::

    foo.c:15:3: note: symbolic number of iterations is (unsigned int) n_9(D)

  (albeit with capitalization changes), to the "-fopt-info" destination(s).

(b) It can be emitted as a "remark" through the diagnostics subsystem,
    showing the extra metadata::

      foo.c:15:3: remark: symbolic number of iterations is ‘(unsigned int)
      n_9(D)’ [pass=vect] [count(guessed_local)=955630224]

    getting the standard diagnostics benefits:

    * announcing "In function 'blah':" etc when this changes.

    * printing the corresponding source lines (unless
      "-fno-diagnostics-show-caret"), and

    * colorizing the various parts of the message if stderr is at a tty.

    * possibly allowing for remarks to be used in DejaGnu tests to better
      associate testing of an optimization with the source line in
      question, rather than relying on a scan of the dumpfile (which is
      per-source file and thus rather "coarse-grained").

(c) It is emitted in JSON form through "-fsave-optimization-record":

      {
          "kind": "note",
          "count": {
              "quality": "guessed_local",
              "value": 9.5563e+08
          },
          "location": {
              "line": 15,
              "file": "foo.c",
              "column": 3
          },
          "pass": {
              "num": 166,
              "type": "gimple",
              "name": "vect",
              "optgroups": [
                  "loop",
                  "vec"
              ]
          },
          "message": [
              "symbolic number of iterations is ",
              {
                  "expr": "(unsigned int) n_8(D)"
              }
          ],
          "impl_location": {
              "line": 1674,
              "file": "../../src/gcc/tree-vect-loop.c",
              "function": "vect_analyze_loop_form"
          },
          "function": "compute_sum"
      },

    Note how this captures metadata such as profile count, function name,
    pass, etc.  (currently the JSON is emitted on one long line, with no
    pretty indentation, but it's trivial to run it through a tool to
    make it easy to read).

An example of a raw JSON output file can be seen here:
  https://dmalcolm.fedorapeople.org/gcc/2018-05-29/presentation-inlined.c.opt-record.json
and a prettified version of the same (via indentation) can be seen at:
  https://dmalcolm.fedorapeople.org/gcc/2018-05-29/pretty.json


Example 2
*********

Consider this "-fopt-info" code (from "vect_create_data_ref_ptr"):

  if (dump_enabled_p ())
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       dump_printf_loc (MSG_NOTE, vect_location,
                        "create %s-pointer variable to type: ",
                        get_tree_code_name (TREE_CODE (aggr_type)));
       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a record based array ref: ");
       else
         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
       dump_printf (MSG_NOTE, "\n");
     }

where the information is built up piecewise, with conditional logic.

This purely emits text to any destination(s) of "-fopt-info", e.g.:

    foo.c:12:5: note: create vector_type-pointer variable to type:
    vector(4) int  vectorizing a pointer ref: *arr_9(D)

e.g. to stderr, and/or the dumpfile for the current pass.

This can be rewritten by exposing the "pending_optinfo":

  if (optinfo_enabled_p ())
    {
      tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
      pending_optinfo info = OPTINFO_VECT_NOTE
       << optinfo_printf ("create %s-pointer variable to type: ",
                          get_tree_code_name (TREE_CODE (aggr_type)))
       << slim (aggr_type);
      if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
       info << "  vectorizing an array ref: ";
      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
       info << "  vectorizing a vector ref: ";
      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
       info << "  vectorizing a record based array ref: ";
      else
       info << "  vectorizing a pointer ref: ";
      info << slim (DR_BASE_OBJECT (dr));
    }

The "pending_optinfo" is emitted when it goes out of scope.  As before, it
can be emitted in up to three ways:

(a) as before via "-fopt-info":

    foo.c:12:5: note: create vector_type-pointer variable to type:
    vector(4) int  vectorizing a pointer ref: *arr_9(D)

  (albeit with capitalization changes), to the "-fopt-info" destination(s).

(b) It can be emitted as a "remark" through the diagnostics subsystem:

    foo.c:15:3: remark:   create vector_type-pointer variable to type:
    ‘vector(4) int’  vectorizing a pointer ref: ‘*arr_9(D)’ [pass=vect]
    [count(guessed_local)=860067200]

(c) It is emitted in JSON form through "-fsave-optimization-record":

      {
          "kind": "note",
          "count": {
              "quality": "guessed_local",
              "value": 8.60067e+08
          },
          "location": {
              "line": 15,
              "file": "foo.c",
              "column": 3
          },
          "pass": {
              "num": 166,
              "type": "gimple",
              "name": "vect",
              "optgroups": [
                  "loop",
                  "vec"
              ]
          },
          "message": [
              "create vector_type-pointer variable to type: ",
              {
                  "expr": "vector(4) int"
              },
              "  vectorizing a pointer ref: ",
              {
                  "location": {
                      "line": 16,
                      "file": "foo.c",
                      "column": 5
                  },
                  "expr": "*arr_9(D)"
              }
          ],
          "impl_location": {
              "line": 4702,
              "file": "../../src/gcc/tree-vect-data-refs.c",
              "function": "vect_create_data_ref_ptr"
          },
          "function": "compute_sum"
      },

Note how the message isn't just text: here it has captured the
source location of the pointer ref.  I've not been a fan of
the "<<" C++ idiom, but it seems to work well here for capturing
the data we need in a relative terse internal API.
(but do we need to internationalize the messages?  We're not
doing so at the moment)

As well as capturing text and numbers, there's support for capturing
trees, gimple statements, and symbol table nodes (in each case
capturing their source location, but also the metadata that the
text is talking about these things, allowing for richer views
in the presentation).  I'm thinking of adding support for
basic blocks and for loops.


Viewing the optimization records
********************************

I've written an HTML generator (in Python 3)
 https://github.com/davidmalcolm/gcc-opt-viewer
which takes a directory full of these json files and emits a directory
of HTML files, with an index.html showing the optimizations, prioritized
from hottest code downwards.

An example of HTML output can be seen here:

* https://dmalcolm.fedorapeople.org/gcc/2018-05-18/pgo-demo-test/pgo-demo-test/

based on this source:

* https://github.com/davidmalcolm/pgo-demo


Clicking on the links in the "Source Location column shows the
optimizations inline next to the source code they affect.

It uses a little JavaScript to add expand/collapse toggles when the amount
of text for an optimization gets too long.  You can see the hierarchical
structure of the notes via indentation (e.g. for the vectorization example).
An alternative presentation could add an explicit tree structure with
more expand/collapse toggles - this is just a prototype.

I've ported many messages from the existing API to the "optinfo" API, but
I deliberately didn't make significant changes to messages.  But there's
probably scope for improvements here.  For example, in the
"presentation-not-inlined.c" example at the link above, the vect pass emits:

   === analyzing loop ===
    === analyze_loop_nest ===
      === vect_analyze_loop_form ===
        === get_loop_niters ===
      symbolic number of iterations is (unsigned int) n_9(D)
      not vectorized: loop contains function calls or data references that cannot be analyzed

which is useful, but more useful would be to know *which* function calls
or data references couldn't be analyzed.  So that would be something
to tackle in followups (this post is more about discussing the overall
direction).

Clicking on the "Pass" links shows the GCC source that emitted the
message - or, at least, a close approximation to it on the trunk
(on the github mirror, to reduce traffic on our git server).


Caveats
=======

This is still just a rough prototype (I'm looking for feedback, especially
from loop, vectorization, and IPA maintainers).

Some known issues:

* I haven't yet attempted to bootstrap this

* there are lots of FIXMEs

* the file format ought to be versioned

* "remarks" are a bit half-baked in this patch kit, it's just on/off.
  It probably should support the optimization group tags from
  -fopt-info.  LLVM has a -f[no-]diagnostics-show-hotness; etc.

* I'm using a little C++11 (std::shared_ptr), which would need to be
  ported to C++98.

* the patch kit is relative to r259650, from 2018-04-25


Known unknowns
==============

* Should the "optinfo_enabled_p" use an "UNLIKELY" macro to
  tell a non-PGO build of gcc that all of this stuff is off by
  default and only used by a small subset of users?  We could
  do this for dump_enabled_p for that matter.

* Should the viewer generate static HTML, or serve dynamic HTML?
  When I've tried this on non-trivial examples, the index.html
  becomes unwieldy, so pagination would help a lot.  That said,
  there could be multiple different viewers, a benefit of separating
  the data out from its presentation.

* Should there be a way to pre-filter the optimization records before
  they're written out to JSON?  Or is it OK to dump everything, and
  make filtering be the viewer's responsibility?  (currently the patch
  kit dumps everything)

* optimization records can be emitted before the profile counts have
  been loaded/setup.  An example is the "einline" reports.  Maybe
  there's a way to patch the records with profile information???
  (which would mean storing them in memory before flushing them)

* Should the JSON format interact with the other uses of dumpfiles?
  (both free-form messages, and capturing the IR)  This would allow
  a viewer e.g. to follow a specific function, showing each state of
  it as it goes through the optimizers, rather than the current "show
  me every function at time T" view that we get from browsing dumpfiles.
  This could allow neat things like "show me every pass in which function
  FOO changed" etc.
  I've been experimenting with this, but it's not in the patch kit as
  is.

* Should the output format capture the passes and their hierarchy?
  (passes.def, plus any from plugins).  I'm thinking "yes", with the
  optimization records referencing back to those passes, rather than
  repeating the pass metadata in each optimization record.

* Formatting convention mismatches:

  * diagnostics have lowercase start, and no trailing period

  * -fopt-info dumps have uppercase start, and often have a trailing period

    * but some of them have multiple sentences

* Is there a natural tension here between logs (which are timed-ordered
  events) vs source markup (source-ordered)?  Consider e.g. a
  collection of related messages from one subroutine.

* Should optimization info be internationalized/localized? Currently
  "-fopt-info" isn't.  I say let's *not* i18n it.  This is straddling
  the border between implementation detail and advanced-user-facing.

* My prototype respects TDF_DETAILS for the existing "-fopt-info" output,
  but given that it appears to have been applied haphazardly, and that we
  can filter in other ways, my prototype ignores it for the other output
  formats.  It could be added.

* Which JSON library to use for output?  Note that we only need output, so
  that for the final version we can directly write the JSON, rather than the
  current approach which, inefficiently, requires first building a DOM-like
  tree in memory.  It would be fairly simple to write JSON directly to
  a pretty_printer *.

Thanks for reading this far.

Thoughts?
Dave


David Malcolm (10):
  Convert dump and optgroup flags to enums
  Add JSON implementation
  Add optinfo, remarks and optimization records
  Use indentation to show nesting for -fopt-info
  Experiment with using optinfo for vectorization
  Experiments with using optinfo for inlining
  Experiment with using optinfo for loop-handling
  Experiment with using optinfo for devirtualization
  Experiment with using optinfo in gimple-loop-interchange.cc
  Experiment with optinfo in tree-ssa-loop-im.c

 gcc/Makefile.in                              |    8 +
 gcc/c-family/c-pretty-print.c                |    2 +-
 gcc/cfg.c                                    |    4 +-
 gcc/cfghooks.c                               |    2 +-
 gcc/common.opt                               |    9 +
 gcc/coretypes.h                              |    8 +
 gcc/diagnostic-color.c                       |    2 +
 gcc/diagnostic-core.h                        |    2 +
 gcc/diagnostic.c                             |   17 +
 gcc/diagnostic.def                           |    1 +
 gcc/doc/invoke.texi                          |   34 +-
 gcc/dumpfile.c                               |   72 +-
 gcc/dumpfile.h                               |  230 +++-
 gcc/early-remat.c                            |    2 +-
 gcc/fortran/gfc-diagnostic.def               |    1 +
 gcc/gengtype.c                               |    3 +-
 gcc/gimple-fold.c                            |   29 +-
 gcc/gimple-loop-interchange.cc               |   36 +-
 gcc/gimple-pretty-print.c                    |    2 +-
 gcc/gimple-ssa-store-merging.c               |    6 +-
 gcc/gimple-ssa-strength-reduction.c          |    2 +-
 gcc/gimple.c                                 |    2 +-
 gcc/ipa-devirt.c                             |   14 +-
 gcc/ipa-inline.c                             |   25 +
 gcc/ipa.c                                    |   17 +-
 gcc/json.cc                                  | 1914 ++++++++++++++++++++++++++
 gcc/json.h                                   |  214 +++
 gcc/opt-functions.awk                        |    1 +
 gcc/optinfo-emit-diagnostics.cc              |  141 ++
 gcc/optinfo-emit-diagnostics.h               |   26 +
 gcc/optinfo-emit-fopt-info.cc                |  106 ++
 gcc/optinfo-emit-fopt-info.h                 |   26 +
 gcc/optinfo-emit-json.cc                     |  408 ++++++
 gcc/optinfo-emit-json.h                      |   37 +
 gcc/optinfo-internal.h                       |  139 ++
 gcc/optinfo.cc                               |  272 ++++
 gcc/optinfo.h                                |  389 ++++++
 gcc/opts.c                                   |    4 +
 gcc/opts.h                                   |   13 +-
 gcc/passes.c                                 |    2 +-
 gcc/print-tree.c                             |    7 +-
 gcc/profile-count.c                          |   28 +
 gcc/profile-count.h                          |    5 +
 gcc/selftest-run-tests.c                     |    1 +
 gcc/selftest.h                               |    1 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |    2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |   32 +
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c |  158 +++
 gcc/testsuite/lib/gcc-dg.exp                 |    9 +
 gcc/toplev.c                                 |    5 +
 gcc/tree-inline.c                            |   10 +
 gcc/tree-pass.h                              |    2 +-
 gcc/tree-pretty-print.c                      |    2 +-
 gcc/tree-ssa-live.c                          |    4 +-
 gcc/tree-ssa-loop-im.c                       |   13 +
 gcc/tree-ssa-loop-ivcanon.c                  |   38 +-
 gcc/tree-ssa-loop-niter.c                    |    7 +-
 gcc/tree-ssa-math-opts.c                     |    4 +-
 gcc/tree-ssa-reassoc.c                       |    2 +-
 gcc/tree-ssa-sccvn.c                         |    2 +-
 gcc/tree-vect-data-refs.c                    |  979 ++++++-------
 gcc/tree-vect-loop-manip.c                   |   98 +-
 gcc/tree-vect-loop.c                         | 1390 +++++++++----------
 gcc/tree-vect-patterns.c                     |  100 +-
 gcc/tree-vect-slp.c                          |  507 ++++---
 gcc/tree-vect-stmts.c                        |  617 ++++-----
 gcc/tree-vectorizer.c                        |   42 +-
 gcc/tree-vectorizer.h                        |   37 +
 68 files changed, 6193 insertions(+), 2131 deletions(-)
 create mode 100644 gcc/json.cc
 create mode 100644 gcc/json.h
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-fopt-info.cc
 create mode 100644 gcc/optinfo-emit-fopt-info.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (6 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 07/10] Experiment with using optinfo for loop-handling David Malcolm
@ 2018-05-29 20:32 ` David Malcolm
  2018-06-01  9:51   ` Richard Biener
  2018-05-29 20:32 ` [PATCH 03/10] Add optinfo, remarks and optimization records David Malcolm
  2018-05-29 20:35 ` [PATCH 01/10] Convert dump and optgroup flags to enums David Malcolm
  9 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This was an experiment to try to capture information on a
loop optimization.

gcc/ChangeLog:
	* gimple-loop-interchange.cc (should_interchange_loops): Add
	optinfo note when interchange gives better data locality behavior.
	(tree_loop_interchange::interchange): Add OPTINFO_SCOPE.
	Add optinfo for successful and unsuccessful interchanges.
	(prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
	optinfo note.
	(pass_linterchange::execute): Add OPTINFO_SCOPE.
---
 gcc/gimple-loop-interchange.cc | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index eb35263..cd32288 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned i_idx, unsigned o_idx,
   ratio = innermost_loops_p ? INNER_STRIDE_RATIO : OUTER_STRIDE_RATIO;
   /* Do interchange if it gives better data locality behavior.  */
   if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides, ratio)))
-    return true;
+    {
+      if (optinfo_enabled_p ())
+	OPTINFO_NOTE ((gimple *)NULL) // FIXME
+	  << "interchange gives better data locality behavior: "
+	  << "iloop_strides: "
+	  << decu (iloop_strides)
+	  << " > (oloop_strides: "
+	  << decu (oloop_strides)
+	  << " * ratio: "
+	  << decu (ratio)
+	  << ")";
+      return true;
+    }
   if (wi::gtu_p (iloop_strides, oloop_strides))
     {
       /* Or it creates more invariant memory references.  */
@@ -1578,6 +1590,8 @@ bool
 tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    vec<ddr_p> ddrs)
 {
+  OPTINFO_SCOPE ("tree_loop_interchange::interchange", m_loop_nest[0]);
+
   location_t loc = find_loop_location (m_loop_nest[0]);
   bool changed_p = false;
   /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
@@ -1628,6 +1642,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 	    fprintf (dump_file,
 		     "Loop_pair<outer:%d, inner:%d> is interchanged\n\n",
 		     oloop.m_loop->num, iloop.m_loop->num);
+	  if (optinfo_enabled_p ())
+	    OPTINFO_SUCCESS (oloop.m_loop)
+	      << optinfo_printf ("Loop_pair<outer:%d, inner:%d> is interchanged",
+				 oloop.m_loop->num, iloop.m_loop->num);
 
 	  changed_p = true;
 	  interchange_loops (iloop, oloop);
@@ -1641,6 +1659,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 	    fprintf (dump_file,
 		     "Loop_pair<outer:%d, inner:%d> is not interchanged\n\n",
 		     oloop.m_loop->num, iloop.m_loop->num);
+	  if (optinfo_enabled_p ())
+	    OPTINFO_FAILURE (oloop.m_loop)
+	      << optinfo_printf ("Loop_pair<outer:%d, inner:%d> is not interchanged",
+				 oloop.m_loop->num, iloop.m_loop->num);
 	}
     }
   simple_dce_from_worklist (m_dce_seeds);
@@ -1648,6 +1670,9 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
   if (changed_p)
     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
 		     "loops interchanged in loop nest\n");
+  if (optinfo_enabled_p ())
+    OPTINFO_SUCCESS (m_loop_nest[0])
+      << "loops interchanged in loop nest";
 
   return changed_p;
 }
@@ -1971,6 +1996,8 @@ static bool
 prepare_perfect_loop_nest (struct loop *loop, vec<loop_p> *loop_nest,
 			   vec<data_reference_p> *datarefs, vec<ddr_p> *ddrs)
 {
+  OPTINFO_SCOPE ("prepare_perfect_loop_nest", loop);
+
   struct loop *start_loop = NULL, *innermost = loop;
   struct loop *outermost = loops_for_fn (cfun)->tree_root;
 
@@ -2029,6 +2056,12 @@ prepare_perfect_loop_nest (struct loop *loop, vec<loop_p> *loop_nest,
 	  fprintf (dump_file,
 		   "\nConsider loop interchange for loop_nest<%d - %d>\n",
 		   start_loop->num, innermost->num);
+	if (optinfo_enabled_p ())
+	  {
+	    OPTINFO_NOTE (start_loop)
+	      << optinfo_printf ("consider loop interchange for loop_nest<%d - %d>",
+				 start_loop->num, innermost->num);
+	  }
 
 	if (loop != start_loop)
 	  prune_access_strides_not_in_loop (start_loop, innermost, *datarefs);
@@ -2061,6 +2094,7 @@ pass_linterchange::execute (function *fun)
   struct loop *loop;
   FOR_EACH_LOOP (loop, LI_ONLY_INNERMOST)
     {
+      OPTINFO_SCOPE ("considering loop for interchange", loop);
       vec<loop_p> loop_nest = vNULL;
       vec<data_reference_p> datarefs = vNULL;
       vec<ddr_p> ddrs = vNULL;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 01/10] Convert dump and optgroup flags to enums
  2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
                   ` (8 preceding siblings ...)
  2018-05-29 20:32 ` [PATCH 03/10] Add optinfo, remarks and optimization records David Malcolm
@ 2018-05-29 20:35 ` David Malcolm
  2018-06-01 10:00   ` Richard Biener
  9 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-05-29 20:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

The dump machinery uses "int" in a few places, for two different
sets of bitmasks.

This patch makes things more self-documenting and type-safe by using
a new pair of enums: one for the dump_flags_t and another for the
optgroup_flags.

This requires adding some overloaded bit operations to the enums
in question, which, in this patch is done for each enum .  If the basic
idea is OK, should I add a template for this?  (with some kind of
magic to express that bitmasking operations are only supported on
certain opt-in enums).

gcc/c-family/ChangeLog:
	* c-pretty-print.c (c_pretty_printer::statement): Use TDF_NONE
	rather than 0.

gcc/ChangeLog:
	* cfg.c (debug): Use TDF_NONE rather than 0.
	* cfghooks.c (debug): Likewise.
	* dumpfile.c (DUMP_FILE_INFO): Likewise; also for OPTGROUP.
	(struct dump_option_value_info): Convert to...
	(struct kv_pair): ...this template type.
	(dump_options): Convert to kv_pair<dump_flags_t>; use TDF_NONE
	rather than 0.
	(optinfo_verbosity_options): Likewise.
	(optgroup_options): Convert to kv_pair<optgroup_flags_t>; use
	OPTGROUP_NONE.
	(gcc::dump_manager::dump_register): Use optgroup_flags_t rather
	than int for "optgroup_flags" param.
	(dump_generic_expr_loc): Use dump_flags_t rather than int for
	"dump_kind" param.
	(dump_finish): Use TDF_NONE rather than 0.
	(gcc::dump_manager::opt_info_enable_passes): Use optgroup_flags_t
	rather than int for "optgroup_flags" param.  Use TDF_NONE rather
	than 0.  Update for change to option_ptr.
	(opt_info_switch_p_1): Convert "optgroup_flags" param from int *
	to optgroup_flags_t *.  Use TDF_NONE and OPTGROUP_NONE rather than
	0.  Update for changes to optinfo_verbosity_options and
	optgroup_options.
	(opt_info_switch_p): Convert optgroup_flags from int to
	optgroup_flags_t.
	* dumpfile.h (TDF_ADDRESS, TDF_SLIM, TDF_RAW, TDF_DETAILS,
	TDF_STATS, TDF_BLOCKS, TDF_VOPS, TDF_LINENO, TDF_UID)
	TDF_STMTADDR, TDF_GRAPH, TDF_MEMSYMS, TDF_RHS_ONLY, TDF_ASMNAME,
	TDF_EH, TDF_NOUID, TDF_ALIAS, TDF_ENUMERATE_LOCALS, TDF_CSELIB,
	TDF_SCEV, TDF_GIMPLE, TDF_FOLDING, MSG_OPTIMIZED_LOCATIONS,
	MSG_MISSED_OPTIMIZATION, MSG_NOTE, MSG_ALL, TDF_COMPARE_DEBUG,
	TDF_NONE): Convert from macros to...
	(enum dump_flag): ...this new enum.
	(dump_flags_t): Update to use enum.
	(operator|, operator&, operator~, operator|=, operator&=):
	Implement for dump_flags_t.
	(OPTGROUP_NONE, OPTGROUP_IPA, OPTGROUP_LOOP, OPTGROUP_INLINE,
	OPTGROUP_OMP, OPTGROUP_VEC, OPTGROUP_OTHER, OPTGROUP_ALL):
	Convert from macros to...
	(enum optgroup_flag): ...this new enum.
	(optgroup_flags_t): New typedef.
	(operator|, operator|=): Implement for optgroup_flags_t.
	(struct dump_file_info): Convert field "alt_flags" to
	dump_flags_t.  Convert field "optgroup_flags" to
	optgroup_flags_t.
	(dump_register): Convert param "optgroup_flags" to
	optgroup_flags_t.
	(opt_info_enable_passes): Likewise.
	* early-remat.c (early_remat::dump_edge_list): Use TDF_NONE rather
	than 0.
	* gimple-pretty-print.c (debug): Likewise.
	* gimple-ssa-store-merging.c (bswap_replace): Likewise.
	(merged_store_group::apply_stores): Likewise.
	* gimple-ssa-strength-reduction.c (insert_initializers): Likewise.
	* gimple.c (verify_gimple_pp): Likewise.
	* passes.c (pass_manager::register_one_dump_file): Convert
	local "optgroup_flags" to optgroup_flags_t.
	* print-tree.c (print_node): Use TDF_NONE rather than 0.
	(debug): Likewise.
	(debug_body): Likewise.
	* tree-pass.h (struct pass_data): Convert field "optgroup_flags"
	to optgroup_flags_t.
	* tree-pretty-print.c (print_struct_decl): Use TDF_NONE rather
	than 0.
	* tree-ssa-math-opts.c (convert_mult_to_fma_1): Likewise.
	(convert_mult_to_fma): Likewise.
	* tree-ssa-reassoc.c (undistribute_ops_list): Likewise.
	* tree-ssa-sccvn.c (vn_eliminate): Likewise.
	* tree-vect-data-refs.c (dump_lower_bound): Convert param
	"dump_kind" to dump_flags_t.
---
 gcc/c-family/c-pretty-print.c       |   2 +-
 gcc/cfg.c                           |   4 +-
 gcc/cfghooks.c                      |   2 +-
 gcc/dumpfile.c                      |  56 ++++-----
 gcc/dumpfile.h                      | 227 +++++++++++++++++++++++++++---------
 gcc/early-remat.c                   |   2 +-
 gcc/gimple-pretty-print.c           |   2 +-
 gcc/gimple-ssa-store-merging.c      |   6 +-
 gcc/gimple-ssa-strength-reduction.c |   2 +-
 gcc/gimple.c                        |   2 +-
 gcc/passes.c                        |   2 +-
 gcc/print-tree.c                    |   7 +-
 gcc/tree-pass.h                     |   2 +-
 gcc/tree-pretty-print.c             |   2 +-
 gcc/tree-ssa-math-opts.c            |   4 +-
 gcc/tree-ssa-reassoc.c              |   2 +-
 gcc/tree-ssa-sccvn.c                |   2 +-
 gcc/tree-vect-data-refs.c           |   2 +-
 18 files changed, 222 insertions(+), 106 deletions(-)

diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index dc76c99..efb41c5 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -2341,7 +2341,7 @@ c_pretty_printer::statement (tree stmt)
   if (pp_needs_newline (this))
     pp_newline_and_indent (this, 0);
 
-  dump_generic_node (this, stmt, pp_indentation (this), 0, true);
+  dump_generic_node (this, stmt, pp_indentation (this), TDF_NONE, true);
 }
 
 \f
diff --git a/gcc/cfg.c b/gcc/cfg.c
index 11026e7..6d55516 100644
--- a/gcc/cfg.c
+++ b/gcc/cfg.c
@@ -545,8 +545,8 @@ DEBUG_FUNCTION void
 debug (edge_def &ref)
 {
   /* FIXME (crowl): Is this desireable?  */
-  dump_edge_info (stderr, &ref, 0, false);
-  dump_edge_info (stderr, &ref, 0, true);
+  dump_edge_info (stderr, &ref, TDF_NONE, false);
+  dump_edge_info (stderr, &ref, TDF_NONE, true);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/cfghooks.c b/gcc/cfghooks.c
index 87d864c..ea106e0 100644
--- a/gcc/cfghooks.c
+++ b/gcc/cfghooks.c
@@ -288,7 +288,7 @@ dump_bb (FILE *outf, basic_block bb, int indent, dump_flags_t flags)
 DEBUG_FUNCTION void
 debug (basic_block_def &ref)
 {
-  dump_bb (stderr, &ref, 0, 0);
+  dump_bb (stderr, &ref, 0, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 0acc7a9..6af1445 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -50,8 +50,8 @@ const char *dump_file_name;
 dump_flags_t dump_flags;
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
-  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, 0, 0, 0, 0, 0, num, \
-   false, false}
+  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
+   OPTGROUP_NONE, 0, 0, num, false, false}
 
 /* Table of tree dump switches. This must be consistent with the
    TREE_DUMP_INDEX enumeration in dumpfile.h.  */
@@ -74,15 +74,16 @@ static struct dump_file_info dump_files[TDI_end] =
 };
 
 /* Define a name->number mapping for a dump flag value.  */
-struct dump_option_value_info
+template <typename ValueType>
+struct kv_pair
 {
   const char *const name;	/* the name of the value */
-  const dump_flags_t value;	/* the value of the name */
+  const ValueType value;	/* the value of the name */
 };
 
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
-static const struct dump_option_value_info dump_options[] =
+static const kv_pair<dump_flags_t> dump_options[] =
 {
   {"address", TDF_ADDRESS},
   {"asmname", TDF_ASMNAME},
@@ -114,23 +115,23 @@ static const struct dump_option_value_info dump_options[] =
   {"all", dump_flags_t (~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_GRAPH
 			| TDF_STMTADDR | TDF_RHS_ONLY | TDF_NOUID
 			| TDF_ENUMERATE_LOCALS | TDF_SCEV | TDF_GIMPLE))},
-  {NULL, 0}
+  {NULL, TDF_NONE}
 };
 
 /* A subset of the dump_options table which is used for -fopt-info
    types. This must be consistent with the MSG_* flags in dumpfile.h.
  */
-static const struct dump_option_value_info optinfo_verbosity_options[] =
+static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 {
   {"optimized", MSG_OPTIMIZED_LOCATIONS},
   {"missed", MSG_MISSED_OPTIMIZATION},
   {"note", MSG_NOTE},
   {"all", MSG_ALL},
-  {NULL, 0}
+  {NULL, TDF_NONE}
 };
 
 /* Flags used for -fopt-info groups.  */
-static const struct dump_option_value_info optgroup_options[] =
+static const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -138,7 +139,7 @@ static const struct dump_option_value_info optgroup_options[] =
   {"omp", OPTGROUP_OMP},
   {"vec", OPTGROUP_VEC},
   {"optall", OPTGROUP_ALL},
-  {NULL, 0}
+  {NULL, OPTGROUP_NONE}
 };
 
 gcc::dump_manager::dump_manager ():
@@ -173,7 +174,8 @@ gcc::dump_manager::~dump_manager ()
 unsigned int
 gcc::dump_manager::
 dump_register (const char *suffix, const char *swtch, const char *glob,
-	       dump_kind dkind, int optgroup_flags, bool take_ownership)
+	       dump_kind dkind, optgroup_flags_t optgroup_flags,
+	       bool take_ownership)
 {
   int num = m_next_dump++;
 
@@ -410,7 +412,7 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
    location.  */
 
 void
-dump_generic_expr_loc (int dump_kind, source_location loc,
+dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
 		       dump_flags_t extra_dump_flags, tree t)
 {
   if (dump_file && (dump_kind & pflags))
@@ -575,9 +577,9 @@ dump_finish (int phase)
   dfi->pstream = NULL;
   dump_file = NULL;
   alt_dump_file = NULL;
-  dump_flags = TDI_none;
-  alt_flags = 0;
-  pflags = 0;
+  dump_flags = TDF_NONE;
+  alt_flags = TDF_NONE;
+  pflags = TDF_NONE;
 }
 
 /* Begin a tree dump for PHASE. Stores any user supplied flag in
@@ -750,7 +752,7 @@ dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename)
 
 int
 gcc::dump_manager::
-opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
+opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
 			const char *filename)
 {
   int n = 0;
@@ -817,11 +819,11 @@ dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob)
     return 0;
 
   ptr = option_value;
-  flags = 0;
+  flags = TDF_NONE;
 
   while (*ptr)
     {
-      const struct dump_option_value_info *option_ptr;
+      const struct kv_pair<dump_flags_t> *option_ptr;
       const char *end_ptr;
       const char *eq_ptr;
       unsigned length;
@@ -903,8 +905,8 @@ dump_switch_p (const char *arg)
    and filename.  Return non-zero if it is a recognized switch.  */
 
 static int
-opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
-                     char **filename)
+opt_info_switch_p_1 (const char *arg, dump_flags_t *flags,
+		     optgroup_flags_t *optgroup_flags, char **filename)
 {
   const char *option_value;
   const char *ptr;
@@ -913,15 +915,14 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
   ptr = option_value;
 
   *filename = NULL;
-  *flags = 0;
-  *optgroup_flags = 0;
+  *flags = TDF_NONE;
+  *optgroup_flags = OPTGROUP_NONE;
 
   if (!ptr)
     return 1;       /* Handle '-fopt-info' without any additional options.  */
 
   while (*ptr)
     {
-      const struct dump_option_value_info *option_ptr;
       const char *end_ptr;
       const char *eq_ptr;
       unsigned length;
@@ -938,8 +939,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
 	end_ptr = ptr + strlen (ptr);
       length = end_ptr - ptr;
 
-      for (option_ptr = optinfo_verbosity_options; option_ptr->name;
-           option_ptr++)
+      for (const kv_pair<dump_flags_t> *option_ptr = optinfo_verbosity_options;
+	   option_ptr->name; option_ptr++)
 	if (strlen (option_ptr->name) == length
 	    && !memcmp (option_ptr->name, ptr, length))
           {
@@ -947,7 +948,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
 	    goto found;
           }
 
-      for (option_ptr = optgroup_options; option_ptr->name; option_ptr++)
+      for (const kv_pair<optgroup_flags_t> *option_ptr = optgroup_options;
+	   option_ptr->name; option_ptr++)
 	if (strlen (option_ptr->name) == length
 	    && !memcmp (option_ptr->name, ptr, length))
           {
@@ -982,7 +984,7 @@ int
 opt_info_switch_p (const char *arg)
 {
   dump_flags_t flags;
-  int optgroup_flags;
+  optgroup_flags_t optgroup_flags;
   char *filename;
   static char *file_seen = NULL;
   gcc::dump_manager *dumps = g->get_dumps ();
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 21803a6..b5582f7 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -58,65 +58,177 @@ enum dump_kind
    the DUMP_OPTIONS array in dumpfile.c. The TDF_* flags coexist with
    MSG_* flags (for -fopt-info) and the bit values must be chosen to
    allow that.  */
-#define TDF_ADDRESS	(1 << 0)	/* dump node addresses */
-#define TDF_SLIM	(1 << 1)	/* don't go wild following links */
-#define TDF_RAW		(1 << 2)	/* don't unparse the function */
-#define TDF_DETAILS	(1 << 3)	/* show more detailed info about
-					   each pass */
-#define TDF_STATS	(1 << 4)	/* dump various statistics about
-					   each pass */
-#define TDF_BLOCKS	(1 << 5)	/* display basic block boundaries */
-#define TDF_VOPS	(1 << 6)	/* display virtual operands */
-#define TDF_LINENO	(1 << 7)	/* display statement line numbers */
-#define TDF_UID		(1 << 8)	/* display decl UIDs */
-
-#define TDF_STMTADDR	(1 << 9)       /* Address of stmt.  */
-
-#define TDF_GRAPH	(1 << 10)	/* a graph dump is being emitted */
-#define TDF_MEMSYMS	(1 << 11)	/* display memory symbols in expr.
-					   Implies TDF_VOPS.  */
-
-#define TDF_RHS_ONLY	(1 << 12)	/* a flag to only print the RHS of
-					   a gimple stmt.  */
-#define TDF_ASMNAME	(1 << 13)	/* display asm names of decls  */
-#define TDF_EH		(1 << 14)	/* display EH region number
-					   holding this gimple statement.  */
-#define TDF_NOUID	(1 << 15)	/* omit UIDs from dumps.  */
-#define TDF_ALIAS	(1 << 16)	/* display alias information  */
-#define TDF_ENUMERATE_LOCALS (1 << 17)	/* Enumerate locals by uid.  */
-#define TDF_CSELIB	(1 << 18)	/* Dump cselib details.  */
-#define TDF_SCEV	(1 << 19)	/* Dump SCEV details.  */
-#define TDF_GIMPLE	(1 << 20)	/* Dump in GIMPLE FE syntax  */
-#define TDF_FOLDING	(1 << 21)	/* Dump folding details.  */
-#define MSG_OPTIMIZED_LOCATIONS	 (1 << 22)  /* -fopt-info optimized sources */
-#define MSG_MISSED_OPTIMIZATION	 (1 << 23)  /* missed opportunities */
-#define MSG_NOTE		 (1 << 24)  /* general optimization info */
-#define MSG_ALL		(MSG_OPTIMIZED_LOCATIONS | MSG_MISSED_OPTIMIZATION \
-			 | MSG_NOTE)
-#define TDF_COMPARE_DEBUG (1 << 25)	/* Dumping for -fcompare-debug.  */
-
-
-/* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
-
-#define TDF_NONE 0
+enum dump_flag
+{
+  /* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
+  TDF_NONE  = 0,
+
+  /* Dump node addresses.  */
+  TDF_ADDRESS = (1 << 0),
+
+  /* Don't go wild following links.  */
+  TDF_SLIM = (1 << 1),
+
+  /* Don't unparse the function.  */
+  TDF_RAW = (1 << 2),
+
+  /* Show more detailed info about each pass.  */
+  TDF_DETAILS = (1 << 3),
+
+  /* Dump various statistics about each pass.  */
+  TDF_STATS = (1 << 4),
+
+  /* Display basic block boundaries.  */
+  TDF_BLOCKS = (1 << 5),
+
+  /* Display virtual operands.  */
+  TDF_VOPS = (1 << 6),
+
+  /* Display statement line numbers.  */
+  TDF_LINENO = (1 << 7),
+
+  /* Display decl UIDs.  */
+  TDF_UID  = (1 << 8),
+
+  /* Address of stmt.  */
+  TDF_STMTADDR = (1 << 9),
+
+  /* A graph dump is being emitted.  */
+  TDF_GRAPH = (1 << 10),
+
+  /* Display memory symbols in expr.
+     Implies TDF_VOPS.  */
+  TDF_MEMSYMS = (1 << 11),
+
+  /* A flag to only print the RHS of a gimple stmt.  */
+  TDF_RHS_ONLY = (1 << 12),
+
+  /* Display asm names of decls.  */
+  TDF_ASMNAME = (1 << 13),
+
+  /* Display EH region number holding this gimple statement.  */
+  TDF_EH  = (1 << 14),
+
+  /* Omit UIDs from dumps.  */
+  TDF_NOUID = (1 << 15),
+
+  /* Display alias information.  */
+  TDF_ALIAS = (1 << 16),
+
+  /* Enumerate locals by uid.  */
+  TDF_ENUMERATE_LOCALS = (1 << 17),
+
+  /* Dump cselib details.  */
+  TDF_CSELIB = (1 << 18),
+
+  /* Dump SCEV details.  */
+  TDF_SCEV = (1 << 19),
+
+  /* Dump in GIMPLE FE syntax  */
+  TDF_GIMPLE = (1 << 20),
+
+  /* Dump folding details.  */
+  TDF_FOLDING = (1 << 21),
+
+  /* -fopt-info optimized sources.  */
+  MSG_OPTIMIZED_LOCATIONS = (1 << 22),
+
+  /* Missed opportunities.  */
+  MSG_MISSED_OPTIMIZATION = (1 << 23),
+
+  /* General optimization info.  */
+  MSG_NOTE = (1 << 24),
+
+  MSG_ALL = (MSG_OPTIMIZED_LOCATIONS
+	     | MSG_MISSED_OPTIMIZATION
+	     | MSG_NOTE),
+
+  /* Dumping for -fcompare-debug.  */
+  TDF_COMPARE_DEBUG = (1 << 25),
+};
+
+/* Dump flags type.  */
+
+typedef enum dump_flag dump_flags_t;
+
+// FIXME: template
+// FIXME: underlying storage type?
+static inline dump_flags_t
+operator| (dump_flags_t lhs, dump_flags_t rhs)
+{
+  return (dump_flags_t)((int)lhs | (int)rhs);
+}
+
+static inline dump_flags_t
+operator& (dump_flags_t lhs, dump_flags_t rhs)
+{
+  return (dump_flags_t)((int)lhs & (int)rhs);
+}
+
+static inline dump_flags_t
+operator~ (dump_flags_t flags)
+{
+  return (dump_flags_t)~((int)flags);
+}
+
+static inline dump_flags_t &
+operator|= (dump_flags_t &lhs, dump_flags_t rhs)
+{
+  lhs = (dump_flags_t)((int)lhs | (int)rhs);
+  return lhs;
+}
+
+static inline dump_flags_t &
+operator&= (dump_flags_t &lhs, dump_flags_t rhs)
+{
+  lhs = (dump_flags_t)((int)lhs & (int)rhs);
+  return lhs;
+}
 
 /* Flags to control high-level -fopt-info dumps.  Usually these flags
    define a group of passes.  An optimization pass can be part of
    multiple groups.  */
-#define OPTGROUP_NONE	     (0)
-#define OPTGROUP_IPA	     (1 << 1)	/* IPA optimization passes */
-#define OPTGROUP_LOOP	     (1 << 2)	/* Loop optimization passes */
-#define OPTGROUP_INLINE	     (1 << 3)	/* Inlining passes */
-#define OPTGROUP_OMP	     (1 << 4)	/* OMP (Offloading and Multi
-					   Processing) transformations */
-#define OPTGROUP_VEC	     (1 << 5)	/* Vectorization passes */
-#define OPTGROUP_OTHER	     (1 << 6)	/* All other passes */
-#define OPTGROUP_ALL	     (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE \
-			      | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
 
-/* Dump flags type.  */
+enum optgroup_flag
+{
+  OPTGROUP_NONE = 0,
+
+  /* IPA optimization passes */
+  OPTGROUP_IPA  = (1 << 1),
+
+  /* Loop optimization passes */
+  OPTGROUP_LOOP = (1 << 2),
+
+  /* Inlining passes */
+  OPTGROUP_INLINE = (1 << 3),
 
-typedef uint64_t dump_flags_t;
+  /* OMP (Offloading and Multi Processing) transformations */
+  OPTGROUP_OMP = (1 << 4),
+
+  /* Vectorization passes */
+  OPTGROUP_VEC = (1 << 5),
+
+  /* All other passes */
+  OPTGROUP_OTHER = (1 << 6),
+
+  OPTGROUP_ALL = (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE
+		  | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
+};
+
+typedef enum optgroup_flag optgroup_flags_t;
+
+static inline optgroup_flags_t
+operator| (optgroup_flags_t lhs, optgroup_flags_t rhs)
+{
+  return (optgroup_flags_t)((int)lhs | (int)rhs);
+}
+
+static inline optgroup_flags_t &
+operator|= (optgroup_flags_t &lhs, optgroup_flags_t rhs)
+{
+  lhs = (optgroup_flags_t)((int)lhs | (int)rhs);
+  return lhs;
+}
 
 /* Define a tree dump switch.  */
 struct dump_file_info
@@ -140,9 +252,9 @@ struct dump_file_info
   /* Dump flags.  */
   dump_flags_t pflags;
   /* A pass flags for -fopt-info.  */
-  int alt_flags;
+  dump_flags_t alt_flags;
   /* Flags for -fopt-info given by a user.  */
-  int optgroup_flags;
+  optgroup_flags_t optgroup_flags;
   /* State of pass-specific stream.  */
   int pstate;
   /* State of the -fopt-info stream.  */
@@ -214,7 +326,8 @@ public:
      SUFFIX, SWTCH, and GLOB. */
   unsigned int
   dump_register (const char *suffix, const char *swtch, const char *glob,
-		 dump_kind dkind, int optgroup_flags, bool take_ownership);
+		 dump_kind dkind, optgroup_flags_t optgroup_flags,
+		 bool take_ownership);
 
   /* Allow languages and middle-end to register their dumps before the
      optimization passes.  */
@@ -275,7 +388,7 @@ private:
   dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename);
 
   int
-  opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
+  opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
 			  const char *filename);
 
 private:
diff --git a/gcc/early-remat.c b/gcc/early-remat.c
index 28eb9b4..776b2d0 100644
--- a/gcc/early-remat.c
+++ b/gcc/early-remat.c
@@ -657,7 +657,7 @@ early_remat::dump_edge_list (basic_block bb, bool do_succ)
   edge e;
   edge_iterator ei;
   FOR_EACH_EDGE (e, ei, do_succ ? bb->succs : bb->preds)
-    dump_edge_info (dump_file, e, 0, do_succ);
+    dump_edge_info (dump_file, e, TDF_NONE, do_succ);
 }
 
 /* Print information about basic block BB to the dump file.  */
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 6695526..39edaa1 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -153,7 +153,7 @@ print_gimple_stmt (FILE *file, gimple *g, int spc, dump_flags_t flags)
 DEBUG_FUNCTION void
 debug (gimple &ref)
 {
-  print_gimple_stmt (stderr, &ref, 0, 0);
+  print_gimple_stmt (stderr, &ref, 0, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/gimple-ssa-store-merging.c b/gcc/gimple-ssa-store-merging.c
index 6f6538b..7362128 100644
--- a/gcc/gimple-ssa-store-merging.c
+++ b/gcc/gimple-ssa-store-merging.c
@@ -1075,7 +1075,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
 	    print_gimple_stmt (dump_file, cur_stmt, 0);
 	  else
 	    {
-	      print_generic_expr (dump_file, tgt, 0);
+	      print_generic_expr (dump_file, tgt, TDF_NONE);
 	      fprintf (dump_file, "\n");
 	    }
 	}
@@ -1145,7 +1145,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
 	print_gimple_stmt (dump_file, cur_stmt, 0);
       else
 	{
-	  print_generic_expr (dump_file, tgt, 0);
+	  print_generic_expr (dump_file, tgt, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
     }
@@ -1956,7 +1956,7 @@ merged_store_group::apply_stores ()
 	  if (ret)
 	    {
 	      fprintf (dump_file, "After writing ");
-	      print_generic_expr (dump_file, cst, 0);
+	      print_generic_expr (dump_file, cst, TDF_NONE);
 	      fprintf (dump_file, " of size " HOST_WIDE_INT_PRINT_DEC
 		       " at position %d the merged region contains:\n",
 		       info->bitsize, pos_in_buffer);
diff --git a/gcc/gimple-ssa-strength-reduction.c b/gcc/gimple-ssa-strength-reduction.c
index 1c00f09..14fc420 100644
--- a/gcc/gimple-ssa-strength-reduction.c
+++ b/gcc/gimple-ssa-strength-reduction.c
@@ -3353,7 +3353,7 @@ insert_initializers (slsr_cand_t c)
 	      fputs ("Using existing initializer: ", dump_file);
 	      print_gimple_stmt (dump_file,
 				 SSA_NAME_DEF_STMT (incr_vec[i].initializer),
-				 0, 0);
+				 0, TDF_NONE);
 	    }
 	  continue;
 	}
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 9dc4911..c18526a 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -3148,7 +3148,7 @@ static void
 verify_gimple_pp (const char *expected, gimple *stmt)
 {
   pretty_printer pp;
-  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, 0 /* flags */);
+  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, TDF_NONE /* flags */);
   ASSERT_STREQ (expected, pp_formatted_text (&pp));
 }
 
diff --git a/gcc/passes.c b/gcc/passes.c
index ad0a912..62fd413 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -784,7 +784,7 @@ pass_manager::register_one_dump_file (opt_pass *pass)
   char num[11];
   dump_kind dkind;
   int id;
-  int optgroup_flags = OPTGROUP_NONE;
+  optgroup_flags_t optgroup_flags = OPTGROUP_NONE;
   gcc::dump_manager *dumps = m_ctxt->get_dumps ();
 
   /* See below in next_pass_1.  */
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index caf5f26..5c736c5 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -894,7 +894,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
 	  {
 	    pretty_printer buffer;
 	    buffer.buffer->stream = file;
-	    pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4, 0);
+	    pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4,
+			      TDF_NONE);
 	    pp_flush (&buffer);
 	  }
 
@@ -1039,7 +1040,7 @@ dump_tree_via_hooks (const tree_node *ptr, dump_flags_t options)
 DEBUG_FUNCTION void
 debug (const tree_node &ref)
 {
-  dump_tree_via_hooks (&ref, 0);
+  dump_tree_via_hooks (&ref, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
@@ -1070,7 +1071,7 @@ DEBUG_FUNCTION void
 debug_body (const tree_node &ref)
 {
   if (TREE_CODE (&ref) == FUNCTION_DECL)
-    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, 0);
+    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, TDF_NONE);
   else
     debug (ref);
 }
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 93a6a99..c757591 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -47,7 +47,7 @@ struct pass_data
   const char *name;
 
   /* The -fopt-info optimization group flags as defined in dumpfile.h. */
-  unsigned int optinfo_flags;
+  optgroup_flags_t optinfo_flags;
 
   /* The timevar id associated with this pass.  */
   /* ??? Ideally would be dynamically assigned.  */
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 276ad00..8963ae0 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -3404,7 +3404,7 @@ print_struct_decl (pretty_printer *pp, const_tree node, int spc,
 		|| TREE_CODE (node) == QUAL_UNION_TYPE))
 	pp_string (pp, "union ");
 
-      dump_generic_node (pp, TYPE_NAME (node), spc, 0, false);
+      dump_generic_node (pp, TYPE_NAME (node), spc, TDF_NONE, false);
     }
 
   /* Print the contents of the structure.  */
diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
index 16d9399..b3f8cb6 100644
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -2710,7 +2710,7 @@ convert_mult_to_fma_1 (tree mul_result, tree op1, tree op2)
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Generated FMA ");
-	  print_gimple_stmt (dump_file, fma_stmt, 0, 0);
+	  print_gimple_stmt (dump_file, fma_stmt, 0, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
 
@@ -3046,7 +3046,7 @@ convert_mult_to_fma (gimple *mul_stmt, tree op1, tree op2,
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Deferred generating FMA for multiplication ");
-	  print_gimple_stmt (dump_file, mul_stmt, 0, 0);
+	  print_gimple_stmt (dump_file, mul_stmt, 0, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
 
diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c
index 0e59bb5..dde9701 100644
--- a/gcc/tree-ssa-reassoc.c
+++ b/gcc/tree-ssa-reassoc.c
@@ -1606,7 +1606,7 @@ undistribute_ops_list (enum tree_code opcode,
     {
       fprintf (dump_file, "searching for un-distribute opportunities ");
       print_generic_expr (dump_file,
-	(*ops)[bitmap_first_set_bit (candidates)]->op, 0);
+	(*ops)[bitmap_first_set_bit (candidates)]->op, TDF_NONE);
       fprintf (dump_file, " %d\n", nr_candidates);
     }
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 1463c1d..d8ab1f0 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5921,7 +5921,7 @@ vn_eliminate (bitmap inserted_exprs)
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Removing dead stmt ");
-	  print_gimple_stmt (dump_file, stmt, 0, 0);
+	  print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
 	}
 
       gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 9aabcc1..ebc56c0 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -3229,7 +3229,7 @@ dependence_distance_ge_vf (data_dependence_relation *ddr,
 /* Dump LOWER_BOUND using flags DUMP_KIND.  Dumps are known to be enabled.  */
 
 static void
-dump_lower_bound (int dump_kind, const vec_lower_bound &lower_bound)
+dump_lower_bound (dump_flags_t dump_kind, const vec_lower_bound &lower_bound)
 {
   dump_printf (dump_kind, "%s (", lower_bound.unsigned_p ? "unsigned" : "abs");
   dump_generic_expr (dump_kind, TDF_SLIM, lower_bound.expr);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 02/10] Add JSON implementation
  2018-05-29 20:32 ` [PATCH 02/10] Add JSON implementation David Malcolm
@ 2018-05-30 17:31   ` Eric Gallager
  2018-05-30 17:32     ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Eric Gallager @ 2018-05-30 17:31 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 5/29/18, David Malcolm <dmalcolm@redhat.com> wrote:
> This patch is the JSON patch I posted last year;
> it adds support to gcc for reading and writing JSON,
> based on DOM-like trees of json::value instances.
>
> This is overkill for what's needed by the rest of the
> patch kit (which just needs to be able to write JSON),
> but this code already existed, so I'm using it for now.
>

I think I remember you posting this last year, but I forget where in
the archives it is. Could you post a link to the thread from last year
just for reference? Thanks.

> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add json.o.
> 	* json.cc: New file.
> 	* json.h: New file.
> 	* selftest-run-tests.c (selftest::run_tests): Call json_cc_tests.
> 	* selftest.h (selftest::json_cc_tests): New decl.
> ---
>  gcc/Makefile.in          |    1 +
>  gcc/json.cc              | 1914
> ++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/json.h               |  214 ++++++
>  gcc/selftest-run-tests.c |    1 +
>  gcc/selftest.h           |    1 +
>  5 files changed, 2131 insertions(+)
>  create mode 100644 gcc/json.cc
>  create mode 100644 gcc/json.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 20bee04..b3c7d5d 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1385,6 +1385,7 @@ OBJS = \
>  	ira-color.o \
>  	ira-emit.o \
>  	ira-lives.o \
> +	json.o \
>  	jump.o \
>  	langhooks.o \
>  	lcm.o \
> diff --git a/gcc/json.cc b/gcc/json.cc
> new file mode 100644
> index 0000000..e0d5a76
> --- /dev/null
> +++ b/gcc/json.cc
> @@ -0,0 +1,1914 @@
> +/* JSON parsing
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "json.h"
> +#include "pretty-print.h"
> +#include "math.h"
> +#include "selftest.h"
> +
> +using namespace json;
> +
> +/* class json::value.  */
> +
> +/* Generate a char * for this json::value tree.
> +   The returned value must be freed by the caller.  */
> +
> +char *
> +value::to_str () const
> +{
> +  pretty_printer pp;
> +  print (&pp);
> +  return xstrdup (pp_formatted_text (&pp));
> +}
> +
> +/* Dump this json::value tree to OUTF.
> +   No formatting is done.  There are no guarantees about the order
> +   in which the key/value pairs of json::objects are printed.  */
> +
> +void
> +value::dump (FILE *outf) const
> +{
> +  pretty_printer pp;
> +  pp_buffer (&pp)->stream = outf;
> +  print (&pp);
> +  pp_flush (&pp);
> +}
> +
> +/* If this json::value is a json::object, return it,
> +   otherwise return NULL.  */
> +
> +const object *
> +value::as_object () const
> +{
> +  if (get_kind () != JSON_OBJECT)
> +    return NULL;
> +  return static_cast <const object *> (this);
> +}
> +
> +/* If this json::value is a json::array, return it,
> +   otherwise return NULL.  */
> +
> +const array *
> +value::as_array () const
> +{
> +  if (get_kind () != JSON_ARRAY)
> +    return NULL;
> +  return static_cast <const array *> (this);
> +}
> +
> +/* If this json::value is a json::number, return it,
> +   otherwise return NULL.  */
> +
> +const number *
> +value::as_number () const
> +{
> +  if (get_kind () != JSON_NUMBER)
> +    return NULL;
> +  return static_cast <const number *> (this);
> +}
> +
> +/* If this json::value is a json::string, return it,
> +   otherwise return NULL.  */
> +
> +const string *
> +value::as_string () const
> +{
> +  if (get_kind () != JSON_STRING)
> +    return NULL;
> +  return static_cast <const string *> (this);
> +}
> +
> +/* Attempt to get the value of a key/value pair from this value
> +   as if THIS value were an object.
> +
> +   If THIS is not a json::object, return write an error message to OUT_ERR
> +   (which must be freed by the caller) and return false.
> +
> +   Otherwise write the value ptr (possibly NULL) to OUT_VALUE and
> +   return true.  */
> +
> +bool
> +value::get_optional_value_by_key (const char *name, const value
> *&out_value,
> +				  char *&out_err) const
> +{
> +  const json::object *obj = as_object ();
> +  if (!obj)
> +    {
> +      out_err = xstrdup ("not an object");
> +      return false;
> +    }
> +  out_value = obj->get (name);
> +  return true;
> +}
> +
> +/* Attempt to get a string value of a key/value pair from this value
> +   as if THIS value were an object.
> +
> +   If THIS is a json::object, and KEY is either not present, is a string,
> +   or is the "null" JSON literal, then return true, and write to
> OUT_VALUE.
> +   If a string, then the ptr is written to OUT_VALUE, otherwise NULL
> +   is written to OUT_VALUE.
> +
> +   If THIS is not a json::object, or KEY is not a string/"null",
> +   return false and write an error message to OUT_ERR
> +   (which must be freed by the caller).  */
> +
> +bool
> +value::get_optional_string_by_key (const char *name, const char
> *&out_value,
> +				   char *&out_err) const
> +{
> +  const json::value *v;
> +  if (!get_optional_value_by_key (name, v, out_err))
> +    return false;
> +  if (v && v->get_kind () != JSON_NULL)
> +    {
> +      const json::string *s = v->as_string ();
> +      if (!s)
> +	{
> +	  out_err = xasprintf ("not a string: \"%s\"", name);
> +	  return false;
> +	}
> +      out_value = s->get_string ();
> +      return true;
> +    }
> +  else
> +    {
> +      out_value = NULL;
> +      return true;
> +    }
> +}
> +
> +/* Attempt to get lookup the value of a key/value pair from this value
> +   as if this value were an object.
> +
> +   To succeed, THIS must be a json::object, and it must have a key named
> +   NAME.
> +
> +   On success, return true and write the value to OUT_VALUE.
> +   On failure, return false and write an error message to OUT_ERR
> +   (which must be freed by the caller).  */
> +
> +bool
> +value::get_value_by_key (const char *name, const value *&out_value,
> +			 char *&out_err) const
> +{
> +  const json::object *obj = as_object ();
> +  if (!obj)
> +    {
> +      out_err = xstrdup ("not an object");
> +      return false;
> +    }
> +  const json::value *v = obj->get (name);
> +  if (!v)
> +    {
> +      out_err = xasprintf ("missing attribute: \"%s\"", name);
> +      return false;
> +    }
> +  out_value = v;
> +  return true;
> +}
> +
> +/* As value::get_value_by_key, but the value must be a number;
> +   if successful, write it as an int to OUT_VALUE.  */
> +
> +bool
> +value::get_int_by_key (const char *name, int &out_value, char *&out_err)
> const
> +{
> +  const json::value *v;
> +  if (!get_value_by_key (name, v, out_err))
> +    return false;
> +  const json::number *n = v->as_number ();
> +  if (!n)
> +    {
> +      out_err = xasprintf ("not a number: \"%s\"", name);
> +      return false;
> +    }
> +  out_value = n->get ();
> +  return true;
> +}
> +
> +/* As value::get_value_by_key, but the value must be a string;
> +   if successful, write it as const char * to OUT_VALUE.  */
> +
> +bool
> +value::get_string_by_key (const char *name, const char *&out_value,
> +			  char *&out_err) const
> +{
> +  const json::value *v;
> +  if (!get_value_by_key (name, v, out_err))
> +    return false;
> +  const json::string *s = v->as_string ();
> +  if (!s)
> +    {
> +      out_err = xasprintf ("not a string: \"%s\"", name);
> +      return false;
> +    }
> +  out_value = s->get_string ();
> +  return true;
> +}
> +
> +/* As value::get_value_by_key, but the value must be an array;
> +   if successful, write it as a json::array * to OUT_VALUE.  */
> +
> +bool
> +value::get_array_by_key (const char *name, const array *&out_value,
> +			 char *&out_err) const
> +{
> +  const json::value *v;
> +  if (!get_value_by_key (name, v, out_err))
> +    return false;
> +  const json::array *arr = v->as_array ();
> +  if (!arr)
> +    {
> +      out_err = xasprintf ("not an array: \"%s\"", name);
> +      return false;
> +    }
> +  out_value = arr;
> +  return true;
> +}
> +
> +/* class json::object, a subclass of json::value, representing
> +   an unordered collection of key/value pairs.  */
> +
> +/* json:object's dtor.  */
> +
> +object::~object ()
> +{
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      free (const_cast <char *>((*it).first));
> +      delete ((*it).second);
> +    }
> +}
> +
> +/* Implementation of json::value::print for json::object.  */
> +
> +void
> +object::print (pretty_printer *pp) const
> +{
> +  /* Note that the order is not guaranteed.  */
> +  pp_character (pp, '{');
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      if (it != m_map.begin ())
> +	pp_string (pp, ", ");
> +      const char *key = const_cast <char *>((*it).first);
> +      value *value = (*it).second;
> +      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
> +      value->print (pp);
> +    }
> +  pp_character (pp, '}');
> +}
> +
> +/* Implementation of json::value::clone for json::object.  */
> +
> +value *
> +object::clone () const
> +{
> +  object *other = new object ();
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      const char *key = const_cast <char *>((*it).first);
> +      value *value = (*it).second;
> +      other->set (key, value->clone ());
> +    }
> +  return other;
> +}
> +
> +/* Get the json::value * for KEY, or NULL if the key is not present.  */
> +
> +value *
> +object::get (const char *key) const
> +{
> +  value **slot = const_cast <object*> (this)->m_map.get (key);
> +  if (slot)
> +    return *slot;
> +  return NULL;
> +}
> +
> +/* As object::get (KEY), but return NULL if the value of the key
> +   is the "null" JSON literal.  */
> +
> +value *
> +object::get_if_nonnull (const char *key) const
> +{
> +  value *result = get (key);
> +  if (!result)
> +    return NULL;
> +  if (result->get_kind () == JSON_NULL)
> +    return NULL;
> +  return result;
> +}
> +
> +/* Set the json::value * for KEY, taking ownership of VALUE
> +   (and taking a copy of KEY if necessary).  */
> +
> +void
> +object::set (const char *key, value *v)
> +{
> +  value **ptr = m_map.get (key);
> +  if (ptr)
> +    {
> +      /* If the key is already present, delete the existing value
> +	 and overwrite it.  */
> +      delete *ptr;
> +      *ptr = v;
> +    }
> +  else
> +    /* If the key wasn't already present, take a copy of the key,
> +       and store the value.  */
> +    m_map.put (xstrdup (key), v);
> +}
> +
> +/* class json::array, a subclass of json::value, representing
> +   an ordered collection of values.  */
> +
> +/* json::array's dtor.  */
> +
> +array::~array ()
> +{
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    delete v;
> +}
> +
> +/* Implementation of json::value::print for json::array.  */
> +
> +void
> +array::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '[');
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    {
> +      if (i)
> +	pp_string (pp, ", ");
> +      v->print (pp);
> +    }
> +  pp_character (pp, ']');
> +}
> +
> +/* Implementation of json::value::clone for json::array.  */
> +
> +value *
> +array::clone () const
> +{
> +  array *other = new array ();
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    other->append (v->clone ());
> +  return other;
> +}
> +
> +/* class json::number, a subclass of json::value, wrapping a double.  */
> +
> +/* Implementation of json::value::print for json::number.  */
> +
> +void
> +number::print (pretty_printer *pp) const
> +{
> +  char tmp[1024];
> +  snprintf (tmp, sizeof (tmp), "%g", m_value);
> +  pp_string (pp, tmp);
> +}
> +
> +/* Implementation of json::value::clone for json::number.  */
> +
> +value *
> +number::clone () const
> +{
> +  return new number (m_value);
> +}
> +
> +/* class json::string, a subclass of json::value.  */
> +
> +void
> +string::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '"');
> +  for (const char *ptr = m_utf8; *ptr; ptr++)
> +    {
> +      char ch = *ptr;
> +      switch (ch)
> +	{
> +	case '"':
> +	  pp_string (pp, "\\\"");
> +	  break;
> +	case '\\':
> +	  pp_string (pp, "\\n");
> +	  break;
> +	case '\b':
> +	  pp_string (pp, "\\b");
> +	  break;
> +	case '\f':
> +	  pp_string (pp, "\\f");
> +	  break;
> +	case '\n':
> +	  pp_string (pp, "\\n");
> +	  break;
> +	case '\r':
> +	  pp_string (pp, "\\r");
> +	  break;
> +	case '\t':
> +	  pp_string (pp, "\\t");
> +	  break;
> +
> +	default:
> +	  pp_character (pp, ch);
> +	}
> +    }
> +  pp_character (pp, '"');
> +}
> +
> +/* Implementation of json::value::clone for json::string.  */
> +
> +value *
> +string::clone () const
> +{
> +  return new string (m_utf8);
> +}
> +
> +/* class json::literal, a subclass of json::value.  */
> +
> +/* Implementation of json::value::print for json::literal.  */
> +
> +void
> +literal::print (pretty_printer *pp) const
> +{
> +  switch (m_kind)
> +    {
> +    case JSON_TRUE:
> +      pp_string (pp, "true");
> +      break;
> +    case JSON_FALSE:
> +      pp_string (pp, "false");
> +      break;
> +    case JSON_NULL:
> +      pp_string (pp, "null");
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
> +/* Implementation of json::value::clone for json::literal.  */
> +
> +value *
> +literal::clone () const
> +{
> +  return new literal (m_kind);
> +}
> +
> +\f
> +/* Declarations relating to parsing JSON, all within an
> +   anonymous namespace.  */
> +
> +namespace {
> +
> +/* A typedef representing a single unicode character.  */
> +
> +typedef unsigned unichar;
> +
> +/* An enum for discriminating different kinds of JSON token.  */
> +
> +enum token_id
> +{
> +  TOK_ERROR,
> +
> +  TOK_EOF,
> +
> +  /* Punctuation.  */
> +  TOK_OPEN_SQUARE,
> +  TOK_OPEN_CURLY,
> +  TOK_CLOSE_SQUARE,
> +  TOK_CLOSE_CURLY,
> +  TOK_COLON,
> +  TOK_COMMA,
> +
> +  /* Literal names.  */
> +  TOK_TRUE,
> +  TOK_FALSE,
> +  TOK_NULL,
> +
> +  TOK_STRING,
> +  TOK_NUMBER
> +};
> +
> +/* Human-readable descriptions of enum token_id.  */
> +
> +static const char *token_id_name[] = {
> +  "error",
> +  "EOF",
> +  "'['",
> +  "'{'",
> +  "']'",
> +  "'}'",
> +  "':'",
> +  "','",
> +  "'true'",
> +  "'false'",
> +  "'null'",
> +  "string",
> +  "number"
> +};
> +
> +/* Tokens within the JSON lexer.  */
> +
> +struct token
> +{
> +  /* The kind of token.  */
> +  enum token_id id;
> +
> +  /* The location of this token within the unicode
> +     character stream.  */
> +  int index;
> +
> +  union
> +  {
> +    /* Value for TOK_ERROR and TOK_STRING.  */
> +    char *string;
> +
> +    /* Value for TOK_NUMBER.  */
> +    double number;
> +  } u;
> +};
> +
> +/* A class for lexing JSON.  */
> +
> +class lexer
> +{
> + public:
> +  lexer ();
> +  ~lexer ();
> +  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
> +
> +  const token *peek ();
> +  void consume ();
> +
> + private:
> +  bool get_char (unichar &out);
> +  void unget_char ();
> +  static void dump_token (FILE *outf, const token *tok);
> +  void lex_token (token *out);
> +  void lex_string (token *out);
> +  void lex_number (token *out, unichar first_char);
> +  bool rest_of_literal (const char *suffix);
> +
> + private:
> +  auto_vec<unichar> m_buffer;
> +  int m_next_char_idx;
> +
> +  static const int MAX_TOKENS = 1;
> +  token m_next_tokens[MAX_TOKENS];
> +  int m_num_next_tokens;
> +};
> +
> +/* A class for parsing JSON.  */
> +
> +class parser
> +{
> + public:
> +  parser (char **err_out);
> +  bool add_utf8 (size_t length, const char *utf8_buf, char **err_out);
> +  value *parse_value (int depth);
> +  object *parse_object (int depth);
> +  array *parse_array (int depth);
> +
> +  bool seen_error_p () const { return *m_err_out; }
> +  void require_eof ();
> +
> + private:
> +  void require (enum token_id tok_id);
> +  void error_at (int, const char *, ...) ATTRIBUTE_PRINTF_3;
> +
> + private:
> +  lexer m_lexer;
> +  char **m_err_out;
> +};
> +
> +} // anonymous namespace for parsing implementation
> +
> +/* Parser implementation.  */
> +
> +/* lexer's ctor.  */
> +
> +lexer::lexer ()
> +: m_buffer (), m_next_char_idx (0), m_num_next_tokens (0)
> +{
> +}
> +
> +/* lexer's dtor.  */
> +
> +lexer::~lexer ()
> +{
> +  while (m_num_next_tokens > 0)
> +    consume ();
> +}
> +
> +/* Peek the next token.  */
> +
> +const token *
> +lexer::peek ()
> +{
> +  if (m_num_next_tokens == 0)
> +    {
> +      lex_token (&m_next_tokens[0]);
> +      m_num_next_tokens++;
> +    }
> +  return &m_next_tokens[0];
> +}
> +
> +/* Consume the next token.  */
> +
> +void
> +lexer::consume ()
> +{
> +  if (m_num_next_tokens == 0)
> +    peek ();
> +
> +  gcc_assert (m_num_next_tokens > 0);
> +  gcc_assert (m_num_next_tokens <= MAX_TOKENS);
> +
> +  if (0)
> +    {
> +      fprintf (stderr, "consuming token: ");
> +      dump_token (stderr, &m_next_tokens[0]);
> +      fprintf (stderr, "\n");
> +    }
> +
> +  if (m_next_tokens[0].id == TOK_ERROR
> +      || m_next_tokens[0].id == TOK_STRING)
> +    free (m_next_tokens[0].u.string);
> +
> +  m_num_next_tokens--;
> +  memmove (&m_next_tokens[0], &m_next_tokens[1],
> +	   sizeof (token) * m_num_next_tokens);
> +}
> +
> +/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this lexer's
> +   buffer.  */
> +
> +bool
> +lexer::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
> +{
> +  /* FIXME: adapted from charset.c:one_utf8_to_cppchar.  */
> +  static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
> +  static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
> +
> +  const uchar *inbuf = (const unsigned char *) (utf8_buf);
> +  const uchar **inbufp = &inbuf;
> +  size_t *inbytesleftp = &length;
> +
> +  while (length > 0)
> +    {
> +      unichar c;
> +      const uchar *inbuf = *inbufp;
> +      size_t nbytes, i;
> +
> +      c = *inbuf;
> +      if (c < 0x80)
> +	{
> +	  m_buffer.safe_push (c);
> +	  *inbytesleftp -= 1;
> +	  *inbufp += 1;
> +	  continue;
> +	}
> +
> +      /* The number of leading 1-bits in the first byte indicates how many
> +	 bytes follow.  */
> +      for (nbytes = 2; nbytes < 7; nbytes++)
> +	if ((c & ~masks[nbytes-1]) == patns[nbytes-1])
> +	  goto found;
> +      *err_out = xstrdup ("ill-formed UTF-8 sequence");
> +      return false;
> +    found:
> +
> +      if (*inbytesleftp < nbytes)
> +	{
> +	  *err_out = xstrdup ("ill-formed UTF-8 sequence");
> +	  return false;
> +	}
> +
> +      c = (c & masks[nbytes-1]);
> +      inbuf++;
> +      for (i = 1; i < nbytes; i++)
> +	{
> +	  unichar n = *inbuf++;
> +	  if ((n & 0xC0) != 0x80)
> +	    {
> +	      *err_out = xstrdup ("ill-formed UTF-8 sequence");
> +	      return false;
> +	    }
> +	  c = ((c << 6) + (n & 0x3F));
> +	}
> +
> +      /* Make sure the shortest possible encoding was used.  */
> +      if ((   c <=      0x7F && nbytes > 1)
> +	  || (c <=     0x7FF && nbytes > 2)
> +	  || (c <=    0xFFFF && nbytes > 3)
> +	  || (c <=  0x1FFFFF && nbytes > 4)
> +	  || (c <= 0x3FFFFFF && nbytes > 5))
> +	{
> +	  *err_out = xstrdup ("ill-formed UTF-8:"
> +			      " shortest possible encoding not used");
> +	  return false;
> +	}
> +
> +      /* Make sure the character is valid.  */
> +      if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF))
> +	{
> +	  *err_out = xstrdup ("ill-formed UTF-8: invalid character");
> +	  return false;
> +	}
> +
> +      m_buffer.safe_push (c);
> +      *inbufp = inbuf;
> +      *inbytesleftp -= nbytes;
> +    }
> +  return true;
> +}
> +
> +/* Attempt to get the next unicode character from this lexer's buffer.
> +   If successful, write it to OUT and return true.
> +   Otherwise, return false.  */
> +
> +bool
> +lexer::get_char (unichar &out)
> +{
> +  if (m_next_char_idx >= (int)m_buffer.length ())
> +    return false;
> +
> +  out = m_buffer[m_next_char_idx++];
> +  return true;
> +}
> +
> +/* FIXME.  */
> +
> +void
> +lexer::unget_char ()
> +{
> +  --m_next_char_idx;
> +}
> +
> +/* Print a textual representation of TOK to OUTF.
> +   This is intended for debugging the lexer and parser,
> +   rather than for user-facing output.  */
> +
> +void
> +lexer::dump_token (FILE *outf, const token *tok)
> +{
> +  switch (tok->id)
> +    {
> +    case TOK_ERROR:
> +      fprintf (outf, "TOK_ERROR (\"%s\")", tok->u.string);
> +      break;
> +
> +    case TOK_EOF:
> +      fprintf (outf, "TOK_EOF");
> +      break;
> +
> +    case TOK_OPEN_SQUARE:
> +      fprintf (outf, "TOK_OPEN_SQUARE");
> +      break;
> +
> +    case TOK_OPEN_CURLY:
> +      fprintf (outf, "TOK_OPEN_CURLY");
> +      break;
> +
> +    case TOK_CLOSE_SQUARE:
> +      fprintf (outf, "TOK_CLOSE_SQUARE");
> +      break;
> +
> +    case TOK_CLOSE_CURLY:
> +      fprintf (outf, "TOK_CLOSE_CURLY");
> +      break;
> +
> +    case TOK_COLON:
> +      fprintf (outf, "TOK_COLON");
> +      break;
> +
> +    case TOK_COMMA:
> +      fprintf (outf, "TOK_COMMA");
> +      break;
> +
> +    case TOK_TRUE:
> +      fprintf (outf, "TOK_TRUE");
> +      break;
> +
> +    case TOK_FALSE:
> +      fprintf (outf, "TOK_FALSE");
> +      break;
> +
> +    case TOK_NULL:
> +      fprintf (outf, "TOK_NULL");
> +      break;
> +
> +    case TOK_STRING:
> +      fprintf (outf, "TOK_STRING (\"%s\")", tok->u.string);
> +      break;
> +
> +    case TOK_NUMBER:
> +      fprintf (outf, "TOK_NUMBER (%f)", tok->u.number);
> +      break;
> +
> +    default:
> +      gcc_unreachable ();
> +      break;
> +    }
> +}
> +
> +/* Attempt to lex the input buffer, writing the next token to OUT.
> +   On errors, TOK_ERROR (or TOK_EOF) is written to OUT.  */
> +
> +void
> +lexer::lex_token (token *out)
> +{
> +  /* Skip to next non-whitespace char.  */
> +  unichar next_char;
> +  while (1)
> +    {
> +      out->index = m_next_char_idx;
> +      if (!get_char (next_char))
> +	{
> +	  out->id = TOK_EOF;
> +	  return;
> +	}
> +      if (next_char != ' '
> +	  && next_char != '\t'
> +	  && next_char != '\n'
> +	  && next_char != '\r')
> +	break;
> +    }
> +
> +  switch (next_char)
> +    {
> +    case '[':
> +      out->id = TOK_OPEN_SQUARE;
> +      break;
> +
> +    case '{':
> +      out->id = TOK_OPEN_CURLY;
> +      break;
> +
> +    case ']':
> +      out->id = TOK_CLOSE_SQUARE;
> +      break;
> +
> +    case '}':
> +      out->id = TOK_CLOSE_CURLY;
> +      break;
> +
> +    case ':':
> +      out->id = TOK_COLON;
> +      break;
> +
> +    case ',':
> +      out->id = TOK_COMMA;
> +      break;
> +
> +    case '"':
> +      lex_string (out);
> +      break;
> +
> +    case '-':
> +    case '0':
> +    case '1':
> +    case '2':
> +    case '3':
> +    case '4':
> +    case '5':
> +    case '6':
> +    case '7':
> +    case '8':
> +    case '9':
> +      lex_number (out, next_char);
> +      break;
> +
> +    case 't':
> +      /* Handle literal "true".  */
> +      if (rest_of_literal ("rue"))
> +	{
> +	  out->id = TOK_TRUE;
> +	  break;
> +	}
> +      else
> +	goto err;
> +
> +    case 'f':
> +      /* Handle literal "false".  */
> +      if (rest_of_literal ("alse"))
> +	{
> +	  out->id = TOK_FALSE;
> +	  break;
> +	}
> +      else
> +	goto err;
> +
> +    case 'n':
> +      /* Handle literal "null".  */
> +      if (rest_of_literal ("ull"))
> +	{
> +	  out->id = TOK_NULL;
> +	  break;
> +	}
> +      else
> +	goto err;
> +
> +    err:
> +    default:
> +      out->id = TOK_ERROR;
> +      out->u.string = xasprintf ("unexpected character: %c", next_char);
> +      break;
> +    }
> +}
> +
> +/* Having consumed an open-quote character from the lexer's buffer,
> attempt
> +   to lex the rest of a JSON string, writing the result to OUT (or
> TOK_ERROR)
> +   if an error occurred.
> +   (ECMA-404 section 9; RFC 7159 section 7).  */
> +
> +void
> +lexer::lex_string (token *out)
> +{
> +  auto_vec<unichar> content;
> +  bool still_going = true;
> +  while (still_going)
> +    {
> +      unichar uc;
> +      if (!get_char (uc))
> +	{
> +	  out->id = TOK_ERROR;
> +	  out->u.string = xstrdup ("EOF within string");
> +	  return;
> +	}
> +      switch (uc)
> +	{
> +	case '"':
> +	  still_going = false;
> +	  break;
> +	case '\\':
> +	  {
> +	    unichar next_char;
> +	    if (!get_char (next_char))
> +	      {
> +		out->id = TOK_ERROR;
> +		out->u.string = xstrdup ("EOF within string");;
> +		return;
> +	      }
> +	    switch (next_char)
> +	      {
> +	      case '"':
> +	      case '\\':
> +	      case '/':
> +		content.safe_push (next_char);
> +		break;
> +
> +	      case 'b':
> +		content.safe_push ('\b');
> +		break;
> +
> +	      case 'f':
> +		content.safe_push ('\f');
> +		break;
> +
> +	      case 'n':
> +		content.safe_push ('\n');
> +		break;
> +
> +	      case 'r':
> +		content.safe_push ('\r');
> +		break;
> +
> +	      case 't':
> +		content.safe_push ('\t');
> +		break;
> +
> +	      case 'u':
> +		{
> +		  unichar result = 0;
> +		  for (int i = 0; i < 4; i++)
> +		    {
> +		      unichar hexdigit;
> +		      if (!get_char (hexdigit))
> +			{
> +			  out->id = TOK_ERROR;
> +			  out->u.string = xstrdup ("EOF within string");
> +			  return;
> +			}
> +		      result <<= 4;
> +		      if (hexdigit >= '0' && hexdigit <= '9')
> +			result += hexdigit - '0';
> +		      else if (hexdigit >= 'a' && hexdigit <= 'f')
> +			result += (hexdigit - 'a') + 10;
> +		      else if (hexdigit >= 'A' && hexdigit <= 'F')
> +			result += (hexdigit - 'A') + 10;
> +		      else
> +			{
> +			  out->id = TOK_ERROR;
> +			  out->u.string = xstrdup ("bogus hex char");
> +			  return;
> +			}
> +		    }
> +		  content.safe_push (result);
> +		}
> +		break;
> +
> +	      default:
> +		out->id = TOK_ERROR;
> +		out->u.string = xstrdup ("unrecognized escape char");
> +		return;
> +	      }
> +	  }
> +	  break;
> +
> +	default:
> +	  /* Reject unescaped control characters U+0000 through U+001F
> +	     (ECMA-404 section 9 para 1; RFC 7159 section 7 para 1).  */
> +	  if (uc <= 0x1f)
> +	    {
> +		out->id = TOK_ERROR;
> +		out->u.string = xstrdup ("unescaped control char");
> +		return;
> +	    }
> +
> +	  /* Otherwise, add regular unicode code point.  */
> +	  content.safe_push (uc);
> +	  break;
> +	}
> +    }
> +
> +  out->id = TOK_STRING;
> +
> +  auto_vec<char> utf8_buf;
> +  // FIXME: adapted from libcpp/charset.c:one_cppchar_to_utf8
> +  for (unsigned i = 0; i < content.length (); i++)
> +    {
> +      static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
> };
> +      static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE
> };
> +      size_t nbytes;
> +      uchar buf[6], *p = &buf[6];
> +      unichar c = content[i];
> +
> +      nbytes = 1;
> +      if (c < 0x80)
> +	*--p = c;
> +      else
> +	{
> +	  do
> +	    {
> +	      *--p = ((c & 0x3F) | 0x80);
> +	      c >>= 6;
> +	      nbytes++;
> +	    }
> +	  while (c >= 0x3F || (c & limits[nbytes-1]));
> +	  *--p = (c | masks[nbytes-1]);
> +	}
> +
> +      while (p < &buf[6])
> +	utf8_buf.safe_push (*p++);
> +    }
> +
> +  out->u.string = XNEWVEC (char, utf8_buf.length () + 1);
> +  for (unsigned i = 0; i < utf8_buf.length (); i++)
> +    out->u.string[i] = utf8_buf[i];
> +  out->u.string[utf8_buf.length ()] = '\0';
> +
> +  // FIXME: leaks?  have a json_context do the allocation
> +}
> +
> +/* Having consumed FIRST_CHAR, an initial digit or '-' character from
> +   the lexer's buffer attempt to lex the rest of a JSON number, writing
> +   the result to OUT (or TOK_ERROR) if an error occurred.
> +   (ECMA-404 section 8; RFC 7159 section 6).  */
> +
> +void
> +lexer::lex_number (token *out, unichar first_char)
> +{
> +  bool negate = false;
> +  double value = 0.0;
> +  if (first_char == '-')
> +    {
> +      negate = true;
> +      if (!get_char (first_char))
> +	{
> +	  out->id = TOK_ERROR;
> +	  out->u.string = xstrdup ("expected digit");
> +	  return;
> +	}
> +    }
> +
> +  if (first_char == '0')
> +    value = 0.0;
> +  else if (!ISDIGIT (first_char))
> +    {
> +      out->id = TOK_ERROR;
> +      out->u.string = xstrdup ("expected digit");
> +      return;
> +    }
> +  else
> +    {
> +      /* Got a nonzero digit; expect zero or more digits.  */
> +      value = first_char - '0';
> +      while (1)
> +	{
> +	  unichar uc;
> +	  if (!get_char (uc))
> +	    break;
> +	  if (ISDIGIT (uc))
> +	    {
> +	      value *= 10;
> +	      value += uc -'0';
> +	      continue;
> +	    }
> +	  else
> +	    {
> +	      unget_char ();
> +	      break;
> +	    }
> +	}
> +    }
> +
> +  /* Optional '.', followed by one or more decimals.  */
> +  unichar next_char;
> +  if (get_char (next_char))
> +    {
> +      if (next_char == '.')
> +	{
> +	  /* Parse decimal digits.  */
> +	  bool had_digit = false;
> +	  // FIXME: does this lose too much precision?
> +	  double digit_factor = 0.1;
> +	  while (get_char (next_char))
> +	    {
> +	      if (!ISDIGIT (next_char))
> +		{
> +		  unget_char ();
> +		  break;
> +		}
> +	      value += (next_char - '0') * digit_factor;
> +	      digit_factor *= 0.1;
> +	      had_digit = true;
> +	    }
> +	  if (!had_digit)
> +	    {
> +	      out->id = TOK_ERROR;
> +	      out->u.string = xstrdup ("expected digit");
> +	      return;
> +	    }
> +	}
> +      else
> +	unget_char ();
> +    }
> +
> +  /* Parse 'e' and 'E'.  */
> +  unichar exponent_char;
> +  if (get_char (exponent_char))
> +    {
> +      if (exponent_char == 'e' || exponent_char == 'E')
> +	{
> +	  /* Optional +/-.  */
> +	  unichar sign_char;
> +	  int exponent = 0;
> +	  bool negate_exponent = false;
> +	  bool had_exponent_digit = false;
> +	  if (!get_char (sign_char))
> +	    {
> +	      out->id = TOK_ERROR;
> +	      out->u.string = xstrdup ("EOF within exponent");
> +	      return;
> +	    }
> +	  if (sign_char == '-')
> +	    negate_exponent = true;
> +	  else if (sign_char == '+')
> +	    ;
> +	  else if (ISDIGIT (sign_char))
> +	    {
> +	      exponent = sign_char - '0';
> +	      had_exponent_digit = true;
> +	    }
> +	  else
> +	    {
> +	      out->id = TOK_ERROR;
> +	      out->u.string
> +		= xstrdup ("expected '-','+' or digit within exponent");
> +	      return;
> +	    }
> +
> +	  /* One or more digits (we might have seen the digit above,
> +	     though).  */
> +	  while (1)
> +	    {
> +	      unichar uc;
> +	      if (!get_char (uc))
> +		break;
> +	      if (ISDIGIT (uc))
> +		{
> +		  exponent *= 10;
> +		  exponent += uc -'0';
> +		  had_exponent_digit = true;
> +		  continue;
> +		}
> +	      else
> +		{
> +		  unget_char ();
> +		  break;
> +		}
> +	    }
> +	  if (!had_exponent_digit)
> +	    {
> +	      out->id = TOK_ERROR;
> +	      out->u.string = xstrdup ("expected digit within exponent");
> +	      return;
> +	    }
> +	  if (negate_exponent)
> +	    exponent = -exponent;
> +	  /* FIXME: better way to do this?  */
> +	  value = value * pow (10, exponent);
> +	}
> +      else
> +	unget_char ();
> +    }
> +
> +  if (negate)
> +    value = -value;
> +
> +  out->id = TOK_NUMBER;
> +  out->u.number = value;
> +}
> +
> +/* Determine if the next characters to be lexed match SUFFIX.
> +   SUFFIX must be pure ASCII.
> +   If so, consume the characters and return true.
> +   Otherwise, return false.  */
> +
> +bool
> +lexer::rest_of_literal (const char *suffix)
> +{
> +  int suffix_idx = 0;
> +  int buf_idx = m_next_char_idx;
> +  while (1)
> +    {
> +      if (suffix[suffix_idx] == '\0')
> +	{
> +	  m_next_char_idx += suffix_idx;
> +	  return true;
> +	}
> +      if (buf_idx >= (int)m_buffer.length ())
> +	return false;
> +      /* This assumes that suffix is ASCII.  */
> +      if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx])
> +	return false;
> +      buf_idx++;
> +      suffix_idx++;
> +    }
> +}
> +
> +/* parser's ctor.  */
> +
> +parser::parser (char **err_out)
> +: m_lexer (), m_err_out (err_out)
> +{
> +  gcc_assert (err_out);
> +  gcc_assert (*err_out == NULL);
> +  *err_out = NULL;
> +}
> +
> +/* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this parser's
> +   lexer's buffer.  */
> +
> +bool
> +parser::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
> +{
> +  return m_lexer.add_utf8 (length, utf8_buf, err_out);
> +}
> +
> +/* Parse a JSON value (object, array, number, string, or literal).
> +   (ECMA-404 section 5; RFC 7159 section 3).  */
> +
> +value *
> +parser::parse_value (int depth)
> +{
> +  const token *tok = m_lexer.peek ();
> +
> +  /* Avoid stack overflow with deeply-nested inputs; RFC 7159 section 9
> +     states: "An implementation may set limits on the maximum depth
> +     of nesting.".
> +
> +     Ideally we'd avoid this limit (e.g. by rewriting parse_value,
> +     parse_object, and parse_array into a single function with a vec of
> +     state).  */
> +  const int MAX_DEPTH = 100;
> +  if (depth >= MAX_DEPTH)
> +    {
> +      error_at (tok->index, "maximum nesting depth exceeded: %i",
> MAX_DEPTH);
> +      return NULL;
> +    }
> +
> +  switch (tok->id)
> +    {
> +    case TOK_OPEN_CURLY:
> +      return parse_object (depth);
> +
> +    case TOK_STRING:
> +      {
> +	string *result = new string (tok->u.string);
> +	m_lexer.consume ();
> +	return result;
> +      }
> +
> +    case TOK_OPEN_SQUARE:
> +      return parse_array (depth);
> +
> +    case TOK_NUMBER:
> +      {
> +	number *result = new number (tok->u.number);
> +	m_lexer.consume ();
> +	return result;
> +      }
> +
> +    case TOK_TRUE:
> +      {
> +	literal *result = new literal (JSON_TRUE);
> +	m_lexer.consume ();
> +	return result;
> +      }
> +
> +    case TOK_FALSE:
> +      {
> +	literal *result = new literal (JSON_FALSE);
> +	m_lexer.consume ();
> +	return result;
> +      }
> +
> +    case TOK_NULL:
> +      {
> +	literal *result = new literal (JSON_NULL);
> +	m_lexer.consume ();
> +	return result;
> +      }
> +
> +    default:
> +      error_at (tok->index, "unexpected token: %s",
> token_id_name[tok->id]);
> +      return NULL;
> +    }
> +}
> +
> +/* Parse a JSON object.
> +   (ECMA-404 section 6; RFC 7159 section 4).  */
> +
> +object *
> +parser::parse_object (int depth)
> +{
> +  require (TOK_OPEN_CURLY);
> +
> +  object *result = new object ();
> +
> +  const token *tok = m_lexer.peek ();
> +  if (tok->id == TOK_CLOSE_CURLY)
> +    {
> +      require (TOK_CLOSE_CURLY);
> +      return result;
> +    }
> +  if (tok->id != TOK_STRING)
> +    {
> +      error_at (tok->index, "expected string for object key");
> +      return result;
> +    }
> +  while (!seen_error_p ())
> +    {
> +      tok = m_lexer.peek ();
> +      if (tok->id != TOK_STRING)
> +	{
> +	  error_at (tok->index, "expected string for object key");
> +	  return result;
> +	}
> +      char *key = xstrdup (tok->u.string);
> +      m_lexer.consume ();
> +
> +      require (TOK_COLON);
> +
> +      value *v = parse_value (depth + 1);
> +      if (!v)
> +	{
> +	  free (key);
> +	  return result;
> +	}
> +      /* We don't enforce uniqueness for keys.  */
> +      result->set (key, v);
> +      free (key);
> +
> +      tok = m_lexer.peek ();
> +      if (tok->id == TOK_COMMA)
> +	{
> +	  m_lexer.consume ();
> +	  continue;
> +	}
> +      else
> +	{
> +	  require (TOK_CLOSE_CURLY);
> +	  break;
> +	}
> +    }
> +  return result;
> +}
> +
> +/* Parse a JSON array.
> +   (ECMA-404 section 7; RFC 7159 section 5).  */
> +
> +array *
> +parser::parse_array (int depth)
> +{
> +  require (TOK_OPEN_SQUARE);
> +
> +  array *result = new array ();
> +
> +  const token *tok = m_lexer.peek ();
> +  if (tok->id == TOK_CLOSE_SQUARE)
> +    {
> +      m_lexer.consume ();
> +      return result;
> +    }
> +
> +  while (!seen_error_p ())
> +    {
> +      value *v = parse_value (depth + 1);
> +      if (!v)
> +	return result;
> +
> +      result->append (v);
> +
> +      tok = m_lexer.peek ();
> +      if (tok->id == TOK_COMMA)
> +	{
> +	  m_lexer.consume ();
> +	  continue;
> +	}
> +      else
> +	{
> +	  require (TOK_CLOSE_SQUARE);
> +	  break;
> +	}
> +    }
> +
> +  return result;
> +}
> +
> +/* Require an EOF, or fail if there is surplus input.  */
> +
> +void
> +parser::require_eof ()
> +{
> +  require (TOK_EOF);
> +}
> +
> +/* Consume the next token, issuing an error if it is not of kind TOK_ID.
> */
> +
> +void
> +parser::require (enum token_id tok_id)
> +{
> +  const token *tok = m_lexer.peek ();
> +  if (tok->id != tok_id)
> +    {
> +      if (tok->id == TOK_ERROR)
> +	error_at (tok->index, "expected %s; got bad token: %s",
> +		  token_id_name[tok_id], tok->u.string);
> +      else
> +	error_at (tok->index, "expected %s; got %s", token_id_name[tok_id],
> +		  token_id_name[tok->id]);
> +    }
> +  m_lexer.consume ();
> +}
> +
> +/* Issue a parsing error.  If this is the first error that has occurred on
> +   the parser, store it within the parser's m_err_out (the buffer will
> +   eventually need to be free by the caller of the parser).
> +   Otherwise the error is discarded.
> +
> +   TODO: maybe provide a callback so that client code can print all errors?
>  */
> +
> +void
> +parser::error_at (int index, const char *fmt, ...)
> +{
> +  va_list ap;
> +  va_start (ap, fmt);
> +  char *formatted = xvasprintf (fmt, ap);
> +  va_end (ap);
> +
> +  char *msg_with_index = xasprintf ("error at index %i: %s",
> +				    index, formatted);
> +  free (formatted);
> +
> +  if (0)
> +    fprintf (stderr, "%s\n", msg_with_index);
> +  if (*m_err_out == NULL)
> +    *m_err_out = msg_with_index;
> +  else
> +    free (msg_with_index);
> +}
> +
> +/* Attempt to parse the UTF-8 encoded buffer at UTF8_BUF
> +   of the given LENGTH.
> +   If successful, return a non-NULL json::value *.
> +   if there was a problem, return NULL and write an error
> +   message to err_out, which must be freed by the caller.  */
> +
> +value *
> +json::parse_utf8_string (size_t length, const char *utf8_buf,
> +			 char **err_out)
> +{
> +  gcc_assert (err_out);
> +  gcc_assert (*err_out == NULL);
> +
> +  parser p (err_out);
> +  if (!p.add_utf8 (length, utf8_buf, err_out))
> +    return NULL;
> +  value *result = p.parse_value (0);
> +  if (!p.seen_error_p ())
> +    p.require_eof ();
> +  if (p.seen_error_p ())
> +    {
> +      gcc_assert (*err_out);
> +      delete result;
> +      return NULL;
> +    }
> +  return result;
> +}
> +
> +/* Attempt to parse the nil-terminated UTF-8 encoded buffer at
> +   UTF8_BUF.
> +   If successful, return a non-NULL json::value *.
> +   if there was a problem, return NULL and write an error
> +   message to err_out, which must be freed by the caller.  */
> +
> +value *
> +json::parse_utf8_string (const char *utf8, char **err_out)
> +{
> +  return parse_utf8_string (strlen (utf8), utf8, err_out);
> +}
> +
> +\f
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Selftests.  */
> +
> +/* Verify that JV->to_str () equals EXPECTED_JSON.  */
> +
> +static void
> +assert_to_str_eq (const char *expected_json, json::value *jv)
> +{
> +  char *json = jv->to_str ();
> +  ASSERT_STREQ (expected_json, json);
> +  free (json);
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_string ()
> +{
> +  char *err = NULL;
> +  json::value *jv = parse_utf8_string ("\"foo\"", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_STRING, jv->get_kind ());
> +  ASSERT_STREQ ("foo", ((json::string *)jv)->get_string ());
> +  assert_to_str_eq ("\"foo\"", jv);
> +
> +  json::value *clone = jv->clone ();
> +  ASSERT_EQ (JSON_STRING, clone->get_kind ());
> +  ASSERT_STREQ ("foo", ((json::string *)clone)->get_string ());
> +  assert_to_str_eq ("\"foo\"", clone);
> +  delete clone;
> +  delete jv;
> +
> +  const char *contains_quotes = "\"before \\\"quoted\\\" after\"";
> +  jv = parse_utf8_string (contains_quotes, &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_STRING, jv->get_kind ());
> +  ASSERT_STREQ ("before \"quoted\" after", ((json::string *)jv)->get_string
> ());
> +  assert_to_str_eq (contains_quotes, jv);
> +  delete jv;
> +
> +  /* Test of non-ASCII input.  This string is the Japanese word
> "mojibake",
> +     written as C octal-escaped UTF-8.  */
> +  const char *mojibake = (/* Opening quote.  */
> +			  "\""
> +			  /* U+6587 CJK UNIFIED IDEOGRAPH-6587
> +			     UTF-8: 0xE6 0x96 0x87
> +			     C octal escaped UTF-8: \346\226\207.  */
> +			  "\346\226\207"
> +			  /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
> +			     UTF-8: 0xE5 0xAD 0x97
> +			     C octal escaped UTF-8: \345\255\227.  */
> +			  "\345\255\227"
> +			 /* U+5316 CJK UNIFIED IDEOGRAPH-5316
> +			      UTF-8: 0xE5 0x8C 0x96
> +			      C octal escaped UTF-8: \345\214\226.  */
> +			  "\345\214\226"
> +			 /* U+3051 HIRAGANA LETTER KE
> +			      UTF-8: 0xE3 0x81 0x91
> +			      C octal escaped UTF-8: \343\201\221.  */
> +			  "\343\201\221"
> +			  /* Closing quote.  */
> +			  "\"");
> +  jv = parse_utf8_string (mojibake, &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_STRING, jv->get_kind ());
> +  /* Result of get_string should be UTF-8 encoded, without quotes.  */
> +  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226"
> "\343\201\221",
> +		((json::string *)jv)->get_string ());
> +  /* Result of dump should be UTF-8 encoded, with quotes.  */
> +  assert_to_str_eq (mojibake, jv);
> +  delete jv;
> +
> +  /* Test of \u-escaped unicode.  This is "mojibake" again, as above.  */
> +  const char *escaped_unicode = "\"\\u6587\\u5b57\\u5316\\u3051\"";
> +  jv = parse_utf8_string (escaped_unicode, &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_STRING, jv->get_kind ());
> +  /* Result of get_string should be UTF-8 encoded, without quotes.  */
> +  ASSERT_STREQ ("\346\226\207" "\345\255\227" "\345\214\226"
> "\343\201\221",
> +		((json::string *)jv)->get_string ());
> +  /* Result of dump should be UTF-8 encoded, with quotes.  */
> +  assert_to_str_eq (mojibake, jv);
> +  delete jv;
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_number ()
> +{
> +  json::value *jv, *clone;
> +
> +  char *err = NULL;
> +  jv = parse_utf8_string ("42", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (42.0, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("42", jv);
> +  clone = jv->clone ();
> +  ASSERT_EQ (JSON_NUMBER, clone->get_kind ());
> +  delete clone;
> +  delete jv;
> +
> +  /* Negative number.  */
> +  jv = parse_utf8_string ("-17", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (-17.0, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("-17", jv);
> +  delete jv;
> +
> +  /* Decimal.  */
> +  jv = parse_utf8_string ("3.141", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("3.141", jv);
> +  delete jv;
> +
> +  /* Exponents.  */
> +  jv = parse_utf8_string ("3.141e+0", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("3.141", jv);
> +  delete jv;
> +
> +  jv = parse_utf8_string ("42e2", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (4200, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("4200", jv);
> +  delete jv;
> +
> +  jv = parse_utf8_string ("42e-1", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
> +  ASSERT_EQ (4.2, ((json::number *)jv)->get ());
> +  assert_to_str_eq ("4.2", jv);
> +  delete jv;
> +
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_array ()
> +{
> +  json::value *jv, *clone;
> +
> +  char *err = NULL;
> +  jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_EQ (JSON_ARRAY, jv->get_kind ());
> +  json::array *arr = static_cast <json::array *> (jv);
> +  ASSERT_EQ (10, arr->get_length ());
> +  for (int i = 0; i < 10; i++)
> +    {
> +      json::value *element = arr->get (i);
> +      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
> +      ASSERT_EQ (i, ((json::number *)element)->get ());
> +    }
> +  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", jv);
> +
> +  clone = jv->clone ();
> +  ASSERT_EQ (JSON_ARRAY, clone->get_kind ());
> +  arr = static_cast <json::array *> (clone);
> +  ASSERT_EQ (10, arr->get_length ());
> +  for (int i = 0; i < 10; i++)
> +    {
> +      json::value *element = arr->get (i);
> +      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
> +      ASSERT_EQ (i, ((json::number *)element)->get ());
> +    }
> +  assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", clone);
> +  delete clone;
> +
> +  delete jv;
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_object ()
> +{
> +  char *err = NULL;
> +  json::value *jv
> +    = parse_utf8_string ("{\"foo\": \"bar\", \"baz\": [42, null]}", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
> +  json::object *jo = static_cast <json::object *> (jv);
> +
> +  json::value *foo_value = jo->get ("foo");
> +  ASSERT_TRUE (foo_value != NULL);
> +  ASSERT_EQ (JSON_STRING, foo_value->get_kind ());
> +  ASSERT_STREQ ("bar", ((json::string *)foo_value)->get_string ());
> +
> +  json::value *baz_value = jo->get ("baz");
> +  ASSERT_TRUE (baz_value != NULL);
> +  ASSERT_EQ (JSON_ARRAY, baz_value->get_kind ());
> +
> +  json::array *baz_array = (json::array *)baz_value;
> +  ASSERT_EQ (2, baz_array->get_length ());
> +  ASSERT_EQ (42, baz_array->get (0)->as_number ()->get ());
> +  ASSERT_EQ (JSON_NULL, baz_array->get (1)->get_kind ());
> +
> +  // TODO: error-handling
> +  // TODO: partial document
> +
> +  /* We can't use assert_to_str_eq since ordering is not guaranteed.  */
> +
> +  json::value *clone = jv->clone ();
> +  ASSERT_EQ (JSON_OBJECT, clone->get_kind ());
> +  ASSERT_EQ (JSON_STRING, clone->as_object ()->get ("foo")->get_kind ());
> +  delete clone;
> +
> +  delete jv;
> +}
> +
> +/* Verify that the literals "true", "false" and "null" are parsed,
> +   dumped, and are clonable.  */
> +
> +static void
> +test_parse_literals ()
> +{
> +  json::value *jv, *clone;
> +  char *err = NULL;
> +  jv = parse_utf8_string ("true", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  ASSERT_EQ (JSON_TRUE, jv->get_kind ());
> +  assert_to_str_eq ("true", jv);
> +  clone = jv->clone ();
> +  ASSERT_EQ (JSON_TRUE, clone->get_kind ());
> +  delete clone;
> +  delete jv;
> +
> +  jv = parse_utf8_string ("false", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  ASSERT_EQ (JSON_FALSE, jv->get_kind ());
> +  assert_to_str_eq ("false", jv);
> +  clone = jv->clone ();
> +  ASSERT_EQ (JSON_FALSE, clone->get_kind ());
> +  delete clone;
> +  delete jv;
> +
> +  jv = parse_utf8_string ("null", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  ASSERT_EQ (JSON_NULL, jv->get_kind ());
> +  assert_to_str_eq ("null", jv);
> +  clone = jv->clone ();
> +  ASSERT_EQ (JSON_NULL, clone->get_kind ());
> +  delete clone;
> +  delete jv;
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_jsonrpc ()
> +{
> +  char *err = NULL;
> +  const char *request
> +    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
> +       " \"params\": [42, 23], \"id\": 1}");
> +  json::value *jv = parse_utf8_string (request, &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  delete jv;
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_parse_empty_object ()
> +{
> +  char *err = NULL;
> +  json::value *jv = parse_utf8_string ("{}", &err);
> +  ASSERT_EQ (NULL, err);
> +  ASSERT_TRUE (jv != NULL);
> +  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
> +  assert_to_str_eq ("{}", jv);
> +  delete jv;
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_error_empty_string ()
> +{
> +  char *err = NULL;
> +  json::value *jv = parse_utf8_string ("", &err);
> +  ASSERT_STREQ ("error at index 0: unexpected token: EOF", err);
> +  ASSERT_TRUE (jv == NULL);
> +  free (err);
> +}
> +
> +/* FIXME.  */
> +
> +static void
> +test_error_missing_comma ()
> +{
> +  char *err = NULL;
> +  /*                  01234567.  */
> +  const char *json = "[0, 1 2]";
> +  json::value *jv = parse_utf8_string (json, &err);
> +  ASSERT_STREQ ("error at index 6: expected ']'; got number",
> +		err);
> +  // FIXME: unittest the lexer?
> +  ASSERT_TRUE (jv == NULL);
> +  free (err);
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +json_cc_tests ()
> +{
> +  test_parse_string ();
> +  test_parse_number ();
> +  test_parse_array ();
> +  test_parse_object ();
> +  test_parse_literals ();
> +  test_parse_jsonrpc ();
> +  test_parse_empty_object ();
> +  test_error_empty_string ();
> +  test_error_missing_comma ();
> +
> +  /* FIXME: tests for roundtripping (noting that we don't preserve
> +     object key ordering).  */
> +
> +  /* FIXME: cloning.  */
> +}
> +
> +} // namespace selftest
> +
> +#endif /* #if CHECKING_P */
> diff --git a/gcc/json.h b/gcc/json.h
> new file mode 100644
> index 0000000..aedf84a
> --- /dev/null
> +++ b/gcc/json.h
> @@ -0,0 +1,214 @@
> +/* JSON parsing
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +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_JSON_H
> +#define GCC_JSON_H
> +
> +/* Implementation of JSON, a lightweight data-interchange format.
> +
> +   See http://www.json.org/
> +   and
> http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
> +   and https://tools.ietf.org/html/rfc7159
> +
> +   Supports parsing text into a DOM-like tree of json::value *, dumping
> +   json::value * to text.  */
> +
> +namespace json
> +{
> +
> +/* Forward decls of json::value and its subclasses (using indentation
> +   to denote inheritance.  */
> +
> +class value;
> +  class object;
> +  class array;
> +  class number;
> +  class string;
> +  class literal;
> +
> +/* An enum for discriminating the subclasses of json::value.  */
> +
> +enum kind
> +{
> +  /* class json::object.  */
> +  JSON_OBJECT,
> +
> +  /* class json::array.  */
> +  JSON_ARRAY,
> +
> +  /* class json::number.  */
> +  JSON_NUMBER,
> +
> +  /* class json::string.  */
> +  JSON_STRING,
> +
> +  /* class json::literal uses these three values to identify the
> +     particular literal.  */
> +  JSON_TRUE,
> +  JSON_FALSE,
> +  JSON_NULL
> +};
> +
> +/* Base class of JSON value.  */
> +
> +class value
> +{
> + public:
> +  virtual ~value () {}
> +  virtual enum kind get_kind () const = 0;
> +  virtual void print (pretty_printer *pp) const = 0;
> +
> +  /* Create a deep copy of the value, returning a value which must be
> +     deleted by the caller.  */
> +  virtual value *clone () const = 0;
> +
> +  char *to_str () const;
> +  void dump (FILE *) const;
> +
> +  /* Methods for dynamically casting a value to one of the subclasses,
> +     returning NULL if the value is of the wrong kind.  */
> +  const object *as_object () const;
> +  const array *as_array () const;
> +  const number *as_number () const;
> +  const string *as_string () const;
> +
> +  /* Convenience accessors for attempting to perform key/value lookups
> +     on this value as if it were an json::object.
> +
> +     On success, return true and write the value to OUT_VALUE.
> +     On failure, return false and write an error message to OUT_ERR
> +     (which must be freed by the caller).  */
> +  bool get_value_by_key (const char *name, const value *&out_value,
> +			 char *&out_err) const;
> +  bool get_int_by_key (const char *name, int &out_value, char *&out_err)
> const;
> +  bool get_string_by_key (const char *name, const char *&out_value,
> +			  char *&out_err) const;
> +  bool get_array_by_key (const char *name, const array *&out_value,
> +			 char *&out_err) const;
> +
> +  /* As above, but the key is optional.  THIS must still be an object,
> +     though.  */
> +  bool get_optional_value_by_key (const char *name, const value
> *&out_value,
> +				  char *&out_err) const;
> +  bool get_optional_string_by_key (const char *name, const char
> *&out_value,
> +				   char *&out_err) const;
> +};
> +
> +/* Subclass of value for objects: an unordered collection of
> +   key/value pairs.  */
> +
> +class object : public value
> +{
> + public:
> +  ~object ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +  value *clone () const FINAL OVERRIDE;
> +
> +  value *get (const char *key) const;
> +  value *get_if_nonnull (const char *key) const;
> +
> +  void set (const char *key, value *v);
> +
> + private:
> +  typedef hash_map <char *, value *,
> +    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
> +  map_t m_map;
> +};
> +
> +/* Subclass of value for arrays.  */
> +
> +class array : public value
> +{
> + public:
> +  ~array ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +  value *clone () const FINAL OVERRIDE;
> +
> +  unsigned get_length () const { return m_elements.length (); }
> +  value *get (int idx) const { return m_elements[idx]; }
> +  void append (value *v) { m_elements.safe_push (v); }
> +
> + private:
> +  auto_vec<value *> m_elements;
> +};
> +
> +/* Subclass of value for numbers.  */
> +
> +class number : public value
> +{
> + public:
> +  number (double value) : m_value (value) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +  value *clone () const FINAL OVERRIDE;
> +
> +  double get () const { return m_value; }
> +
> + private:
> +  double m_value;
> +};
> +
> +/* Subclass of value for strings.  */
> +
> +class string : public value
> +{
> + public:
> +  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
> +  ~string () { free (m_utf8); }
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +  value *clone () const FINAL OVERRIDE;
> +
> +  const char *get_string () const { return m_utf8; }
> +
> + private:
> +  char *m_utf8;
> +};
> +
> +/* Subclass of value for the three JSON literals "true", "false",
> +   and "null".  */
> +
> +class literal : public value
> +{
> + public:
> +  literal (enum kind kind) : m_kind (kind) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +  value *clone () const FINAL OVERRIDE;
> +
> + private:
> +  enum kind m_kind;
> +};
> +
> +/* Declarations for parsing JSON to a json::value * tree.  */
> +
> +extern value *parse_utf8_string (size_t length, const char *utf8_buf,
> +				 char **err_out);
> +extern value *parse_utf8_string (const char *utf8, char **err_out);
> +
> +} // namespace json
> +
> +#endif  /* GCC_JSON_H  */
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index fe221ff..36879cf 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -70,6 +70,7 @@ selftest::run_tests ()
>    fibonacci_heap_c_tests ();
>    typed_splay_tree_c_tests ();
>    unique_ptr_tests_cc_tests ();
> +  json_cc_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index e3117c6..2a912d8 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -199,6 +199,7 @@ extern void ggc_tests_c_tests ();
>  extern void hash_map_tests_c_tests ();
>  extern void hash_set_tests_c_tests ();
>  extern void input_c_tests ();
> +extern void json_cc_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
>  extern void rtl_tests_c_tests ();
> --
> 1.8.5.3
>
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 02/10] Add JSON implementation
  2018-05-30 17:31   ` Eric Gallager
@ 2018-05-30 17:32     ` David Malcolm
  2018-05-30 21:23       ` Eric Gallager
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-05-30 17:32 UTC (permalink / raw)
  To: Eric Gallager; +Cc: gcc-patches

On Wed, 2018-05-30 at 13:25 -0400, Eric Gallager wrote:
> On 5/29/18, David Malcolm <dmalcolm@redhat.com> wrote:
> > This patch is the JSON patch I posted last year;
> > it adds support to gcc for reading and writing JSON,
> > based on DOM-like trees of json::value instances.
> > 
> > This is overkill for what's needed by the rest of the
> > patch kit (which just needs to be able to write JSON),
> > but this code already existed, so I'm using it for now.
> > 
> 
> I think I remember you posting this last year, but I forget where in
> the archives it is. Could you post a link to the thread from last
> year
> just for reference? Thanks.

It was "[PATCH 03/22] Add JSON implementation"
  https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00417.html
as part of:
  "[PATCH 00/22] RFC: integrated 3rd-party static analysis support"
   https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00416.html

[...snip...]

Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 02/10] Add JSON implementation
  2018-05-30 17:32     ` David Malcolm
@ 2018-05-30 21:23       ` Eric Gallager
  0 siblings, 0 replies; 80+ messages in thread
From: Eric Gallager @ 2018-05-30 21:23 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 5/30/18, David Malcolm <dmalcolm@redhat.com> wrote:
> On Wed, 2018-05-30 at 13:25 -0400, Eric Gallager wrote:
>> On 5/29/18, David Malcolm <dmalcolm@redhat.com> wrote:
>> > This patch is the JSON patch I posted last year;
>> > it adds support to gcc for reading and writing JSON,
>> > based on DOM-like trees of json::value instances.
>> >
>> > This is overkill for what's needed by the rest of the
>> > patch kit (which just needs to be able to write JSON),
>> > but this code already existed, so I'm using it for now.
>> >
>>
>> I think I remember you posting this last year, but I forget where in
>> the archives it is. Could you post a link to the thread from last
>> year
>> just for reference? Thanks.
>
> It was "[PATCH 03/22] Add JSON implementation"
>   https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00417.html
> as part of:
>   "[PATCH 00/22] RFC: integrated 3rd-party static analysis support"
>    https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00416.html
>
> [...snip...]
>
> Dave
>

Right, thanks! Considering that the static analysis support is a
different thing, I almost wonder if it's worth adding the JSON code
separately from either that or this, so it can be ready whenever
something else needs it...

Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-05-29 20:32 ` [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc David Malcolm
@ 2018-06-01  9:51   ` Richard Biener
  2018-06-01 13:40     ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-01  9:51 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This was an experiment to try to capture information on a
> loop optimization.
>
> gcc/ChangeLog:
>         * gimple-loop-interchange.cc (should_interchange_loops): Add
>         optinfo note when interchange gives better data locality behavior.
>         (tree_loop_interchange::interchange): Add OPTINFO_SCOPE.
>         Add optinfo for successful and unsuccessful interchanges.
>         (prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
>         optinfo note.
>         (pass_linterchange::execute): Add OPTINFO_SCOPE.
> ---
>  gcc/gimple-loop-interchange.cc | 36 +++++++++++++++++++++++++++++++++++-
>  1 file changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
> index eb35263..cd32288 100644
> --- a/gcc/gimple-loop-interchange.cc
> +++ b/gcc/gimple-loop-interchange.cc
> @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned i_idx, unsigned o_idx,
>    ratio = innermost_loops_p ? INNER_STRIDE_RATIO : OUTER_STRIDE_RATIO;
>    /* Do interchange if it gives better data locality behavior.  */
>    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides, ratio)))
> -    return true;
> +    {
> +      if (optinfo_enabled_p ())
> +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
> +         << "interchange gives better data locality behavior: "
> +         << "iloop_strides: "
> +         << decu (iloop_strides)
> +         << " > (oloop_strides: "
> +         << decu (oloop_strides)
> +         << " * ratio: "
> +         << decu (ratio)
> +         << ")";

Just randomly inside the thread.

NOOOOOOOOOO!

:/

Please do _not_ add more stream-like APIs.  How do you expect
translators to deal with those?

Yes, I'm aware of the graphite-* ones and I dislike those very much.

What's wrong with the existing dump API?

Richard.

> +      return true;
> +    }
>    if (wi::gtu_p (iloop_strides, oloop_strides))
>      {
>        /* Or it creates more invariant memory references.  */
> @@ -1578,6 +1590,8 @@ bool
>  tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
>                                     vec<ddr_p> ddrs)
>  {
> +  OPTINFO_SCOPE ("tree_loop_interchange::interchange", m_loop_nest[0]);
> +
>    location_t loc = find_loop_location (m_loop_nest[0]);
>    bool changed_p = false;
>    /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
> @@ -1628,6 +1642,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
>             fprintf (dump_file,
>                      "Loop_pair<outer:%d, inner:%d> is interchanged\n\n",
>                      oloop.m_loop->num, iloop.m_loop->num);
> +         if (optinfo_enabled_p ())
> +           OPTINFO_SUCCESS (oloop.m_loop)
> +             << optinfo_printf ("Loop_pair<outer:%d, inner:%d> is interchanged",
> +                                oloop.m_loop->num, iloop.m_loop->num);
>
>           changed_p = true;
>           interchange_loops (iloop, oloop);
> @@ -1641,6 +1659,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
>             fprintf (dump_file,
>                      "Loop_pair<outer:%d, inner:%d> is not interchanged\n\n",
>                      oloop.m_loop->num, iloop.m_loop->num);
> +         if (optinfo_enabled_p ())
> +           OPTINFO_FAILURE (oloop.m_loop)
> +             << optinfo_printf ("Loop_pair<outer:%d, inner:%d> is not interchanged",
> +                                oloop.m_loop->num, iloop.m_loop->num);
>         }
>      }
>    simple_dce_from_worklist (m_dce_seeds);
> @@ -1648,6 +1670,9 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
>    if (changed_p)
>      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                      "loops interchanged in loop nest\n");
> +  if (optinfo_enabled_p ())
> +    OPTINFO_SUCCESS (m_loop_nest[0])
> +      << "loops interchanged in loop nest";
>
>    return changed_p;
>  }
> @@ -1971,6 +1996,8 @@ static bool
>  prepare_perfect_loop_nest (struct loop *loop, vec<loop_p> *loop_nest,
>                            vec<data_reference_p> *datarefs, vec<ddr_p> *ddrs)
>  {
> +  OPTINFO_SCOPE ("prepare_perfect_loop_nest", loop);
> +
>    struct loop *start_loop = NULL, *innermost = loop;
>    struct loop *outermost = loops_for_fn (cfun)->tree_root;
>
> @@ -2029,6 +2056,12 @@ prepare_perfect_loop_nest (struct loop *loop, vec<loop_p> *loop_nest,
>           fprintf (dump_file,
>                    "\nConsider loop interchange for loop_nest<%d - %d>\n",
>                    start_loop->num, innermost->num);
> +       if (optinfo_enabled_p ())
> +         {
> +           OPTINFO_NOTE (start_loop)
> +             << optinfo_printf ("consider loop interchange for loop_nest<%d - %d>",
> +                                start_loop->num, innermost->num);
> +         }
>
>         if (loop != start_loop)
>           prune_access_strides_not_in_loop (start_loop, innermost, *datarefs);
> @@ -2061,6 +2094,7 @@ pass_linterchange::execute (function *fun)
>    struct loop *loop;
>    FOR_EACH_LOOP (loop, LI_ONLY_INNERMOST)
>      {
> +      OPTINFO_SCOPE ("considering loop for interchange", loop);
>        vec<loop_p> loop_nest = vNULL;
>        vec<data_reference_p> datarefs = vNULL;
>        vec<ddr_p> ddrs = vNULL;
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 01/10] Convert dump and optgroup flags to enums
  2018-05-29 20:35 ` [PATCH 01/10] Convert dump and optgroup flags to enums David Malcolm
@ 2018-06-01 10:00   ` Richard Biener
  2018-06-05  8:44     ` Trevor Saunders
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-01 10:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, May 29, 2018 at 10:32 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> The dump machinery uses "int" in a few places, for two different
> sets of bitmasks.
>
> This patch makes things more self-documenting and type-safe by using
> a new pair of enums: one for the dump_flags_t and another for the
> optgroup_flags.

Great!  This should also make them accessible in gdb w/o using -g3.

> This requires adding some overloaded bit operations to the enums
> in question, which, in this patch is done for each enum .  If the basic
> idea is OK, should I add a template for this?  (with some kind of
> magic to express that bitmasking operations are only supported on
> certain opt-in enums).

Does C++ allow > int enums?  I think we want some way of knowing
when either enum exceeds int (dump_flags_t was already uint64_t
but you now make it effectively int again).  That is, general wrapping
for enum ops should chose an appropriate unsigned integer for
the operation.  So yes, a common implementation looks useful to me.

I think this patch is independently useful.

Thanks,
Richard.

>
> gcc/c-family/ChangeLog:
>         * c-pretty-print.c (c_pretty_printer::statement): Use TDF_NONE
>         rather than 0.
>
> gcc/ChangeLog:
>         * cfg.c (debug): Use TDF_NONE rather than 0.
>         * cfghooks.c (debug): Likewise.
>         * dumpfile.c (DUMP_FILE_INFO): Likewise; also for OPTGROUP.
>         (struct dump_option_value_info): Convert to...
>         (struct kv_pair): ...this template type.
>         (dump_options): Convert to kv_pair<dump_flags_t>; use TDF_NONE
>         rather than 0.
>         (optinfo_verbosity_options): Likewise.
>         (optgroup_options): Convert to kv_pair<optgroup_flags_t>; use
>         OPTGROUP_NONE.
>         (gcc::dump_manager::dump_register): Use optgroup_flags_t rather
>         than int for "optgroup_flags" param.
>         (dump_generic_expr_loc): Use dump_flags_t rather than int for
>         "dump_kind" param.
>         (dump_finish): Use TDF_NONE rather than 0.
>         (gcc::dump_manager::opt_info_enable_passes): Use optgroup_flags_t
>         rather than int for "optgroup_flags" param.  Use TDF_NONE rather
>         than 0.  Update for change to option_ptr.
>         (opt_info_switch_p_1): Convert "optgroup_flags" param from int *
>         to optgroup_flags_t *.  Use TDF_NONE and OPTGROUP_NONE rather than
>         0.  Update for changes to optinfo_verbosity_options and
>         optgroup_options.
>         (opt_info_switch_p): Convert optgroup_flags from int to
>         optgroup_flags_t.
>         * dumpfile.h (TDF_ADDRESS, TDF_SLIM, TDF_RAW, TDF_DETAILS,
>         TDF_STATS, TDF_BLOCKS, TDF_VOPS, TDF_LINENO, TDF_UID)
>         TDF_STMTADDR, TDF_GRAPH, TDF_MEMSYMS, TDF_RHS_ONLY, TDF_ASMNAME,
>         TDF_EH, TDF_NOUID, TDF_ALIAS, TDF_ENUMERATE_LOCALS, TDF_CSELIB,
>         TDF_SCEV, TDF_GIMPLE, TDF_FOLDING, MSG_OPTIMIZED_LOCATIONS,
>         MSG_MISSED_OPTIMIZATION, MSG_NOTE, MSG_ALL, TDF_COMPARE_DEBUG,
>         TDF_NONE): Convert from macros to...
>         (enum dump_flag): ...this new enum.
>         (dump_flags_t): Update to use enum.
>         (operator|, operator&, operator~, operator|=, operator&=):
>         Implement for dump_flags_t.
>         (OPTGROUP_NONE, OPTGROUP_IPA, OPTGROUP_LOOP, OPTGROUP_INLINE,
>         OPTGROUP_OMP, OPTGROUP_VEC, OPTGROUP_OTHER, OPTGROUP_ALL):
>         Convert from macros to...
>         (enum optgroup_flag): ...this new enum.
>         (optgroup_flags_t): New typedef.
>         (operator|, operator|=): Implement for optgroup_flags_t.
>         (struct dump_file_info): Convert field "alt_flags" to
>         dump_flags_t.  Convert field "optgroup_flags" to
>         optgroup_flags_t.
>         (dump_register): Convert param "optgroup_flags" to
>         optgroup_flags_t.
>         (opt_info_enable_passes): Likewise.
>         * early-remat.c (early_remat::dump_edge_list): Use TDF_NONE rather
>         than 0.
>         * gimple-pretty-print.c (debug): Likewise.
>         * gimple-ssa-store-merging.c (bswap_replace): Likewise.
>         (merged_store_group::apply_stores): Likewise.
>         * gimple-ssa-strength-reduction.c (insert_initializers): Likewise.
>         * gimple.c (verify_gimple_pp): Likewise.
>         * passes.c (pass_manager::register_one_dump_file): Convert
>         local "optgroup_flags" to optgroup_flags_t.
>         * print-tree.c (print_node): Use TDF_NONE rather than 0.
>         (debug): Likewise.
>         (debug_body): Likewise.
>         * tree-pass.h (struct pass_data): Convert field "optgroup_flags"
>         to optgroup_flags_t.
>         * tree-pretty-print.c (print_struct_decl): Use TDF_NONE rather
>         than 0.
>         * tree-ssa-math-opts.c (convert_mult_to_fma_1): Likewise.
>         (convert_mult_to_fma): Likewise.
>         * tree-ssa-reassoc.c (undistribute_ops_list): Likewise.
>         * tree-ssa-sccvn.c (vn_eliminate): Likewise.
>         * tree-vect-data-refs.c (dump_lower_bound): Convert param
>         "dump_kind" to dump_flags_t.
> ---
>  gcc/c-family/c-pretty-print.c       |   2 +-
>  gcc/cfg.c                           |   4 +-
>  gcc/cfghooks.c                      |   2 +-
>  gcc/dumpfile.c                      |  56 ++++-----
>  gcc/dumpfile.h                      | 227 +++++++++++++++++++++++++++---------
>  gcc/early-remat.c                   |   2 +-
>  gcc/gimple-pretty-print.c           |   2 +-
>  gcc/gimple-ssa-store-merging.c      |   6 +-
>  gcc/gimple-ssa-strength-reduction.c |   2 +-
>  gcc/gimple.c                        |   2 +-
>  gcc/passes.c                        |   2 +-
>  gcc/print-tree.c                    |   7 +-
>  gcc/tree-pass.h                     |   2 +-
>  gcc/tree-pretty-print.c             |   2 +-
>  gcc/tree-ssa-math-opts.c            |   4 +-
>  gcc/tree-ssa-reassoc.c              |   2 +-
>  gcc/tree-ssa-sccvn.c                |   2 +-
>  gcc/tree-vect-data-refs.c           |   2 +-
>  18 files changed, 222 insertions(+), 106 deletions(-)
>
> diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
> index dc76c99..efb41c5 100644
> --- a/gcc/c-family/c-pretty-print.c
> +++ b/gcc/c-family/c-pretty-print.c
> @@ -2341,7 +2341,7 @@ c_pretty_printer::statement (tree stmt)
>    if (pp_needs_newline (this))
>      pp_newline_and_indent (this, 0);
>
> -  dump_generic_node (this, stmt, pp_indentation (this), 0, true);
> +  dump_generic_node (this, stmt, pp_indentation (this), TDF_NONE, true);
>  }
>
>
> diff --git a/gcc/cfg.c b/gcc/cfg.c
> index 11026e7..6d55516 100644
> --- a/gcc/cfg.c
> +++ b/gcc/cfg.c
> @@ -545,8 +545,8 @@ DEBUG_FUNCTION void
>  debug (edge_def &ref)
>  {
>    /* FIXME (crowl): Is this desireable?  */
> -  dump_edge_info (stderr, &ref, 0, false);
> -  dump_edge_info (stderr, &ref, 0, true);
> +  dump_edge_info (stderr, &ref, TDF_NONE, false);
> +  dump_edge_info (stderr, &ref, TDF_NONE, true);
>  }
>
>  DEBUG_FUNCTION void
> diff --git a/gcc/cfghooks.c b/gcc/cfghooks.c
> index 87d864c..ea106e0 100644
> --- a/gcc/cfghooks.c
> +++ b/gcc/cfghooks.c
> @@ -288,7 +288,7 @@ dump_bb (FILE *outf, basic_block bb, int indent, dump_flags_t flags)
>  DEBUG_FUNCTION void
>  debug (basic_block_def &ref)
>  {
> -  dump_bb (stderr, &ref, 0, 0);
> +  dump_bb (stderr, &ref, 0, TDF_NONE);
>  }
>
>  DEBUG_FUNCTION void
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 0acc7a9..6af1445 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -50,8 +50,8 @@ const char *dump_file_name;
>  dump_flags_t dump_flags;
>
>  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
> -  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, 0, 0, 0, 0, 0, num, \
> -   false, false}
> +  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
> +   OPTGROUP_NONE, 0, 0, num, false, false}
>
>  /* Table of tree dump switches. This must be consistent with the
>     TREE_DUMP_INDEX enumeration in dumpfile.h.  */
> @@ -74,15 +74,16 @@ static struct dump_file_info dump_files[TDI_end] =
>  };
>
>  /* Define a name->number mapping for a dump flag value.  */
> -struct dump_option_value_info
> +template <typename ValueType>
> +struct kv_pair
>  {
>    const char *const name;      /* the name of the value */
> -  const dump_flags_t value;    /* the value of the name */
> +  const ValueType value;       /* the value of the name */
>  };
>
>  /* Table of dump options. This must be consistent with the TDF_* flags
>     in dumpfile.h and opt_info_options below. */
> -static const struct dump_option_value_info dump_options[] =
> +static const kv_pair<dump_flags_t> dump_options[] =
>  {
>    {"address", TDF_ADDRESS},
>    {"asmname", TDF_ASMNAME},
> @@ -114,23 +115,23 @@ static const struct dump_option_value_info dump_options[] =
>    {"all", dump_flags_t (~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_GRAPH
>                         | TDF_STMTADDR | TDF_RHS_ONLY | TDF_NOUID
>                         | TDF_ENUMERATE_LOCALS | TDF_SCEV | TDF_GIMPLE))},
> -  {NULL, 0}
> +  {NULL, TDF_NONE}
>  };
>
>  /* A subset of the dump_options table which is used for -fopt-info
>     types. This must be consistent with the MSG_* flags in dumpfile.h.
>   */
> -static const struct dump_option_value_info optinfo_verbosity_options[] =
> +static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
>  {
>    {"optimized", MSG_OPTIMIZED_LOCATIONS},
>    {"missed", MSG_MISSED_OPTIMIZATION},
>    {"note", MSG_NOTE},
>    {"all", MSG_ALL},
> -  {NULL, 0}
> +  {NULL, TDF_NONE}
>  };
>
>  /* Flags used for -fopt-info groups.  */
> -static const struct dump_option_value_info optgroup_options[] =
> +static const kv_pair<optgroup_flags_t> optgroup_options[] =
>  {
>    {"ipa", OPTGROUP_IPA},
>    {"loop", OPTGROUP_LOOP},
> @@ -138,7 +139,7 @@ static const struct dump_option_value_info optgroup_options[] =
>    {"omp", OPTGROUP_OMP},
>    {"vec", OPTGROUP_VEC},
>    {"optall", OPTGROUP_ALL},
> -  {NULL, 0}
> +  {NULL, OPTGROUP_NONE}
>  };
>
>  gcc::dump_manager::dump_manager ():
> @@ -173,7 +174,8 @@ gcc::dump_manager::~dump_manager ()
>  unsigned int
>  gcc::dump_manager::
>  dump_register (const char *suffix, const char *swtch, const char *glob,
> -              dump_kind dkind, int optgroup_flags, bool take_ownership)
> +              dump_kind dkind, optgroup_flags_t optgroup_flags,
> +              bool take_ownership)
>  {
>    int num = m_next_dump++;
>
> @@ -410,7 +412,7 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>     location.  */
>
>  void
> -dump_generic_expr_loc (int dump_kind, source_location loc,
> +dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
>                        dump_flags_t extra_dump_flags, tree t)
>  {
>    if (dump_file && (dump_kind & pflags))
> @@ -575,9 +577,9 @@ dump_finish (int phase)
>    dfi->pstream = NULL;
>    dump_file = NULL;
>    alt_dump_file = NULL;
> -  dump_flags = TDI_none;
> -  alt_flags = 0;
> -  pflags = 0;
> +  dump_flags = TDF_NONE;
> +  alt_flags = TDF_NONE;
> +  pflags = TDF_NONE;
>  }
>
>  /* Begin a tree dump for PHASE. Stores any user supplied flag in
> @@ -750,7 +752,7 @@ dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename)
>
>  int
>  gcc::dump_manager::
> -opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
> +opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
>                         const char *filename)
>  {
>    int n = 0;
> @@ -817,11 +819,11 @@ dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob)
>      return 0;
>
>    ptr = option_value;
> -  flags = 0;
> +  flags = TDF_NONE;
>
>    while (*ptr)
>      {
> -      const struct dump_option_value_info *option_ptr;
> +      const struct kv_pair<dump_flags_t> *option_ptr;
>        const char *end_ptr;
>        const char *eq_ptr;
>        unsigned length;
> @@ -903,8 +905,8 @@ dump_switch_p (const char *arg)
>     and filename.  Return non-zero if it is a recognized switch.  */
>
>  static int
> -opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
> -                     char **filename)
> +opt_info_switch_p_1 (const char *arg, dump_flags_t *flags,
> +                    optgroup_flags_t *optgroup_flags, char **filename)
>  {
>    const char *option_value;
>    const char *ptr;
> @@ -913,15 +915,14 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
>    ptr = option_value;
>
>    *filename = NULL;
> -  *flags = 0;
> -  *optgroup_flags = 0;
> +  *flags = TDF_NONE;
> +  *optgroup_flags = OPTGROUP_NONE;
>
>    if (!ptr)
>      return 1;       /* Handle '-fopt-info' without any additional options.  */
>
>    while (*ptr)
>      {
> -      const struct dump_option_value_info *option_ptr;
>        const char *end_ptr;
>        const char *eq_ptr;
>        unsigned length;
> @@ -938,8 +939,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
>         end_ptr = ptr + strlen (ptr);
>        length = end_ptr - ptr;
>
> -      for (option_ptr = optinfo_verbosity_options; option_ptr->name;
> -           option_ptr++)
> +      for (const kv_pair<dump_flags_t> *option_ptr = optinfo_verbosity_options;
> +          option_ptr->name; option_ptr++)
>         if (strlen (option_ptr->name) == length
>             && !memcmp (option_ptr->name, ptr, length))
>            {
> @@ -947,7 +948,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
>             goto found;
>            }
>
> -      for (option_ptr = optgroup_options; option_ptr->name; option_ptr++)
> +      for (const kv_pair<optgroup_flags_t> *option_ptr = optgroup_options;
> +          option_ptr->name; option_ptr++)
>         if (strlen (option_ptr->name) == length
>             && !memcmp (option_ptr->name, ptr, length))
>            {
> @@ -982,7 +984,7 @@ int
>  opt_info_switch_p (const char *arg)
>  {
>    dump_flags_t flags;
> -  int optgroup_flags;
> +  optgroup_flags_t optgroup_flags;
>    char *filename;
>    static char *file_seen = NULL;
>    gcc::dump_manager *dumps = g->get_dumps ();
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 21803a6..b5582f7 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -58,65 +58,177 @@ enum dump_kind
>     the DUMP_OPTIONS array in dumpfile.c. The TDF_* flags coexist with
>     MSG_* flags (for -fopt-info) and the bit values must be chosen to
>     allow that.  */
> -#define TDF_ADDRESS    (1 << 0)        /* dump node addresses */
> -#define TDF_SLIM       (1 << 1)        /* don't go wild following links */
> -#define TDF_RAW                (1 << 2)        /* don't unparse the function */
> -#define TDF_DETAILS    (1 << 3)        /* show more detailed info about
> -                                          each pass */
> -#define TDF_STATS      (1 << 4)        /* dump various statistics about
> -                                          each pass */
> -#define TDF_BLOCKS     (1 << 5)        /* display basic block boundaries */
> -#define TDF_VOPS       (1 << 6)        /* display virtual operands */
> -#define TDF_LINENO     (1 << 7)        /* display statement line numbers */
> -#define TDF_UID                (1 << 8)        /* display decl UIDs */
> -
> -#define TDF_STMTADDR   (1 << 9)       /* Address of stmt.  */
> -
> -#define TDF_GRAPH      (1 << 10)       /* a graph dump is being emitted */
> -#define TDF_MEMSYMS    (1 << 11)       /* display memory symbols in expr.
> -                                          Implies TDF_VOPS.  */
> -
> -#define TDF_RHS_ONLY   (1 << 12)       /* a flag to only print the RHS of
> -                                          a gimple stmt.  */
> -#define TDF_ASMNAME    (1 << 13)       /* display asm names of decls  */
> -#define TDF_EH         (1 << 14)       /* display EH region number
> -                                          holding this gimple statement.  */
> -#define TDF_NOUID      (1 << 15)       /* omit UIDs from dumps.  */
> -#define TDF_ALIAS      (1 << 16)       /* display alias information  */
> -#define TDF_ENUMERATE_LOCALS (1 << 17) /* Enumerate locals by uid.  */
> -#define TDF_CSELIB     (1 << 18)       /* Dump cselib details.  */
> -#define TDF_SCEV       (1 << 19)       /* Dump SCEV details.  */
> -#define TDF_GIMPLE     (1 << 20)       /* Dump in GIMPLE FE syntax  */
> -#define TDF_FOLDING    (1 << 21)       /* Dump folding details.  */
> -#define MSG_OPTIMIZED_LOCATIONS         (1 << 22)  /* -fopt-info optimized sources */
> -#define MSG_MISSED_OPTIMIZATION         (1 << 23)  /* missed opportunities */
> -#define MSG_NOTE                (1 << 24)  /* general optimization info */
> -#define MSG_ALL                (MSG_OPTIMIZED_LOCATIONS | MSG_MISSED_OPTIMIZATION \
> -                        | MSG_NOTE)
> -#define TDF_COMPARE_DEBUG (1 << 25)    /* Dumping for -fcompare-debug.  */
> -
> -
> -/* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
> -
> -#define TDF_NONE 0
> +enum dump_flag
> +{
> +  /* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
> +  TDF_NONE  = 0,
> +
> +  /* Dump node addresses.  */
> +  TDF_ADDRESS = (1 << 0),
> +
> +  /* Don't go wild following links.  */
> +  TDF_SLIM = (1 << 1),
> +
> +  /* Don't unparse the function.  */
> +  TDF_RAW = (1 << 2),
> +
> +  /* Show more detailed info about each pass.  */
> +  TDF_DETAILS = (1 << 3),
> +
> +  /* Dump various statistics about each pass.  */
> +  TDF_STATS = (1 << 4),
> +
> +  /* Display basic block boundaries.  */
> +  TDF_BLOCKS = (1 << 5),
> +
> +  /* Display virtual operands.  */
> +  TDF_VOPS = (1 << 6),
> +
> +  /* Display statement line numbers.  */
> +  TDF_LINENO = (1 << 7),
> +
> +  /* Display decl UIDs.  */
> +  TDF_UID  = (1 << 8),
> +
> +  /* Address of stmt.  */
> +  TDF_STMTADDR = (1 << 9),
> +
> +  /* A graph dump is being emitted.  */
> +  TDF_GRAPH = (1 << 10),
> +
> +  /* Display memory symbols in expr.
> +     Implies TDF_VOPS.  */
> +  TDF_MEMSYMS = (1 << 11),
> +
> +  /* A flag to only print the RHS of a gimple stmt.  */
> +  TDF_RHS_ONLY = (1 << 12),
> +
> +  /* Display asm names of decls.  */
> +  TDF_ASMNAME = (1 << 13),
> +
> +  /* Display EH region number holding this gimple statement.  */
> +  TDF_EH  = (1 << 14),
> +
> +  /* Omit UIDs from dumps.  */
> +  TDF_NOUID = (1 << 15),
> +
> +  /* Display alias information.  */
> +  TDF_ALIAS = (1 << 16),
> +
> +  /* Enumerate locals by uid.  */
> +  TDF_ENUMERATE_LOCALS = (1 << 17),
> +
> +  /* Dump cselib details.  */
> +  TDF_CSELIB = (1 << 18),
> +
> +  /* Dump SCEV details.  */
> +  TDF_SCEV = (1 << 19),
> +
> +  /* Dump in GIMPLE FE syntax  */
> +  TDF_GIMPLE = (1 << 20),
> +
> +  /* Dump folding details.  */
> +  TDF_FOLDING = (1 << 21),
> +
> +  /* -fopt-info optimized sources.  */
> +  MSG_OPTIMIZED_LOCATIONS = (1 << 22),
> +
> +  /* Missed opportunities.  */
> +  MSG_MISSED_OPTIMIZATION = (1 << 23),
> +
> +  /* General optimization info.  */
> +  MSG_NOTE = (1 << 24),
> +
> +  MSG_ALL = (MSG_OPTIMIZED_LOCATIONS
> +            | MSG_MISSED_OPTIMIZATION
> +            | MSG_NOTE),
> +
> +  /* Dumping for -fcompare-debug.  */
> +  TDF_COMPARE_DEBUG = (1 << 25),
> +};
> +
> +/* Dump flags type.  */
> +
> +typedef enum dump_flag dump_flags_t;
> +
> +// FIXME: template
> +// FIXME: underlying storage type?
> +static inline dump_flags_t
> +operator| (dump_flags_t lhs, dump_flags_t rhs)
> +{
> +  return (dump_flags_t)((int)lhs | (int)rhs);
> +}
> +
> +static inline dump_flags_t
> +operator& (dump_flags_t lhs, dump_flags_t rhs)
> +{
> +  return (dump_flags_t)((int)lhs & (int)rhs);
> +}
> +
> +static inline dump_flags_t
> +operator~ (dump_flags_t flags)
> +{
> +  return (dump_flags_t)~((int)flags);
> +}
> +
> +static inline dump_flags_t &
> +operator|= (dump_flags_t &lhs, dump_flags_t rhs)
> +{
> +  lhs = (dump_flags_t)((int)lhs | (int)rhs);
> +  return lhs;
> +}
> +
> +static inline dump_flags_t &
> +operator&= (dump_flags_t &lhs, dump_flags_t rhs)
> +{
> +  lhs = (dump_flags_t)((int)lhs & (int)rhs);
> +  return lhs;
> +}
>
>  /* Flags to control high-level -fopt-info dumps.  Usually these flags
>     define a group of passes.  An optimization pass can be part of
>     multiple groups.  */
> -#define OPTGROUP_NONE       (0)
> -#define OPTGROUP_IPA        (1 << 1)   /* IPA optimization passes */
> -#define OPTGROUP_LOOP       (1 << 2)   /* Loop optimization passes */
> -#define OPTGROUP_INLINE             (1 << 3)   /* Inlining passes */
> -#define OPTGROUP_OMP        (1 << 4)   /* OMP (Offloading and Multi
> -                                          Processing) transformations */
> -#define OPTGROUP_VEC        (1 << 5)   /* Vectorization passes */
> -#define OPTGROUP_OTHER      (1 << 6)   /* All other passes */
> -#define OPTGROUP_ALL        (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE \
> -                             | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
>
> -/* Dump flags type.  */
> +enum optgroup_flag
> +{
> +  OPTGROUP_NONE = 0,
> +
> +  /* IPA optimization passes */
> +  OPTGROUP_IPA  = (1 << 1),
> +
> +  /* Loop optimization passes */
> +  OPTGROUP_LOOP = (1 << 2),
> +
> +  /* Inlining passes */
> +  OPTGROUP_INLINE = (1 << 3),
>
> -typedef uint64_t dump_flags_t;
> +  /* OMP (Offloading and Multi Processing) transformations */
> +  OPTGROUP_OMP = (1 << 4),
> +
> +  /* Vectorization passes */
> +  OPTGROUP_VEC = (1 << 5),
> +
> +  /* All other passes */
> +  OPTGROUP_OTHER = (1 << 6),
> +
> +  OPTGROUP_ALL = (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE
> +                 | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
> +};
> +
> +typedef enum optgroup_flag optgroup_flags_t;
> +
> +static inline optgroup_flags_t
> +operator| (optgroup_flags_t lhs, optgroup_flags_t rhs)
> +{
> +  return (optgroup_flags_t)((int)lhs | (int)rhs);
> +}
> +
> +static inline optgroup_flags_t &
> +operator|= (optgroup_flags_t &lhs, optgroup_flags_t rhs)
> +{
> +  lhs = (optgroup_flags_t)((int)lhs | (int)rhs);
> +  return lhs;
> +}
>
>  /* Define a tree dump switch.  */
>  struct dump_file_info
> @@ -140,9 +252,9 @@ struct dump_file_info
>    /* Dump flags.  */
>    dump_flags_t pflags;
>    /* A pass flags for -fopt-info.  */
> -  int alt_flags;
> +  dump_flags_t alt_flags;
>    /* Flags for -fopt-info given by a user.  */
> -  int optgroup_flags;
> +  optgroup_flags_t optgroup_flags;
>    /* State of pass-specific stream.  */
>    int pstate;
>    /* State of the -fopt-info stream.  */
> @@ -214,7 +326,8 @@ public:
>       SUFFIX, SWTCH, and GLOB. */
>    unsigned int
>    dump_register (const char *suffix, const char *swtch, const char *glob,
> -                dump_kind dkind, int optgroup_flags, bool take_ownership);
> +                dump_kind dkind, optgroup_flags_t optgroup_flags,
> +                bool take_ownership);
>
>    /* Allow languages and middle-end to register their dumps before the
>       optimization passes.  */
> @@ -275,7 +388,7 @@ private:
>    dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename);
>
>    int
> -  opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
> +  opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
>                           const char *filename);
>
>  private:
> diff --git a/gcc/early-remat.c b/gcc/early-remat.c
> index 28eb9b4..776b2d0 100644
> --- a/gcc/early-remat.c
> +++ b/gcc/early-remat.c
> @@ -657,7 +657,7 @@ early_remat::dump_edge_list (basic_block bb, bool do_succ)
>    edge e;
>    edge_iterator ei;
>    FOR_EACH_EDGE (e, ei, do_succ ? bb->succs : bb->preds)
> -    dump_edge_info (dump_file, e, 0, do_succ);
> +    dump_edge_info (dump_file, e, TDF_NONE, do_succ);
>  }
>
>  /* Print information about basic block BB to the dump file.  */
> diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
> index 6695526..39edaa1 100644
> --- a/gcc/gimple-pretty-print.c
> +++ b/gcc/gimple-pretty-print.c
> @@ -153,7 +153,7 @@ print_gimple_stmt (FILE *file, gimple *g, int spc, dump_flags_t flags)
>  DEBUG_FUNCTION void
>  debug (gimple &ref)
>  {
> -  print_gimple_stmt (stderr, &ref, 0, 0);
> +  print_gimple_stmt (stderr, &ref, 0, TDF_NONE);
>  }
>
>  DEBUG_FUNCTION void
> diff --git a/gcc/gimple-ssa-store-merging.c b/gcc/gimple-ssa-store-merging.c
> index 6f6538b..7362128 100644
> --- a/gcc/gimple-ssa-store-merging.c
> +++ b/gcc/gimple-ssa-store-merging.c
> @@ -1075,7 +1075,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
>             print_gimple_stmt (dump_file, cur_stmt, 0);
>           else
>             {
> -             print_generic_expr (dump_file, tgt, 0);
> +             print_generic_expr (dump_file, tgt, TDF_NONE);
>               fprintf (dump_file, "\n");
>             }
>         }
> @@ -1145,7 +1145,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
>         print_gimple_stmt (dump_file, cur_stmt, 0);
>        else
>         {
> -         print_generic_expr (dump_file, tgt, 0);
> +         print_generic_expr (dump_file, tgt, TDF_NONE);
>           fprintf (dump_file, "\n");
>         }
>      }
> @@ -1956,7 +1956,7 @@ merged_store_group::apply_stores ()
>           if (ret)
>             {
>               fprintf (dump_file, "After writing ");
> -             print_generic_expr (dump_file, cst, 0);
> +             print_generic_expr (dump_file, cst, TDF_NONE);
>               fprintf (dump_file, " of size " HOST_WIDE_INT_PRINT_DEC
>                        " at position %d the merged region contains:\n",
>                        info->bitsize, pos_in_buffer);
> diff --git a/gcc/gimple-ssa-strength-reduction.c b/gcc/gimple-ssa-strength-reduction.c
> index 1c00f09..14fc420 100644
> --- a/gcc/gimple-ssa-strength-reduction.c
> +++ b/gcc/gimple-ssa-strength-reduction.c
> @@ -3353,7 +3353,7 @@ insert_initializers (slsr_cand_t c)
>               fputs ("Using existing initializer: ", dump_file);
>               print_gimple_stmt (dump_file,
>                                  SSA_NAME_DEF_STMT (incr_vec[i].initializer),
> -                                0, 0);
> +                                0, TDF_NONE);
>             }
>           continue;
>         }
> diff --git a/gcc/gimple.c b/gcc/gimple.c
> index 9dc4911..c18526a 100644
> --- a/gcc/gimple.c
> +++ b/gcc/gimple.c
> @@ -3148,7 +3148,7 @@ static void
>  verify_gimple_pp (const char *expected, gimple *stmt)
>  {
>    pretty_printer pp;
> -  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, 0 /* flags */);
> +  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, TDF_NONE /* flags */);
>    ASSERT_STREQ (expected, pp_formatted_text (&pp));
>  }
>
> diff --git a/gcc/passes.c b/gcc/passes.c
> index ad0a912..62fd413 100644
> --- a/gcc/passes.c
> +++ b/gcc/passes.c
> @@ -784,7 +784,7 @@ pass_manager::register_one_dump_file (opt_pass *pass)
>    char num[11];
>    dump_kind dkind;
>    int id;
> -  int optgroup_flags = OPTGROUP_NONE;
> +  optgroup_flags_t optgroup_flags = OPTGROUP_NONE;
>    gcc::dump_manager *dumps = m_ctxt->get_dumps ();
>
>    /* See below in next_pass_1.  */
> diff --git a/gcc/print-tree.c b/gcc/print-tree.c
> index caf5f26..5c736c5 100644
> --- a/gcc/print-tree.c
> +++ b/gcc/print-tree.c
> @@ -894,7 +894,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
>           {
>             pretty_printer buffer;
>             buffer.buffer->stream = file;
> -           pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4, 0);
> +           pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4,
> +                             TDF_NONE);
>             pp_flush (&buffer);
>           }
>
> @@ -1039,7 +1040,7 @@ dump_tree_via_hooks (const tree_node *ptr, dump_flags_t options)
>  DEBUG_FUNCTION void
>  debug (const tree_node &ref)
>  {
> -  dump_tree_via_hooks (&ref, 0);
> +  dump_tree_via_hooks (&ref, TDF_NONE);
>  }
>
>  DEBUG_FUNCTION void
> @@ -1070,7 +1071,7 @@ DEBUG_FUNCTION void
>  debug_body (const tree_node &ref)
>  {
>    if (TREE_CODE (&ref) == FUNCTION_DECL)
> -    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, 0);
> +    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, TDF_NONE);
>    else
>      debug (ref);
>  }
> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> index 93a6a99..c757591 100644
> --- a/gcc/tree-pass.h
> +++ b/gcc/tree-pass.h
> @@ -47,7 +47,7 @@ struct pass_data
>    const char *name;
>
>    /* The -fopt-info optimization group flags as defined in dumpfile.h. */
> -  unsigned int optinfo_flags;
> +  optgroup_flags_t optinfo_flags;
>
>    /* The timevar id associated with this pass.  */
>    /* ??? Ideally would be dynamically assigned.  */
> diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
> index 276ad00..8963ae0 100644
> --- a/gcc/tree-pretty-print.c
> +++ b/gcc/tree-pretty-print.c
> @@ -3404,7 +3404,7 @@ print_struct_decl (pretty_printer *pp, const_tree node, int spc,
>                 || TREE_CODE (node) == QUAL_UNION_TYPE))
>         pp_string (pp, "union ");
>
> -      dump_generic_node (pp, TYPE_NAME (node), spc, 0, false);
> +      dump_generic_node (pp, TYPE_NAME (node), spc, TDF_NONE, false);
>      }
>
>    /* Print the contents of the structure.  */
> diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
> index 16d9399..b3f8cb6 100644
> --- a/gcc/tree-ssa-math-opts.c
> +++ b/gcc/tree-ssa-math-opts.c
> @@ -2710,7 +2710,7 @@ convert_mult_to_fma_1 (tree mul_result, tree op1, tree op2)
>        if (dump_file && (dump_flags & TDF_DETAILS))
>         {
>           fprintf (dump_file, "Generated FMA ");
> -         print_gimple_stmt (dump_file, fma_stmt, 0, 0);
> +         print_gimple_stmt (dump_file, fma_stmt, 0, TDF_NONE);
>           fprintf (dump_file, "\n");
>         }
>
> @@ -3046,7 +3046,7 @@ convert_mult_to_fma (gimple *mul_stmt, tree op1, tree op2,
>        if (dump_file && (dump_flags & TDF_DETAILS))
>         {
>           fprintf (dump_file, "Deferred generating FMA for multiplication ");
> -         print_gimple_stmt (dump_file, mul_stmt, 0, 0);
> +         print_gimple_stmt (dump_file, mul_stmt, 0, TDF_NONE);
>           fprintf (dump_file, "\n");
>         }
>
> diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c
> index 0e59bb5..dde9701 100644
> --- a/gcc/tree-ssa-reassoc.c
> +++ b/gcc/tree-ssa-reassoc.c
> @@ -1606,7 +1606,7 @@ undistribute_ops_list (enum tree_code opcode,
>      {
>        fprintf (dump_file, "searching for un-distribute opportunities ");
>        print_generic_expr (dump_file,
> -       (*ops)[bitmap_first_set_bit (candidates)]->op, 0);
> +       (*ops)[bitmap_first_set_bit (candidates)]->op, TDF_NONE);
>        fprintf (dump_file, " %d\n", nr_candidates);
>      }
>
> diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
> index 1463c1d..d8ab1f0 100644
> --- a/gcc/tree-ssa-sccvn.c
> +++ b/gcc/tree-ssa-sccvn.c
> @@ -5921,7 +5921,7 @@ vn_eliminate (bitmap inserted_exprs)
>        if (dump_file && (dump_flags & TDF_DETAILS))
>         {
>           fprintf (dump_file, "Removing dead stmt ");
> -         print_gimple_stmt (dump_file, stmt, 0, 0);
> +         print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
>         }
>
>        gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
> diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
> index 9aabcc1..ebc56c0 100644
> --- a/gcc/tree-vect-data-refs.c
> +++ b/gcc/tree-vect-data-refs.c
> @@ -3229,7 +3229,7 @@ dependence_distance_ge_vf (data_dependence_relation *ddr,
>  /* Dump LOWER_BOUND using flags DUMP_KIND.  Dumps are known to be enabled.  */
>
>  static void
> -dump_lower_bound (int dump_kind, const vec_lower_bound &lower_bound)
> +dump_lower_bound (dump_flags_t dump_kind, const vec_lower_bound &lower_bound)
>  {
>    dump_printf (dump_kind, "%s (", lower_bound.unsigned_p ? "unsigned" : "abs");
>    dump_generic_expr (dump_kind, TDF_SLIM, lower_bound.expr);
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-06-01  9:51   ` Richard Biener
@ 2018-06-01 13:40     ` David Malcolm
  2018-06-01 15:32       ` Richard Biener
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-01 13:40 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Fri, 2018-06-01 at 11:50 +0200, Richard Biener wrote:
> On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This was an experiment to try to capture information on a
> > loop optimization.
> > 
> > gcc/ChangeLog:
> >         * gimple-loop-interchange.cc (should_interchange_loops):
> > Add
> >         optinfo note when interchange gives better data locality
> > behavior.
> >         (tree_loop_interchange::interchange): Add OPTINFO_SCOPE.
> >         Add optinfo for successful and unsuccessful interchanges.
> >         (prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
> >         optinfo note.
> >         (pass_linterchange::execute): Add OPTINFO_SCOPE.
> > ---
> >  gcc/gimple-loop-interchange.cc | 36
> > +++++++++++++++++++++++++++++++++++-
> >  1 file changed, 35 insertions(+), 1 deletion(-)
> > 
> > diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-
> > interchange.cc
> > index eb35263..cd32288 100644
> > --- a/gcc/gimple-loop-interchange.cc
> > +++ b/gcc/gimple-loop-interchange.cc
> > @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned i_idx,
> > unsigned o_idx,
> >    ratio = innermost_loops_p ? INNER_STRIDE_RATIO :
> > OUTER_STRIDE_RATIO;
> >    /* Do interchange if it gives better data locality behavior.  */
> >    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides, ratio)))
> > -    return true;
> > +    {
> > +      if (optinfo_enabled_p ())
> > +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
> > +         << "interchange gives better data locality behavior: "
> > +         << "iloop_strides: "
> > +         << decu (iloop_strides)
> > +         << " > (oloop_strides: "
> > +         << decu (oloop_strides)
> > +         << " * ratio: "
> > +         << decu (ratio)
> > +         << ")";
> 
> Just randomly inside the thread.
> 
> NOOOOOOOOOO!
> 
> :/

> Please do _not_ add more stream-like APIs.  How do you expect
> translators to deal with those?
> 
> Yes, I'm aware of the graphite-* ones and I dislike those very much.
> 
> What's wrong with the existing dump API?

The existing API suffers from a "wall of text" problem:

* although it's possible to filter based on various criteria (the
optgroup tags, specific passes, success vs failure), it's not possible
to filter base on code hotness: the -fopt-info API works purely in
terms of location_t.  So all of the messages about the hottest
functions in the workload are intermingled with all of the other
messages about all of the other functions.

* some of the text notes refer to function entry, but all of these are
emitted "at the same level": there's no way to see the nesting of these
function-entry logs, and where other notes are in relation to them. 
For example, in:

  test.c:8:3: note: === analyzing loop ===
  test.c:8:3: note: === analyze_loop_nest ===
  test.c:8:3: note: === vect_analyze_loop_form ===
  test.c:8:3: note: === get_loop_niters ===
  test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
  test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
  test.c:8:3: note: vectorized 0 loops in function

there's no way to tell that the "vect_analyze_loop_form" is in fact
inside the call to "analyze_loop_nest", and where the "symbolic number
of iterations" messages is coming from in relation to them.  This may
not seem significant here, but I'm quoting a small example;
vectorization typically leads to dozens of messages, with a deep
nesting structure (where that structure isn't visible in the -fopt-info 
output).

The existing API is throwing data away:

* as noted above, by working purely with a location_t, the execution
count isn't associated with the messages.  The output format purely
gives file/line/column information, but doesn't cover the inlining
chain.   For C++ templates it doesn't specify which instance of a
template is being optimized.

* there's no metadata about where the messages are coming from.  It's
easy to get at the current pass internally, but the messages don't
capture that.  Figuring out where a message came from requires grepping
the GCC source code.  The prototype I posted captures the __FILE__ and
__LINE__ within the gcc source for every message emitted, and which
pass instance emitted it.

* The current output format is of the form:
     "FILE:LINE:COLUMN: free-form text\n"
This is only machine-readable up to a point: if a program is parsing
it, all it has is the free-form text.  The prototype I posted captures
what kinds of things are in the text (statements, trees, symtab_nodes),
and captures location information for them, so that visualizations of
the dumps can provide useful links.

There's no API-level grouping of messages, beyond looking for newline
characters.

I'm probably re-hashing a lot of the material in the cover letter here:
"[PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis"
  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01675.html


I'd like to provide a machine-readable output format that covers the
above - in particular the profile data (whilst retaining -fopt-info for
compatibility).  Separation of the data from its presentation.

Clearly you don't like the stream-like API from the prototype :)

So I'm wondering what the API ought to look like, one that would allow
for the kind of "richer" machine-readable output.

Consider this "-fopt-info" code (from "vect_create_data_ref_ptr"; this
is example 2 from the cover-letter):

  if (dump_enabled_p ())
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       dump_printf_loc (MSG_NOTE, vect_location,
                        "create %s-pointer variable to type: ",
                        get_tree_code_name (TREE_CODE (aggr_type)));
       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a record based array ref: ");
       else
         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
       dump_printf (MSG_NOTE, "\n");
     }

where the information is built up piecewise, with conditional logic.
(This existing code isn't particularly amenable to translation either).

One option would be for "vect_location" to become something other than
a location_t, from which a execution count can be gained (e.g. a gimple
*, from which the location_t and the BB can be accessed).  Then
"dump_printf_loc" would signify starting an optimization record, and
the final "\n" would signify the end of an optimization record; the
various dump_generic_expr and dump_printf would add structured
information and text entries to theoptimization record.

This has the advantage of retaining the look of the existing API (so
the existing callsites don't need changing), but it changes their
meaning, so that as well as writing to -fopt-info's destinations, it's
also in a sense parsing the dump_* calls and building optimization
records from them.

AIUI, dump_printf_loc can't be a macro, as it's variadic, so this
approach doesn't allow for capturing the location within GCC for where
the message is emitted.

Another approach: there could be something like:

  if (optinfo_enabled_p ())
    {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       OPTINFO_VECT_NOTE note;
       note.printf ("create %s-pointer variable to type: ",
                    get_tree_code_name (TREE_CODE (aggr_type)));
       note.add_slim (aggr_type);
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         note.printf ("  vectorizing an array ref: ");
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         note.printf (MSG_NOTE, "  vectorizing a vector ref: ");
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         note.printf (MSG_NOTE, "  vectorizing a record based array ref: ");
       else
         note.printf (MSG_NOTE, "  vectorizing a pointer ref: ");
       note.add_slim (DR_BASE_OBJECT (dr));
    }

which uses a macro to get the gcc source location, and avoids the
special meaning for "\n".  This avoids the "<<" - but is kind of just
different syntax for the same - it's hard with this example to avoid
it.

Yet another approach: reworking things to support i18n via a pure
printf-style interface, using, say "%S" to mean "TDF_SLIM", it could be
like:

  if (optinfo_enabled_p ())
    {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
                            "  vectorizing an array ref: %S",
                            get_tree_code_name (TREE_CODE (aggr_type))
	                    aggr_type,
                            DR_BASE_OBJECT (dr));
      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
                            "  vectorizing a vector ref: %S",
                            get_tree_code_name (TREE_CODE (aggr_type))
	                    aggr_type,
                            DR_BASE_OBJECT (dr));
      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
                            "  vectorizing a record based array ref: %S",
                            get_tree_code_name (TREE_CODE (aggr_type))
	                    aggr_type,
                            DR_BASE_OBJECT (dr));
      else
         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
                            "  vectorizing a pointer ref: %S",
                            get_tree_code_name (TREE_CODE (aggr_type))
	                    aggr_type,
                            DR_BASE_OBJECT (dr));
    }

or somesuch.  The %S would allow for the data to be captured when
emitting optimization records for the messages (not just plain text,
e.g. locations of the expressions).

So those are some ideas.  I'm sure there are other ways to fix the
issues with -fopt-info; I'm brainstorming here.

[...snip...]

Thoughts?

Thanks
Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-06-01 13:40     ` David Malcolm
@ 2018-06-01 15:32       ` Richard Biener
  2018-06-01 22:22         ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-01 15:32 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On June 1, 2018 3:40:15 PM GMT+02:00, David Malcolm <dmalcolm@redhat.com> wrote:
>On Fri, 2018-06-01 at 11:50 +0200, Richard Biener wrote:
>> On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redhat.com>
>> wrote:
>> > 
>> > This was an experiment to try to capture information on a
>> > loop optimization.
>> > 
>> > gcc/ChangeLog:
>> >         * gimple-loop-interchange.cc (should_interchange_loops):
>> > Add
>> >         optinfo note when interchange gives better data locality
>> > behavior.
>> >         (tree_loop_interchange::interchange): Add OPTINFO_SCOPE.
>> >         Add optinfo for successful and unsuccessful interchanges.
>> >         (prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
>> >         optinfo note.
>> >         (pass_linterchange::execute): Add OPTINFO_SCOPE.
>> > ---
>> >  gcc/gimple-loop-interchange.cc | 36
>> > +++++++++++++++++++++++++++++++++++-
>> >  1 file changed, 35 insertions(+), 1 deletion(-)
>> > 
>> > diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-
>> > interchange.cc
>> > index eb35263..cd32288 100644
>> > --- a/gcc/gimple-loop-interchange.cc
>> > +++ b/gcc/gimple-loop-interchange.cc
>> > @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned i_idx,
>> > unsigned o_idx,
>> >    ratio = innermost_loops_p ? INNER_STRIDE_RATIO :
>> > OUTER_STRIDE_RATIO;
>> >    /* Do interchange if it gives better data locality behavior.  */
>> >    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides, ratio)))
>> > -    return true;
>> > +    {
>> > +      if (optinfo_enabled_p ())
>> > +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
>> > +         << "interchange gives better data locality behavior: "
>> > +         << "iloop_strides: "
>> > +         << decu (iloop_strides)
>> > +         << " > (oloop_strides: "
>> > +         << decu (oloop_strides)
>> > +         << " * ratio: "
>> > +         << decu (ratio)
>> > +         << ")";
>> 
>> Just randomly inside the thread.
>> 
>> NOOOOOOOOOO!
>> 
>> :/
>
>> Please do _not_ add more stream-like APIs.  How do you expect
>> translators to deal with those?
>> 
>> Yes, I'm aware of the graphite-* ones and I dislike those very much.
>> 
>> What's wrong with the existing dump API?
>
>The existing API suffers from a "wall of text" problem:
>
>* although it's possible to filter based on various criteria (the
>optgroup tags, specific passes, success vs failure), it's not possible
>to filter base on code hotness: the -fopt-info API works purely in
>terms of location_t.  So all of the messages about the hottest
>functions in the workload are intermingled with all of the other
>messages about all of the other functions.

True

>* some of the text notes refer to function entry, but all of these are
>emitted "at the same level": there's no way to see the nesting of these
>function-entry logs, and where other notes are in relation to them. 
>For example, in:
>
>  test.c:8:3: note: === analyzing loop ===
>  test.c:8:3: note: === analyze_loop_nest ===
>  test.c:8:3: note: === vect_analyze_loop_form ===
>  test.c:8:3: note: === get_loop_niters ===
>test.c:8:3: note: symbolic number of iterations is (unsigned int)
>n_9(D)
>test.c:8:3: note: not vectorized: loop contains function calls or data
>references that cannot be analyzed
>  test.c:8:3: note: vectorized 0 loops in function
>
>there's no way to tell that the "vect_analyze_loop_form" is in fact
>inside the call to "analyze_loop_nest", and where the "symbolic number
>of iterations" messages is coming from in relation to them.  This may
>not seem significant here, but I'm quoting a small example;
>vectorization typically leads to dozens of messages, with a deep
>nesting structure (where that structure isn't visible in the -fopt-info
>
>output).

True. The same issue exists for diagnostics BTW. Indeed, being able to collapse 'sections' in dump files, opt-info or diagnostics sounds useful. 

Note that for dump files and opt-info the level argument was sort of designed to do that. 

>
>The existing API is throwing data away:
>
>* as noted above, by working purely with a location_t, the execution
>count isn't associated with the messages.  The output format purely
>gives file/line/column information, but doesn't cover the inlining
>chain.   For C++ templates it doesn't specify which instance of a
>template is being optimized.

It might be useful to enhance the interface by allowing different kind of 'locations'. 

>* there's no metadata about where the messages are coming from.  It's
>easy to get at the current pass internally, but the messages don't
>capture that.  Figuring out where a message came from requires grepping
>the GCC source code.  The prototype I posted captures the __FILE__ and
>__LINE__ within the gcc source for every message emitted, and which
>pass instance emitted it.

The opt info group was supposed to captures this to the level interesting for a user. 

>* The current output format is of the form:
>     "FILE:LINE:COLUMN: free-form text\n"
>This is only machine-readable up to a point: if a program is parsing
>it, all it has is the free-form text.  The prototype I posted captures
>what kinds of things are in the text (statements, trees, symtab_nodes),
>and captures location information for them, so that visualizations of
>the dumps can provide useful links.
>
>There's no API-level grouping of messages, beyond looking for newline
>characters.
>
>I'm probably re-hashing a lot of the material in the cover letter here:
>"[PATCH 00/10] RFC: Prototype of compiler-assisted performance
>analysis"
>  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01675.html
>
>
>I'd like to provide a machine-readable output format that covers the
>above - in particular the profile data (whilst retaining -fopt-info for
>compatibility).  Separation of the data from its presentation.
>
>Clearly you don't like the stream-like API from the prototype :)

Yes :) I wasn't so much complaining about the content but the presentation /API. 

>So I'm wondering what the API ought to look like, one that would allow
>for the kind of "richer" machine-readable output.
>
>Consider this "-fopt-info" code (from "vect_create_data_ref_ptr"; this
>is example 2 from the cover-letter):
>
>  if (dump_enabled_p ())
>     {
>       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
>       dump_printf_loc (MSG_NOTE, vect_location,
>                        "create %s-pointer variable to type: ",
>                        get_tree_code_name (TREE_CODE (aggr_type)));
>       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
>       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
>         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
>       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
>         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
>       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
>    dump_printf (MSG_NOTE, "  vectorizing a record based array ref: ");
>       else
>         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
>       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
>       dump_printf (MSG_NOTE, "\n");
>     }
>
>where the information is built up piecewise, with conditional logic.
>(This existing code isn't particularly amenable to translation either).
>
>One option would be for "vect_location" to become something other than
>a location_t, from which a execution count can be gained (e.g. a gimple
>*, from which the location_t and the BB can be accessed).

Yes. Like a container that can be initiated from other kind of contexts. 

  Then
>"dump_printf_loc" would signify starting an optimization record, and
>the final "\n" would signify the end of an optimization record; the
>various dump_generic_expr and dump_printf would add structured
>information and text entries to theoptimization record.

A push/pop style API would maybe work as well. (pushing a level with some meta data) 

>This has the advantage of retaining the look of the existing API (so
>the existing callsites don't need changing), but it changes their
>meaning, so that as well as writing to -fopt-info's destinations, it's
>also in a sense parsing the dump_* calls and building optimization
>records from them.
>
>AIUI, dump_printf_loc can't be a macro, as it's variadic, so this
>approach doesn't allow for capturing the location within GCC for where
>the message is emitted.

True, though we have __builtin_FILE and friends that can be used as default args. 

Note a push/pop level API can also buffer intermediate printf style output and annotate/indent it (supporting a dump file emacs/vim mode that can do collapse and expand) 

>Another approach: there could be something like:
>
>  if (optinfo_enabled_p ())
>    {
>       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
>       OPTINFO_VECT_NOTE note;
>       note.printf ("create %s-pointer variable to type: ",
>                    get_tree_code_name (TREE_CODE (aggr_type)));
>       note.add_slim (aggr_type);
>       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
>         note.printf ("  vectorizing an array ref: ");
>       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
>         note.printf (MSG_NOTE, "  vectorizing a vector ref: ");
>       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
>    note.printf (MSG_NOTE, "  vectorizing a record based array ref: ");
>       else
>         note.printf (MSG_NOTE, "  vectorizing a pointer ref: ");
>       note.add_slim (DR_BASE_OBJECT (dr));
>    }
>
>which uses a macro to get the gcc source location, and avoids the
>special meaning for "\n".  This avoids the "<<" - but is kind of just
>different syntax for the same - it's hard with this example to avoid
>it.

Maybe the following raii style that encapsulates the enabling /disabling checks? 

 If (optinfo o = push (msg_optimization,...))
  {
     O.print (...) ;
     Destructor of o 'pops' 
  } 

>
>Yet another approach: reworking things to support i18n via a pure
>printf-style interface, using, say "%S" to mean "TDF_SLIM", it could be
>like:
>
>  if (optinfo_enabled_p ())
>    {
>       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
>       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
>         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
>                            "  vectorizing an array ref: %S",
>                            get_tree_code_name (TREE_CODE (aggr_type))
>	                    aggr_type,
>                            DR_BASE_OBJECT (dr));
>      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
>         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
>                            "  vectorizing a vector ref: %S",
>                            get_tree_code_name (TREE_CODE (aggr_type))
>	                    aggr_type,
>                            DR_BASE_OBJECT (dr));
>      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
>         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
>                          "  vectorizing a record based array ref: %S",
>                            get_tree_code_name (TREE_CODE (aggr_type))
>	                    aggr_type,
>                            DR_BASE_OBJECT (dr));
>      else
>         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
>                            "  vectorizing a pointer ref: %S",
>                            get_tree_code_name (TREE_CODE (aggr_type))
>	                    aggr_type,
>                            DR_BASE_OBJECT (dr));
>    }
>
>or somesuch.  The %S would allow for the data to be captured when
>emitting optimization records for the messages (not just plain text,
>e.g. locations of the expressions).

Certainly when exposing things in opt-info we have to be more disciplined. The vectorizer is a bad example here. 
The original goal of having one set of outputs for both dump files and opt-info is good but I guess the result is less than optimal. Maybe additional detail levels would help here (MSG_Dumpfile_only?) 

>So those are some ideas.  I'm sure there are other ways to fix the
>issues with -fopt-info; I'm brainstorming here.

Likewise. As said I applaud the attempt improve the situation but I detest a stream API ;) 

Richard. 

>[...snip...]
>
>Thoughts?
>
>Thanks
>Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-06-01 15:32       ` Richard Biener
@ 2018-06-01 22:22         ` David Malcolm
  2018-06-04 13:20           ` Richard Biener
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-01 22:22 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Fri, 2018-06-01 at 17:31 +0200, Richard Biener wrote:
> On June 1, 2018 3:40:15 PM GMT+02:00, David Malcolm <dmalcolm@redhat.
> com> wrote:
> > On Fri, 2018-06-01 at 11:50 +0200, Richard Biener wrote:
> > > On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redhat.c
> > > om>
> > > wrote:
> > > > 
> > > > This was an experiment to try to capture information on a
> > > > loop optimization.
> > > > 
> > > > gcc/ChangeLog:
> > > >         * gimple-loop-interchange.cc
> > > > (should_interchange_loops):
> > > > Add
> > > >         optinfo note when interchange gives better data
> > > > locality
> > > > behavior.
> > > >         (tree_loop_interchange::interchange): Add
> > > > OPTINFO_SCOPE.
> > > >         Add optinfo for successful and unsuccessful
> > > > interchanges.
> > > >         (prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
> > > >         optinfo note.
> > > >         (pass_linterchange::execute): Add OPTINFO_SCOPE.
> > > > ---
> > > >  gcc/gimple-loop-interchange.cc | 36
> > > > +++++++++++++++++++++++++++++++++++-
> > > >  1 file changed, 35 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-
> > > > interchange.cc
> > > > index eb35263..cd32288 100644
> > > > --- a/gcc/gimple-loop-interchange.cc
> > > > +++ b/gcc/gimple-loop-interchange.cc
> > > > @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned
> > > > i_idx,
> > > > unsigned o_idx,
> > > >    ratio = innermost_loops_p ? INNER_STRIDE_RATIO :
> > > > OUTER_STRIDE_RATIO;
> > > >    /* Do interchange if it gives better data locality
> > > > behavior.  */
> > > >    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides,
> > > > ratio)))
> > > > -    return true;
> > > > +    {
> > > > +      if (optinfo_enabled_p ())
> > > > +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
> > > > +         << "interchange gives better data locality behavior:
> > > > "
> > > > +         << "iloop_strides: "
> > > > +         << decu (iloop_strides)
> > > > +         << " > (oloop_strides: "
> > > > +         << decu (oloop_strides)
> > > > +         << " * ratio: "
> > > > +         << decu (ratio)
> > > > +         << ")";
> > > 
> > > Just randomly inside the thread.
> > > 
> > > NOOOOOOOOOO!
> > > 
> > > :/
> > > Please do _not_ add more stream-like APIs.  How do you expect
> > > translators to deal with those?
> > > 
> > > Yes, I'm aware of the graphite-* ones and I dislike those very
> > > much.
> > > 
> > > What's wrong with the existing dump API?
> > 
> > The existing API suffers from a "wall of text" problem:
> > 
> > * although it's possible to filter based on various criteria (the
> > optgroup tags, specific passes, success vs failure), it's not
> > possible
> > to filter base on code hotness: the -fopt-info API works purely in
> > terms of location_t.  So all of the messages about the hottest
> > functions in the workload are intermingled with all of the other
> > messages about all of the other functions.
> 
> True
> 
> > * some of the text notes refer to function entry, but all of these
> > are
> > emitted "at the same level": there's no way to see the nesting of
> > these
> > function-entry logs, and where other notes are in relation to
> > them. 
> > For example, in:
> > 
> >  test.c:8:3: note: === analyzing loop ===
> >  test.c:8:3: note: === analyze_loop_nest ===
> >  test.c:8:3: note: === vect_analyze_loop_form ===
> >  test.c:8:3: note: === get_loop_niters ===
> > test.c:8:3: note: symbolic number of iterations is (unsigned int)
> > n_9(D)
> > test.c:8:3: note: not vectorized: loop contains function calls or
> > data
> > references that cannot be analyzed
> >  test.c:8:3: note: vectorized 0 loops in function
> > 
> > there's no way to tell that the "vect_analyze_loop_form" is in fact
> > inside the call to "analyze_loop_nest", and where the "symbolic
> > number
> > of iterations" messages is coming from in relation to them.  This
> > may
> > not seem significant here, but I'm quoting a small example;
> > vectorization typically leads to dozens of messages, with a deep
> > nesting structure (where that structure isn't visible in the -fopt-
> > info
> > 
> > output).
> 
> True. The same issue exists for diagnostics BTW. Indeed, being able
> to collapse 'sections' in dump files, opt-info or diagnostics sounds
> useful. 
> 
> Note that for dump files and opt-info the level argument was sort of
> designed to do that. 

Are you referring to the indentation argument here?

> > 
> > The existing API is throwing data away:
> > 
> > * as noted above, by working purely with a location_t, the
> > execution
> > count isn't associated with the messages.  The output format purely
> > gives file/line/column information, but doesn't cover the inlining
> > chain.   For C++ templates it doesn't specify which instance of a
> > template is being optimized.
> 
> It might be useful to enhance the interface by allowing different
> kind of 'locations'. 

In patch 3 of the kit there's a class optinfo_location, which can be
constructed from:
  * a gimple *, or
  * a basic_block, or
  * a loop *
Hence you can pass in any of the above when an optinfo_location is
required.  Potentially there could also be a constructor taking an
rtx_insn * (though I'm primarily interested in gimple passes here,
especially inlining and vectorization).

Internally it's currently stored as a gimple *, but I guess it could be
, say, a basic_block and a location_t.

> > * there's no metadata about where the messages are coming
> > from.  It's
> > easy to get at the current pass internally, but the messages don't
> > capture that.  Figuring out where a message came from requires
> > grepping
> > the GCC source code.  The prototype I posted captures the __FILE__
> > and
> > __LINE__ within the gcc source for every message emitted, and which
> > pass instance emitted it.
> 
> The opt info group was supposed to captures this to the level
> interesting for a user. 

A question here is who the "user" is.  I'm aiming this both at GCC
developers, and technically-sophisticated end-users.  As a member of
the former category, I'd love to have an easy way to go from a message
to the line of code that emitted it.

The optinfo group seems to be *very* high level: "is this a "loop"
message, or a "vec" message?" etc.  That is useful too (one of the good
things about the existing system).

> > * The current output format is of the form:
> >     "FILE:LINE:COLUMN: free-form text\n"
> > This is only machine-readable up to a point: if a program is
> > parsing
> > it, all it has is the free-form text.  The prototype I posted
> > captures
> > what kinds of things are in the text (statements, trees,
> > symtab_nodes),
> > and captures location information for them, so that visualizations
> > of
> > the dumps can provide useful links.
> > 
> > There's no API-level grouping of messages, beyond looking for
> > newline
> > characters.
> > 
> > I'm probably re-hashing a lot of the material in the cover letter
> > here:
> > "[PATCH 00/10] RFC: Prototype of compiler-assisted performance
> > analysis"
> >  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01675.html
> > 
> > 
> > I'd like to provide a machine-readable output format that covers
> > the
> > above - in particular the profile data (whilst retaining -fopt-info 
> > for
> > compatibility).  Separation of the data from its presentation.
> > 
> > Clearly you don't like the stream-like API from the prototype :)
> 
> Yes :) I wasn't so much complaining about the content but the
> presentation /API. 

(nods)

> > So I'm wondering what the API ought to look like, one that would
> > allow
> > for the kind of "richer" machine-readable output.
> > 
> > Consider this "-fopt-info" code (from "vect_create_data_ref_ptr";
> > this
> > is example 2 from the cover-letter):
> > 
> >  if (dump_enabled_p ())
> >     {
> >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> >       dump_printf_loc (MSG_NOTE, vect_location,
> >                        "create %s-pointer variable to type: ",
> >                        get_tree_code_name (TREE_CODE (aggr_type)));
> >       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
> >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> >         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
> >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> >         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
> >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> >    dump_printf (MSG_NOTE, "  vectorizing a record based array ref:
> > ");
> >       else
> >         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
> >       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
> >       dump_printf (MSG_NOTE, "\n");
> >     }
> > 
> > where the information is built up piecewise, with conditional
> > logic.
> > (This existing code isn't particularly amenable to translation
> > either).
> > 
> > One option would be for "vect_location" to become something other
> > than
> > a location_t, from which a execution count can be gained (e.g. a
> > gimple
> > *, from which the location_t and the BB can be accessed).
> 
> Yes. Like a container that can be initiated from other kind of
> contexts. 

(like the optinfo_location class described above).

So this might be something like:

extern void dump_printf_loc (optinfo_groups_t,
                             optinfo_location,
                             const char *fmt,
                             ...);

or somesuch.

>   Then
> > "dump_printf_loc" would signify starting an optimization record,
> > and
> > the final "\n" would signify the end of an optimization record; the
> > various dump_generic_expr and dump_printf would add structured
> > information and text entries to theoptimization record.
> 
> A push/pop style API would maybe work as well. (pushing a level with
> some meta data) 

Patch 3 in the kit has an OPTINFO_SCOPE macro which uses a RAII class
to automatically do push/pops.

> > This has the advantage of retaining the look of the existing API
> > (so
> > the existing callsites don't need changing), but it changes their
> > meaning, so that as well as writing to -fopt-info's destinations,
> > it's
> > also in a sense parsing the dump_* calls and building optimization
> > records from them.
> > 
> > AIUI, dump_printf_loc can't be a macro, as it's variadic, so this
> > approach doesn't allow for capturing the location within GCC for
> > where
> > the message is emitted.
> 
> True, though we have __builtin_FILE and friends that can be used as
> default args. 

If I'm understanding the idea, this means relying on implicit use of
default arguments.   I'm not sure how compatible that is with variadic
functions: the ellipsis has to come last.

I thought it was impossible to have default args with a variadic
function, but it seems that this is syntactically valid:

extern void dump_printf_loc (optinfo_groups_t,
                             optinfo_location,
                             const char *fmt,
	                     const char *impl_file = __builtin_FILE (),
	                     int impl_line = __builtin_LINE (),
                             ...);

That said, the above decl seems like a bad idea: a recipe for nasty
surprises (what happens if the format arguments expect a const char *
and an int?).

Idea: maybe the optinfo_location constructor could take the default
args for a __builtin_FILE and __builtin_LINE?  Or the pending_optinfo
class from patch 3 of the kit.  I'll experiment with this.

> Note a push/pop level API can also buffer intermediate printf style
> output and annotate/indent it (supporting a dump file emacs/vim mode
> that can do collapse and expand) 

Interesting idea.

I had a go at emitting text in Emacs outline-mode format.  An example,
showing the source location and pass/hotness metadata only for top-
level messages:
 https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-elided.txt
and another example, showing them for all messages:
 https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-unelided.txt

and it works as-is with Emacs outline-mode (though I don't know how
useful it is; would want a jump-to-source option etc etc).

In both cases, this is prioritizing the messages, sorting from hottest
code down to coldest code (or those messages emitted before the profile
data was loaded), similar to the HTML report here:
https://dmalcolm.fedorapeople.org/gcc/2018-05-18/pgo-demo-test/pgo-demo-test/

(These are all being generated by reading the saved optimization
records, rather than being emitted by the compiler itself)

> > Another approach: there could be something like:
> > 
> >  if (optinfo_enabled_p ())
> >    {
> >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> >       OPTINFO_VECT_NOTE note;
> >       note.printf ("create %s-pointer variable to type: ",
> >                    get_tree_code_name (TREE_CODE (aggr_type)));
> >       note.add_slim (aggr_type);
> >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> >         note.printf ("  vectorizing an array ref: ");
> >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> >         note.printf (MSG_NOTE, "  vectorizing a vector ref: ");
> >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> >    note.printf (MSG_NOTE, "  vectorizing a record based array ref:
> > ");
> >       else
> >         note.printf (MSG_NOTE, "  vectorizing a pointer ref: ");
> >       note.add_slim (DR_BASE_OBJECT (dr));
> >    }
> > 
> > which uses a macro to get the gcc source location, and avoids the
> > special meaning for "\n".  This avoids the "<<" - but is kind of
> > just
> > different syntax for the same - it's hard with this example to
> > avoid
> > it.
> 
> Maybe the following raii style that encapsulates the enabling
> /disabling checks? 
> 
>  If (optinfo o = push (msg_optimization,...))
>   {
>      O.print (...) ;
>      Destructor of o 'pops' 
>   } 

I'm assuming that:
* very few users turn on the dump feature, and 
* a goal is that we shouldn't slow down that common "no dumping" case
when implementing dumping.

If so, then presumably we need a really cheap test that can easily be
marked as cold, or optimized away.

How much work would be done by the call to:
   push (msg_optimization,...),
in particular evaluating the arguments?

I was thinking:
   if (optinfo_enabled_p ())
and have it be a very cheap boolean lookup (though it isn't in the
current patch kit), with everything else guarded by it.

Would a GCC_UNLIKELY(expr) macro be appropriate here?  (so that non-PGO 
builds of the compiler can have all this dump stuff moved into cold
sections)

> > Yet another approach: reworking things to support i18n via a pure
> > printf-style interface, using, say "%S" to mean "TDF_SLIM", it
> > could be
> > like:
> > 
> >  if (optinfo_enabled_p ())
> >    {
> >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> >                            "  vectorizing an array ref: %S",
> >                            get_tree_code_name (TREE_CODE
> > (aggr_type))
> > 	                    aggr_type,
> >                            DR_BASE_OBJECT (dr));
> >      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> >                            "  vectorizing a vector ref: %S",
> >                            get_tree_code_name (TREE_CODE
> > (aggr_type))
> > 	                    aggr_type,
> >                            DR_BASE_OBJECT (dr));
> >      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> >                          "  vectorizing a record based array ref:
> > %S",
> >                            get_tree_code_name (TREE_CODE
> > (aggr_type))
> > 	                    aggr_type,
> >                            DR_BASE_OBJECT (dr));
> >      else
> >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> >                            "  vectorizing a pointer ref: %S",
> >                            get_tree_code_name (TREE_CODE
> > (aggr_type))
> > 	                    aggr_type,
> >                            DR_BASE_OBJECT (dr));
> >    }
> > 
> > or somesuch.  The %S would allow for the data to be captured when
> > emitting optimization records for the messages (not just plain
> > text,
> > e.g. locations of the expressions).
> 
> Certainly when exposing things in opt-info we have to be more
> disciplined. The vectorizer is a bad example here. 
> The original goal of having one set of outputs for both dump files
> and opt-info is good but I guess the result is less than optimal.
> Maybe additional detail levels would help here (MSG_Dumpfile_only?) 
> 
> > So those are some ideas.  I'm sure there are other ways to fix the
> > issues with -fopt-info; I'm brainstorming here.
> 
> Likewise. As said I applaud the attempt improve the situation but I
> detest a stream API ;) 

Thanks for the feedback.  I'll continue to try prototyping ideas.

Dave

> Richard. 
> 
> > [...snip...]
> > 
> > Thoughts?
> > 
> > Thanks
> > Dave
> 
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc
  2018-06-01 22:22         ` David Malcolm
@ 2018-06-04 13:20           ` Richard Biener
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
  2018-06-20 16:35             ` [PATCH] v3 " David Malcolm
  0 siblings, 2 replies; 80+ messages in thread
From: Richard Biener @ 2018-06-04 13:20 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Sat, Jun 2, 2018 at 12:22 AM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Fri, 2018-06-01 at 17:31 +0200, Richard Biener wrote:
> > On June 1, 2018 3:40:15 PM GMT+02:00, David Malcolm <dmalcolm@redhat.
> > com> wrote:
> > > On Fri, 2018-06-01 at 11:50 +0200, Richard Biener wrote:
> > > > On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redhat.c
> > > > om>
> > > > wrote:
> > > > >
> > > > > This was an experiment to try to capture information on a
> > > > > loop optimization.
> > > > >
> > > > > gcc/ChangeLog:
> > > > >         * gimple-loop-interchange.cc
> > > > > (should_interchange_loops):
> > > > > Add
> > > > >         optinfo note when interchange gives better data
> > > > > locality
> > > > > behavior.
> > > > >         (tree_loop_interchange::interchange): Add
> > > > > OPTINFO_SCOPE.
> > > > >         Add optinfo for successful and unsuccessful
> > > > > interchanges.
> > > > >         (prepare_perfect_loop_nest): Add OPTINFO_SCOPE.  Add
> > > > >         optinfo note.
> > > > >         (pass_linterchange::execute): Add OPTINFO_SCOPE.
> > > > > ---
> > > > >  gcc/gimple-loop-interchange.cc | 36
> > > > > +++++++++++++++++++++++++++++++++++-
> > > > >  1 file changed, 35 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-
> > > > > interchange.cc
> > > > > index eb35263..cd32288 100644
> > > > > --- a/gcc/gimple-loop-interchange.cc
> > > > > +++ b/gcc/gimple-loop-interchange.cc
> > > > > @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned
> > > > > i_idx,
> > > > > unsigned o_idx,
> > > > >    ratio = innermost_loops_p ? INNER_STRIDE_RATIO :
> > > > > OUTER_STRIDE_RATIO;
> > > > >    /* Do interchange if it gives better data locality
> > > > > behavior.  */
> > > > >    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides,
> > > > > ratio)))
> > > > > -    return true;
> > > > > +    {
> > > > > +      if (optinfo_enabled_p ())
> > > > > +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
> > > > > +         << "interchange gives better data locality behavior:
> > > > > "
> > > > > +         << "iloop_strides: "
> > > > > +         << decu (iloop_strides)
> > > > > +         << " > (oloop_strides: "
> > > > > +         << decu (oloop_strides)
> > > > > +         << " * ratio: "
> > > > > +         << decu (ratio)
> > > > > +         << ")";
> > > >
> > > > Just randomly inside the thread.
> > > >
> > > > NOOOOOOOOOO!
> > > >
> > > > :/
> > > > Please do _not_ add more stream-like APIs.  How do you expect
> > > > translators to deal with those?
> > > >
> > > > Yes, I'm aware of the graphite-* ones and I dislike those very
> > > > much.
> > > >
> > > > What's wrong with the existing dump API?
> > >
> > > The existing API suffers from a "wall of text" problem:
> > >
> > > * although it's possible to filter based on various criteria (the
> > > optgroup tags, specific passes, success vs failure), it's not
> > > possible
> > > to filter base on code hotness: the -fopt-info API works purely in
> > > terms of location_t.  So all of the messages about the hottest
> > > functions in the workload are intermingled with all of the other
> > > messages about all of the other functions.
> >
> > True
> >
> > > * some of the text notes refer to function entry, but all of these
> > > are
> > > emitted "at the same level": there's no way to see the nesting of
> > > these
> > > function-entry logs, and where other notes are in relation to
> > > them.
> > > For example, in:
> > >
> > >  test.c:8:3: note: === analyzing loop ===
> > >  test.c:8:3: note: === analyze_loop_nest ===
> > >  test.c:8:3: note: === vect_analyze_loop_form ===
> > >  test.c:8:3: note: === get_loop_niters ===
> > > test.c:8:3: note: symbolic number of iterations is (unsigned int)
> > > n_9(D)
> > > test.c:8:3: note: not vectorized: loop contains function calls or
> > > data
> > > references that cannot be analyzed
> > >  test.c:8:3: note: vectorized 0 loops in function
> > >
> > > there's no way to tell that the "vect_analyze_loop_form" is in fact
> > > inside the call to "analyze_loop_nest", and where the "symbolic
> > > number
> > > of iterations" messages is coming from in relation to them.  This
> > > may
> > > not seem significant here, but I'm quoting a small example;
> > > vectorization typically leads to dozens of messages, with a deep
> > > nesting structure (where that structure isn't visible in the -fopt-
> > > info
> > >
> > > output).
> >
> > True. The same issue exists for diagnostics BTW. Indeed, being able
> > to collapse 'sections' in dump files, opt-info or diagnostics sounds
> > useful.
> >
> > Note that for dump files and opt-info the level argument was sort of
> > designed to do that.
>
> Are you referring to the indentation argument here?

No, to MSG_NOTE vs. MSG_MISSED_OPTIMIZATION , etc.

> > >
> > > The existing API is throwing data away:
> > >
> > > * as noted above, by working purely with a location_t, the
> > > execution
> > > count isn't associated with the messages.  The output format purely
> > > gives file/line/column information, but doesn't cover the inlining
> > > chain.   For C++ templates it doesn't specify which instance of a
> > > template is being optimized.
> >
> > It might be useful to enhance the interface by allowing different
> > kind of 'locations'.
>
> In patch 3 of the kit there's a class optinfo_location, which can be
> constructed from:
>   * a gimple *, or
>   * a basic_block, or
>   * a loop *
> Hence you can pass in any of the above when an optinfo_location is
> required.  Potentially there could also be a constructor taking an
> rtx_insn * (though I'm primarily interested in gimple passes here,
> especially inlining and vectorization).
>
> Internally it's currently stored as a gimple *, but I guess it could be
> , say, a basic_block and a location_t.

Ok, so that indeed sounds what I had in mind - change the dump_*_loc
interface to take this kind of class rather than (only) a location_t.

> > > * there's no metadata about where the messages are coming
> > > from.  It's
> > > easy to get at the current pass internally, but the messages don't
> > > capture that.  Figuring out where a message came from requires
> > > grepping
> > > the GCC source code.  The prototype I posted captures the __FILE__
> > > and
> > > __LINE__ within the gcc source for every message emitted, and which
> > > pass instance emitted it.
> >
> > The opt info group was supposed to captures this to the level
> > interesting for a user.
>
> A question here is who the "user" is.  I'm aiming this both at GCC
> developers, and technically-sophisticated end-users.  As a member of
> the former category, I'd love to have an easy way to go from a message
> to the line of code that emitted it.
>
> The optinfo group seems to be *very* high level: "is this a "loop"
> message, or a "vec" message?" etc.  That is useful too (one of the good
> things about the existing system).

It somewhat mimics what other compilers offer.  Then there's the level
from above but it is very coarse-grained and mixing things like
detail (MSG_NOTE) with kind (OPTIMIZED_LOCATIONS vs. UNOPTIMIZED one).

So what would be useful I guess is to somehow split this so we can have
a level MSG_ANALYSIS (and drop MSG_UNOPTIMIZED_LOCATIONS?) and
a separate detail level.  So we could start information about an analysis
phase (thus grouping related info), dump what failed, dump details.

The hard part here is of course taking existing verbose dumps and try to
turn them into sth usable for users.  The vectorizer dumpfile is not
really informative unless you look at the source and mix&match the
dumping code with the surroundings...

> > > * The current output format is of the form:
> > >     "FILE:LINE:COLUMN: free-form text\n"
> > > This is only machine-readable up to a point: if a program is
> > > parsing
> > > it, all it has is the free-form text.  The prototype I posted
> > > captures
> > > what kinds of things are in the text (statements, trees,
> > > symtab_nodes),
> > > and captures location information for them, so that visualizations
> > > of
> > > the dumps can provide useful links.
> > >
> > > There's no API-level grouping of messages, beyond looking for
> > > newline
> > > characters.
> > >
> > > I'm probably re-hashing a lot of the material in the cover letter
> > > here:
> > > "[PATCH 00/10] RFC: Prototype of compiler-assisted performance
> > > analysis"
> > >  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01675.html
> > >
> > >
> > > I'd like to provide a machine-readable output format that covers
> > > the
> > > above - in particular the profile data (whilst retaining -fopt-info
> > > for
> > > compatibility).  Separation of the data from its presentation.
> > >
> > > Clearly you don't like the stream-like API from the prototype :)
> >
> > Yes :) I wasn't so much complaining about the content but the
> > presentation /API.
>
> (nods)
>
> > > So I'm wondering what the API ought to look like, one that would
> > > allow
> > > for the kind of "richer" machine-readable output.
> > >
> > > Consider this "-fopt-info" code (from "vect_create_data_ref_ptr";
> > > this
> > > is example 2 from the cover-letter):
> > >
> > >  if (dump_enabled_p ())
> > >     {
> > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > >       dump_printf_loc (MSG_NOTE, vect_location,
> > >                        "create %s-pointer variable to type: ",
> > >                        get_tree_code_name (TREE_CODE (aggr_type)));
> > >       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
> > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > >         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
> > >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > >         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
> > >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > >    dump_printf (MSG_NOTE, "  vectorizing a record based array ref:
> > > ");
> > >       else
> > >         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
> > >       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
> > >       dump_printf (MSG_NOTE, "\n");
> > >     }
> > >
> > > where the information is built up piecewise, with conditional
> > > logic.
> > > (This existing code isn't particularly amenable to translation
> > > either).
> > >
> > > One option would be for "vect_location" to become something other
> > > than
> > > a location_t, from which a execution count can be gained (e.g. a
> > > gimple
> > > *, from which the location_t and the BB can be accessed).
> >
> > Yes. Like a container that can be initiated from other kind of
> > contexts.
>
> (like the optinfo_location class described above).

Yes.

> So this might be something like:
>
> extern void dump_printf_loc (optinfo_groups_t,
>                              optinfo_location,
>                              const char *fmt,
>                              ...);
>
> or somesuch.

Yeah.

> >   Then
> > > "dump_printf_loc" would signify starting an optimization record,
> > > and
> > > the final "\n" would signify the end of an optimization record; the
> > > various dump_generic_expr and dump_printf would add structured
> > > information and text entries to theoptimization record.
> >
> > A push/pop style API would maybe work as well. (pushing a level with
> > some meta data)
>
> Patch 3 in the kit has an OPTINFO_SCOPE macro which uses a RAII class
> to automatically do push/pops.
>
> > > This has the advantage of retaining the look of the existing API
> > > (so
> > > the existing callsites don't need changing), but it changes their
> > > meaning, so that as well as writing to -fopt-info's destinations,
> > > it's
> > > also in a sense parsing the dump_* calls and building optimization
> > > records from them.
> > >
> > > AIUI, dump_printf_loc can't be a macro, as it's variadic, so this
> > > approach doesn't allow for capturing the location within GCC for
> > > where
> > > the message is emitted.
> >
> > True, though we have __builtin_FILE and friends that can be used as
> > default args.
>
> If I'm understanding the idea, this means relying on implicit use of
> default arguments.   I'm not sure how compatible that is with variadic
> functions: the ellipsis has to come last.

True.

> I thought it was impossible to have default args with a variadic
> function, but it seems that this is syntactically valid:
>
> extern void dump_printf_loc (optinfo_groups_t,
>                              optinfo_location,
>                              const char *fmt,
>                              const char *impl_file = __builtin_FILE (),
>                              int impl_line = __builtin_LINE (),
>                              ...);
>
> That said, the above decl seems like a bad idea: a recipe for nasty
> surprises (what happens if the format arguments expect a const char *
> and an int?).

I'm surprised the above works ;)  Does it do what we expect?

> Idea: maybe the optinfo_location constructor could take the default
> args for a __builtin_FILE and __builtin_LINE?  Or the pending_optinfo
> class from patch 3 of the kit.  I'll experiment with this.

Ah, nice idea, yes.

> > Note a push/pop level API can also buffer intermediate printf style
> > output and annotate/indent it (supporting a dump file emacs/vim mode
> > that can do collapse and expand)
>
> Interesting idea.
>
> I had a go at emitting text in Emacs outline-mode format.  An example,
> showing the source location and pass/hotness metadata only for top-
> level messages:
>  https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-elided.txt
> and another example, showing them for all messages:
>  https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-unelided.txt
>
> and it works as-is with Emacs outline-mode (though I don't know how
> useful it is; would want a jump-to-source option etc etc).
>
> In both cases, this is prioritizing the messages, sorting from hottest
> code down to coldest code (or those messages emitted before the profile
> data was loaded), similar to the HTML report here:
> https://dmalcolm.fedorapeople.org/gcc/2018-05-18/pgo-demo-test/pgo-demo-test/
>
> (These are all being generated by reading the saved optimization
> records, rather than being emitted by the compiler itself)
>
> > > Another approach: there could be something like:
> > >
> > >  if (optinfo_enabled_p ())
> > >    {
> > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > >       OPTINFO_VECT_NOTE note;
> > >       note.printf ("create %s-pointer variable to type: ",
> > >                    get_tree_code_name (TREE_CODE (aggr_type)));
> > >       note.add_slim (aggr_type);
> > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > >         note.printf ("  vectorizing an array ref: ");
> > >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > >         note.printf (MSG_NOTE, "  vectorizing a vector ref: ");
> > >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > >    note.printf (MSG_NOTE, "  vectorizing a record based array ref:
> > > ");
> > >       else
> > >         note.printf (MSG_NOTE, "  vectorizing a pointer ref: ");
> > >       note.add_slim (DR_BASE_OBJECT (dr));
> > >    }
> > >
> > > which uses a macro to get the gcc source location, and avoids the
> > > special meaning for "\n".  This avoids the "<<" - but is kind of
> > > just
> > > different syntax for the same - it's hard with this example to
> > > avoid
> > > it.
> >
> > Maybe the following raii style that encapsulates the enabling
> > /disabling checks?
> >
> >  If (optinfo o = push (msg_optimization,...))
> >   {
> >      O.print (...) ;
> >      Destructor of o 'pops'
> >   }
>
> I'm assuming that:
> * very few users turn on the dump feature, and
> * a goal is that we shouldn't slow down that common "no dumping" case
> when implementing dumping.

Yes.

> If so, then presumably we need a really cheap test that can easily be
> marked as cold, or optimized away.
>
> How much work would be done by the call to:
>    push (msg_optimization,...),
> in particular evaluating the arguments?

I'd expect it to bail out quickly when !enabled()

> I was thinking:
>    if (optinfo_enabled_p ())
> and have it be a very cheap boolean lookup (though it isn't in the
> current patch kit), with everything else guarded by it.

Yeah.

> Would a GCC_UNLIKELY(expr) macro be appropriate here?  (so that non-PGO
> builds of the compiler can have all this dump stuff moved into cold
> sections)

Not sure, we could certainly experiment with that.

> > > Yet another approach: reworking things to support i18n via a pure
> > > printf-style interface, using, say "%S" to mean "TDF_SLIM", it
> > > could be
> > > like:
> > >
> > >  if (optinfo_enabled_p ())
> > >    {
> > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> > >                            "  vectorizing an array ref: %S",
> > >                            get_tree_code_name (TREE_CODE
> > > (aggr_type))
> > >                         aggr_type,
> > >                            DR_BASE_OBJECT (dr));
> > >      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> > >                            "  vectorizing a vector ref: %S",
> > >                            get_tree_code_name (TREE_CODE
> > > (aggr_type))
> > >                         aggr_type,
> > >                            DR_BASE_OBJECT (dr));
> > >      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> > >                          "  vectorizing a record based array ref:
> > > %S",
> > >                            get_tree_code_name (TREE_CODE
> > > (aggr_type))
> > >                         aggr_type,
> > >                            DR_BASE_OBJECT (dr));
> > >      else
> > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type: %S"
> > >                            "  vectorizing a pointer ref: %S",
> > >                            get_tree_code_name (TREE_CODE
> > > (aggr_type))
> > >                         aggr_type,
> > >                            DR_BASE_OBJECT (dr));
> > >    }
> > >
> > > or somesuch.  The %S would allow for the data to be captured when
> > > emitting optimization records for the messages (not just plain
> > > text,
> > > e.g. locations of the expressions).
> >
> > Certainly when exposing things in opt-info we have to be more
> > disciplined. The vectorizer is a bad example here.
> > The original goal of having one set of outputs for both dump files
> > and opt-info is good but I guess the result is less than optimal.
> > Maybe additional detail levels would help here (MSG_Dumpfile_only?)
> >
> > > So those are some ideas.  I'm sure there are other ways to fix the
> > > issues with -fopt-info; I'm brainstorming here.
> >
> > Likewise. As said I applaud the attempt improve the situation but I
> > detest a stream API ;)
>
> Thanks for the feedback.  I'll continue to try prototyping ideas.

Thanks a lot.

Note I think we can get incremental improvements, like committing
the enum change and a wrapper class for the location while keeping
the rest of the opt-info API.

Richard.

> Dave
>
> > Richard.
> >
> > > [...snip...]
> > >
> > > Thoughts?
> > >
> > > Thanks
> > > Dave
> >
> >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 01/10] Convert dump and optgroup flags to enums
  2018-06-01 10:00   ` Richard Biener
@ 2018-06-05  8:44     ` Trevor Saunders
  2018-06-05 14:49       ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Trevor Saunders @ 2018-06-05  8:44 UTC (permalink / raw)
  To: Richard Biener; +Cc: David Malcolm, GCC Patches

On Fri, Jun 01, 2018 at 12:00:09PM +0200, Richard Biener wrote:
> On Tue, May 29, 2018 at 10:32 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > The dump machinery uses "int" in a few places, for two different
> > sets of bitmasks.
> >
> > This patch makes things more self-documenting and type-safe by using
> > a new pair of enums: one for the dump_flags_t and another for the
> > optgroup_flags.
> 
> Great!  This should also make them accessible in gdb w/o using -g3.
> 
> > This requires adding some overloaded bit operations to the enums
> > in question, which, in this patch is done for each enum .  If the basic
> > idea is OK, should I add a template for this?  (with some kind of
> > magic to express that bitmasking operations are only supported on
> > certain opt-in enums).

You may want to look at gdb's enum-flags.h which I think already
implements what your doing here.

> 
> Does C++ allow > int enums?  I think we want some way of knowing
> when either enum exceeds int (dump_flags_t was already uint64_t
> but you now make it effectively int again).  That is, general wrapping
> for enum ops should chose an appropriate unsigned integer for
> the operation.  So yes, a common implementation looks useful to me.

I don't remember very well, but istr C++ will actually use a 8 byte
integer if the enum contains constants larger than 2^32.  Testing
sizeof enum x { A =0x400000000 }; gives the desired thing for me, but it
would still be good to check the standard.

Trev


> 
> I think this patch is independently useful.
> 
> Thanks,
> Richard.
> 
> >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums)
  2018-06-05  8:44     ` Trevor Saunders
@ 2018-06-05 14:49       ` David Malcolm
  2018-06-05 16:13         ` Pedro Alves
  2018-06-05 16:17         ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) Richard Biener
  0 siblings, 2 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-05 14:49 UTC (permalink / raw)
  To: Trevor Saunders, Richard Biener; +Cc: GCC Patches, Pedro Alves

On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
> On Fri, Jun 01, 2018 at 12:00:09PM +0200, Richard Biener wrote:
> > On Tue, May 29, 2018 at 10:32 PM David Malcolm <dmalcolm@redhat.com
> > > wrote:
> > > 
> > > The dump machinery uses "int" in a few places, for two different
> > > sets of bitmasks.
> > > 
> > > This patch makes things more self-documenting and type-safe by
> > > using
> > > a new pair of enums: one for the dump_flags_t and another for the
> > > optgroup_flags.
> > 
> > Great!  This should also make them accessible in gdb w/o using -g3.
> > 
> > > This requires adding some overloaded bit operations to the enums
> > > in question, which, in this patch is done for each enum .  If the
> > > basic
> > > idea is OK, should I add a template for this?  (with some kind of
> > > magic to express that bitmasking operations are only supported on
> > > certain opt-in enums).
> 
> You may want to look at gdb's enum-flags.h which I think already
> implements what your doing here.

Aha!  Thanks!

Browsing the git web UI, that gdb header was introduced by Pedro Alves
in:
  https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
and the current state is here:
  https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/common/enum-flags.h;hb=HEAD

I'll try this out with GCC; hopefully it works with C++98.

Presumably it would be good to share this header between GCC and GDB;
CCing Pedro; Pedro: hi!  Does this sound sane?
(for reference, the GCC patch we're discussing here is:
  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )

Presumably gcc's copy should live in the gcc's top-level "include"
subdirectory?

Would we need to change that "This file is part of GDB." comment, and
the include guards' "COMMON_ENUM_FLAGS_H"?

> > Does C++ allow > int enums?  I think we want some way of knowing
> > when either enum exceeds int (dump_flags_t was already uint64_t
> > but you now make it effectively int again).  That is, general
> > wrapping
> > for enum ops should chose an appropriate unsigned integer for
> > the operation.  So yes, a common implementation looks useful to me.
> 
> I don't remember very well, but istr C++ will actually use a 8 byte
> integer if the enum contains constants larger than 2^32.  Testing
> sizeof enum x { A =0x400000000 }; gives the desired thing for me, but
> it
> would still be good to check the standard.

FWIW C++11 onwards has a std::underlying_type for enums:
  http://en.cppreference.com/w/cpp/types/underlying_type
(GCC is on C++98).  The gdb header seems to emulate this via the use of
sizeof(T) to select an appropriate integer_for_size specialization and
thus the appropriate struct enum_underlying_type specialization (if I'm
reading it right).

> Trev
> 
> 
> > 
> > I think this patch is independently useful.

Richard: by this, did you mean that the patch is OK for trunk as-is?
(keeping a more general bitflags enum patch as followup work)  Note to
self: still needs bootstrap-and-testing.

> > Thanks,
> > Richard.

Thanks
Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums)
  2018-06-05 14:49       ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) David Malcolm
@ 2018-06-05 16:13         ` Pedro Alves
  2018-06-05 17:13           ` [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment David Malcolm
  2018-06-05 16:17         ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) Richard Biener
  1 sibling, 1 reply; 80+ messages in thread
From: Pedro Alves @ 2018-06-05 16:13 UTC (permalink / raw)
  To: David Malcolm, Trevor Saunders, Richard Biener; +Cc: GCC Patches

On 06/05/2018 03:49 PM, David Malcolm wrote:
> On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:

>> You may want to look at gdb's enum-flags.h which I think already
>> implements what your doing here.
> 
> Aha!  Thanks!
> 
> Browsing the git web UI, that gdb header was introduced by Pedro Alves
> in:
>   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
> and the current state is here:
>   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/common/enum-flags.h;hb=HEAD
> 
> I'll try this out with GCC; hopefully it works with C++98.

It should -- it was written/added while GDB still required C++98.

Note I have a WIP series here:

 https://github.com/palves/gdb/commits/palves/cxx11-enum-flags

that fixes a few things, and adds a bunch of unit tests.  In
the process, it uses C++11 constructs (hence the branch's name),
but I think that can be reverted to still work with C++98.

I had seen some noises recently about GCC maybe considering
requiring C++11.  Is that reasonable to expect in this cycle?
(GDB requires GCC 4.8 or later, FWIW.)

Getting that branch into master had fallen lower on my TODO,
but if there's interest, I can try to finish it off soon, so we
can share a better baseline.  (I posted it once, but found
some issues which I fixed since but never managed to repost.)

> 
> Presumably it would be good to share this header between GCC and GDB;
> CCing Pedro; Pedro: hi!  Does this sound sane?
> (for reference, the GCC patch we're discussing here is:
>   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )

Sure!

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums)
  2018-06-05 14:49       ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) David Malcolm
  2018-06-05 16:13         ` Pedro Alves
@ 2018-06-05 16:17         ` Richard Biener
  2018-06-08 12:59           ` [committed] v2: Convert dump and optgroup flags to enums David Malcolm
  1 sibling, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-05 16:17 UTC (permalink / raw)
  To: David Malcolm, Trevor Saunders; +Cc: GCC Patches, Pedro Alves

On June 5, 2018 4:49:21 PM GMT+02:00, David Malcolm <dmalcolm@redhat.com> wrote:
>On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
>> On Fri, Jun 01, 2018 at 12:00:09PM +0200, Richard Biener wrote:
>> > On Tue, May 29, 2018 at 10:32 PM David Malcolm <dmalcolm@redhat.com
>> > > wrote:
>> > > 
>> > > The dump machinery uses "int" in a few places, for two different
>> > > sets of bitmasks.
>> > > 
>> > > This patch makes things more self-documenting and type-safe by
>> > > using
>> > > a new pair of enums: one for the dump_flags_t and another for the
>> > > optgroup_flags.
>> > 
>> > Great!  This should also make them accessible in gdb w/o using -g3.
>> > 
>> > > This requires adding some overloaded bit operations to the enums
>> > > in question, which, in this patch is done for each enum .  If the
>> > > basic
>> > > idea is OK, should I add a template for this?  (with some kind of
>> > > magic to express that bitmasking operations are only supported on
>> > > certain opt-in enums).
>> 
>> You may want to look at gdb's enum-flags.h which I think already
>> implements what your doing here.
>
>Aha!  Thanks!
>
>Browsing the git web UI, that gdb header was introduced by Pedro Alves
>in:
>https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
>and the current state is here:
>https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/common/enum-flags.h;hb=HEAD
>
>I'll try this out with GCC; hopefully it works with C++98.
>
>Presumably it would be good to share this header between GCC and GDB;
>CCing Pedro; Pedro: hi!  Does this sound sane?
>(for reference, the GCC patch we're discussing here is:
>  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
>
>Presumably gcc's copy should live in the gcc's top-level "include"
>subdirectory?
>
>Would we need to change that "This file is part of GDB." comment, and
>the include guards' "COMMON_ENUM_FLAGS_H"?
>
>> > Does C++ allow > int enums?  I think we want some way of knowing
>> > when either enum exceeds int (dump_flags_t was already uint64_t
>> > but you now make it effectively int again).  That is, general
>> > wrapping
>> > for enum ops should chose an appropriate unsigned integer for
>> > the operation.  So yes, a common implementation looks useful to me.
>> 
>> I don't remember very well, but istr C++ will actually use a 8 byte
>> integer if the enum contains constants larger than 2^32.  Testing
>> sizeof enum x { A =0x400000000 }; gives the desired thing for me, but
>> it
>> would still be good to check the standard.
>
>FWIW C++11 onwards has a std::underlying_type for enums:
>  http://en.cppreference.com/w/cpp/types/underlying_type
>(GCC is on C++98).  The gdb header seems to emulate this via the use of
>sizeof(T) to select an appropriate integer_for_size specialization and
>thus the appropriate struct enum_underlying_type specialization (if I'm
>reading it right).
>
>> Trev
>> 
>> 
>> > 
>> > I think this patch is independently useful.
>
>Richard: by this, did you mean that the patch is OK for trunk as-is?

Yes. 

Richard. 

>(keeping a more general bitflags enum patch as followup work)  Note to
>self: still needs bootstrap-and-testing.
>
>> > Thanks,
>> > Richard.
>
>Thanks
>Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment
  2018-06-05 16:13         ` Pedro Alves
@ 2018-06-05 17:13           ` David Malcolm
  2018-06-05 17:32             ` Pedro Alves
  2018-06-06  0:48             ` Eric Gallager
  0 siblings, 2 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-05 17:13 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Trevor Saunders, Richard Biener, GCC Patches, David Malcolm

On Tue, 2018-06-05 at 17:13 +0100, Pedro Alves wrote:
> On 06/05/2018 03:49 PM, David Malcolm wrote:
> > On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
> > > You may want to look at gdb's enum-flags.h which I think already
> > > implements what your doing here.
> > 
> > Aha!  Thanks!
> > 
> > Browsing the git web UI, that gdb header was introduced by Pedro
> > Alves
> > in:
> >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit
> > diff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
> > and the current state is here:
> >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f
> > =gdb/common/enum-flags.h;hb=HEAD
> > 
> > I'll try this out with GCC; hopefully it works with C++98.
> 
> It should -- it was written/added while GDB still required C++98.

Thanks.

> Note I have a WIP series here:
> 
>  https://github.com/palves/gdb/commits/palves/cxx11-enum-flags
> 
> that fixes a few things, and adds a bunch of unit tests.  In
> the process, it uses C++11 constructs (hence the branch's name),
> but I think that can be reverted to still work with C++98.
> 
> I had seen some noises recently about GCC maybe considering
> requiring C++11.  Is that reasonable to expect in this cycle?
> (GDB requires GCC 4.8 or later, FWIW.)

I'm expecting that GCC 9 will still require C++98.

> Getting that branch into master had fallen lower on my TODO,
> but if there's interest, I can try to finish it off soon, so we
> can share a better baseline.  (I posted it once, but found
> some issues which I fixed since but never managed to repost.)

Interestingly, it looks like gdb is using Google Test for selftests;
gcc is using a hand-rolled API that is similar, but has numerous
differences (e.g. explicit calling of test functions, rather than
implicit test discovery).  So that's another area of drift between
the projects.

> > 
> > Presumably it would be good to share this header between GCC and
> > GDB;
> > CCing Pedro; Pedro: hi!  Does this sound sane?
> > (for reference, the GCC patch we're discussing here is:
> >   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
> 
> Sure!

Alternatively, if GCC needs to stay at C++98 and GDB moves on to C++11,
then we can at least take advantage of this tested and FSF-assigned
C++98 code (better than writing it from scratch).

I ran into one issue with the header, e.g. with:

  /* Dump flags type.  */
  DEF_ENUM_FLAGS_TYPE(enum dump_flag, dump_flags_t);

This doesn't work:
  TDF_SLIM | flags
but this does:
  flags | TDF_SLIM
where TDF_SLIM is one of the values of "enum dump_flag".

For example, the former leads to:

../../src/gcc/c-family/c-gimplify.c: In function ‘void c_genericize(tree)’:
../../src/gcc/c-family/c-gimplify.c:145:44: error: conversion from ‘int’ to ‘dump_flags_t’ {aka ‘enum_flags<dump_flag>’} is ambiguous
      TDF_SLIM | local_dump_flags, dump_orig);
                                            ^
In file included from ../../src/gcc/dumpfile.h:24,
                 from ../../src/gcc/coretypes.h:428,
                 from ../../src/gcc/c-family/c-gimplify.c:28:
../../src/gcc/../include/enum-flags.h:128:3: note: candidate: ‘enum_flags<E>::enum_flags(enum_flags<E>::zero_type*) [with E = dump_flag]’ <near match>
   enum_flags (struct enum_flags::zero_type *zero)
   ^~~~~~~~~~
../../src/gcc/../include/enum-flags.h:128:3: note:   conversion of argument 1 would be ill-formed:
../../src/gcc/c-family/c-gimplify.c:145:15: error: invalid conversion from ‘int’ to ‘enum_flags<dump_flag>::zero_type*’ [-fpermissive]
      TDF_SLIM | local_dump_flags, dump_orig);
      ~~~~~~~~~^~~~~~~~~~~~~~~~~~
In file included from ../../src/gcc/dumpfile.h:24,
                 from ../../src/gcc/coretypes.h:428,
                 from ../../src/gcc/c-family/c-gimplify.c:28:
../../src/gcc/../include/enum-flags.h:125:3: note: candidate: ‘enum_flags<E>::enum_flags(enum_flags<E>::enum_type) [with E = dump_flag; enum_flags<E>::enum_type = dump_flag]’ <near match>
   enum_flags (enum_type e)
   ^~~~~~~~~~
../../src/gcc/../include/enum-flags.h:125:3: note:   conversion of argument 1 would be ill-formed:
../../src/gcc/c-family/c-gimplify.c:145:15: error: invalid conversion from ‘int’ to ‘enum_flags<dump_flag>::enum_type’ {aka ‘dump_flag’} [-fpermissive]
      TDF_SLIM | local_dump_flags, dump_orig);
      ~~~~~~~~~^~~~~~~~~~~~~~~~~~
In file included from ../../src/gcc/coretypes.h:428,
                 from ../../src/gcc/c-family/c-gimplify.c:28:
../../src/gcc/dumpfile.h:248:13: note:   initializing argument 2 of ‘void dump_node(const_tree, dump_flags_t, FILE*)’
 extern void dump_node (const_tree, dump_flags_t, FILE *);
             ^~~~~~~~~

I'm not quite sure why it doesn't work that way around, but reversing
the order so that the dump_flags_t is on the left-hand-side lets it
work (it only affects 3 sites in gcc's source tree).


> Thanks,
> Pedro Alves

Thanks.

BTW, I spotted this trivial issue in a comment in enum-flags.h whilst
trying it out for gcc:



The DEF_ENUM_FLAGS_TYPE macro should be used with a trailing
semicolon, but the example in the comment lacks one.

	* enum-flags.h: Add trailing semicolon to example in comment.
---
 include/enum-flags.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/enum-flags.h b/include/enum-flags.h
index 65732c1..82568a5 100644
--- a/include/enum-flags.h
+++ b/include/enum-flags.h
@@ -32,7 +32,7 @@
        flag_val3 = 1 << 3,
        flag_val4 = 1 << 4,
     };
-    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags)
+    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags);
 
     some_flags f = flag_val1 | flag_val2;
     f |= flag_val3;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment
  2018-06-05 17:13           ` [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment David Malcolm
@ 2018-06-05 17:32             ` Pedro Alves
  2018-06-06 12:54               ` David Malcolm
  2018-06-06  0:48             ` Eric Gallager
  1 sibling, 1 reply; 80+ messages in thread
From: Pedro Alves @ 2018-06-05 17:32 UTC (permalink / raw)
  To: David Malcolm; +Cc: Trevor Saunders, Richard Biener, GCC Patches, GDB Patches

[adding gdb-patches]

On 06/05/2018 06:56 PM, David Malcolm wrote:
> On Tue, 2018-06-05 at 17:13 +0100, Pedro Alves wrote:
>> On 06/05/2018 03:49 PM, David Malcolm wrote:
>>> On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
>>>> You may want to look at gdb's enum-flags.h which I think already
>>>> implements what your doing here.
>>>
>>> Aha!  Thanks!
>>>
>>> Browsing the git web UI, that gdb header was introduced by Pedro
>>> Alves
>>> in:
>>>   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit
>>> diff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
>>> and the current state is here:
>>>   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f
>>> =gdb/common/enum-flags.h;hb=HEAD
>>>
>>> I'll try this out with GCC; hopefully it works with C++98.
>>
>> It should -- it was written/added while GDB still required C++98.
> 
> Thanks.
> 
>> Note I have a WIP series here:
>>
>>  https://github.com/palves/gdb/commits/palves/cxx11-enum-flags
>>
>> that fixes a few things, and adds a bunch of unit tests.  In
>> the process, it uses C++11 constructs (hence the branch's name),
>> but I think that can be reverted to still work with C++98.
>>
>> I had seen some noises recently about GCC maybe considering
>> requiring C++11.  Is that reasonable to expect in this cycle?
>> (GDB requires GCC 4.8 or later, FWIW.)
> 
> I'm expecting that GCC 9 will still require C++98.

OK.

> 
>> Getting that branch into master had fallen lower on my TODO,
>> but if there's interest, I can try to finish it off soon, so we
>> can share a better baseline.  (I posted it once, but found
>> some issues which I fixed since but never managed to repost.)
> 
> Interestingly, it looks like gdb is using Google Test for selftests;
> gcc is using a hand-rolled API that is similar, but has numerous
> differences (e.g. explicit calling of test functions, rather than
> implicit test discovery).  So that's another area of drift between
> the projects.

Nope, the unit tests framework is hand rolled too.  See gdb/selftest.h/c.
It can be invoked with gdb's "maint selftest" command.
You can see the list of tests with "maint info selftests", and
you can pass a filter to "maint selftest" too.

> 
>>>
>>> Presumably it would be good to share this header between GCC and
>>> GDB;
>>> CCing Pedro; Pedro: hi!  Does this sound sane?
>>> (for reference, the GCC patch we're discussing here is:
>>>   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
>>
>> Sure!
> 
> Alternatively, if GCC needs to stay at C++98 and GDB moves on to C++11,
> then we can at least take advantage of this tested and FSF-assigned
> C++98 code (better than writing it from scratch).

Agreed, but I'll try to see about making the fixes in the branch
C++98 compatible.

> 
> I ran into one issue with the header, e.g. with:
> 
>   /* Dump flags type.  */
>   DEF_ENUM_FLAGS_TYPE(enum dump_flag, dump_flags_t);
> 
> This doesn't work:
>   TDF_SLIM | flags
> but this does:
>   flags | TDF_SLIM
> where TDF_SLIM is one of the values of "enum dump_flag".

ISTR that that's fixed in the branch.

> BTW, I spotted this trivial issue in a comment in enum-flags.h whilst
> trying it out for gcc:
> 
> 
> 
> The DEF_ENUM_FLAGS_TYPE macro should be used with a trailing
> semicolon, but the example in the comment lacks one.
> 
> 	* enum-flags.h: Add trailing semicolon to example in comment.

Thanks.  I've merged it.

From 115f7325b5b76450b9a1d16848a2a54f54e49747 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 5 Jun 2018 18:22:25 +0100
Subject: [PATCH] Fix typo in common/enum-flags.h example

The DEF_ENUM_FLAGS_TYPE macro should be used with a trailing
semicolon, but the example in the comment lacks one.

gdb/ChangeLog:
2018-06-05  David Malcolm  <dmalcolm@redhat.com>

	* common/enum-flags.h: Add trailing semicolon to example in
	comment.
---
 gdb/ChangeLog           | 5 +++++
 gdb/common/enum-flags.h | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 85c62dbd86c..54205a6ea85 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2018-06-05  David Malcolm  <dmalcolm@redhat.com>
+
+	* common/enum-flags.h: Add trailing semicolon to example in
+	comment.
+
 2018-06-05  Tom Tromey	<tom@tromey.com>
 
 	PR cli/12326:
diff --git a/gdb/common/enum-flags.h b/gdb/common/enum-flags.h
index 65732c11a46..82568a5a3d9 100644
--- a/gdb/common/enum-flags.h
+++ b/gdb/common/enum-flags.h
@@ -32,7 +32,7 @@
        flag_val3 = 1 << 3,
        flag_val4 = 1 << 4,
     };
-    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags)
+    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags);
 
     some_flags f = flag_val1 | flag_val2;
     f |= flag_val3;
-- 
2.14.3

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment
  2018-06-05 17:13           ` [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment David Malcolm
  2018-06-05 17:32             ` Pedro Alves
@ 2018-06-06  0:48             ` Eric Gallager
  1 sibling, 0 replies; 80+ messages in thread
From: Eric Gallager @ 2018-06-06  0:48 UTC (permalink / raw)
  To: David Malcolm; +Cc: Pedro Alves, Trevor Saunders, Richard Biener, GCC Patches

On 6/5/18, David Malcolm <dmalcolm@redhat.com> wrote:
> On Tue, 2018-06-05 at 17:13 +0100, Pedro Alves wrote:
>> On 06/05/2018 03:49 PM, David Malcolm wrote:
>> > On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
>> > > You may want to look at gdb's enum-flags.h which I think already
>> > > implements what your doing here.
>> >
>> > Aha!  Thanks!
>> >
>> > Browsing the git web UI, that gdb header was introduced by Pedro
>> > Alves
>> > in:
>> >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit
>> > diff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
>> > and the current state is here:
>> >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f
>> > =gdb/common/enum-flags.h;hb=HEAD
>> >
>> > I'll try this out with GCC; hopefully it works with C++98.
>>
>> It should -- it was written/added while GDB still required C++98.
>
> Thanks.
>
>> Note I have a WIP series here:
>>
>>  https://github.com/palves/gdb/commits/palves/cxx11-enum-flags
>>
>> that fixes a few things, and adds a bunch of unit tests.  In
>> the process, it uses C++11 constructs (hence the branch's name),
>> but I think that can be reverted to still work with C++98.
>>
>> I had seen some noises recently about GCC maybe considering
>> requiring C++11.  Is that reasonable to expect in this cycle?
>> (GDB requires GCC 4.8 or later, FWIW.)
>
> I'm expecting that GCC 9 will still require C++98.

Not that I particularly want GCC to move to C++11 myself, but could
GCC at least switch from building with -Wno-narrowing to building with
-Wnarrowing? That'd make it easier to switch to C++11 when the time
comes, if anyone actually wants to do that.

>
>> Getting that branch into master had fallen lower on my TODO,
>> but if there's interest, I can try to finish it off soon, so we
>> can share a better baseline.  (I posted it once, but found
>> some issues which I fixed since but never managed to repost.)
>
> Interestingly, it looks like gdb is using Google Test for selftests;
> gcc is using a hand-rolled API that is similar, but has numerous
> differences (e.g. explicit calling of test functions, rather than
> implicit test discovery).  So that's another area of drift between
> the projects.
>
>> >
>> > Presumably it would be good to share this header between GCC and
>> > GDB;
>> > CCing Pedro; Pedro: hi!  Does this sound sane?
>> > (for reference, the GCC patch we're discussing here is:
>> >   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
>>
>> Sure!
>
> Alternatively, if GCC needs to stay at C++98 and GDB moves on to C++11,
> then we can at least take advantage of this tested and FSF-assigned
> C++98 code (better than writing it from scratch).
>
> I ran into one issue with the header, e.g. with:
>
>   /* Dump flags type.  */
>   DEF_ENUM_FLAGS_TYPE(enum dump_flag, dump_flags_t);
>
> This doesn't work:
>   TDF_SLIM | flags
> but this does:
>   flags | TDF_SLIM
> where TDF_SLIM is one of the values of "enum dump_flag".
>
> For example, the former leads to:
>
> ../../src/gcc/c-family/c-gimplify.c: In function ‘void c_genericize(tree)’:
> ../../src/gcc/c-family/c-gimplify.c:145:44: error: conversion from ‘int’ to
> ‘dump_flags_t’ {aka ‘enum_flags<dump_flag>’} is ambiguous
>       TDF_SLIM | local_dump_flags, dump_orig);
>                                             ^
> In file included from ../../src/gcc/dumpfile.h:24,
>                  from ../../src/gcc/coretypes.h:428,
>                  from ../../src/gcc/c-family/c-gimplify.c:28:
> ../../src/gcc/../include/enum-flags.h:128:3: note: candidate:
> ‘enum_flags<E>::enum_flags(enum_flags<E>::zero_type*) [with E = dump_flag]’
> <near match>
>    enum_flags (struct enum_flags::zero_type *zero)
>    ^~~~~~~~~~
> ../../src/gcc/../include/enum-flags.h:128:3: note:   conversion of argument
> 1 would be ill-formed:
> ../../src/gcc/c-family/c-gimplify.c:145:15: error: invalid conversion from
> ‘int’ to ‘enum_flags<dump_flag>::zero_type*’ [-fpermissive]
>       TDF_SLIM | local_dump_flags, dump_orig);
>       ~~~~~~~~~^~~~~~~~~~~~~~~~~~
> In file included from ../../src/gcc/dumpfile.h:24,
>                  from ../../src/gcc/coretypes.h:428,
>                  from ../../src/gcc/c-family/c-gimplify.c:28:
> ../../src/gcc/../include/enum-flags.h:125:3: note: candidate:
> ‘enum_flags<E>::enum_flags(enum_flags<E>::enum_type) [with E = dump_flag;
> enum_flags<E>::enum_type = dump_flag]’ <near match>
>    enum_flags (enum_type e)
>    ^~~~~~~~~~
> ../../src/gcc/../include/enum-flags.h:125:3: note:   conversion of argument
> 1 would be ill-formed:
> ../../src/gcc/c-family/c-gimplify.c:145:15: error: invalid conversion from
> ‘int’ to ‘enum_flags<dump_flag>::enum_type’ {aka ‘dump_flag’}
> [-fpermissive]
>       TDF_SLIM | local_dump_flags, dump_orig);
>       ~~~~~~~~~^~~~~~~~~~~~~~~~~~
> In file included from ../../src/gcc/coretypes.h:428,
>                  from ../../src/gcc/c-family/c-gimplify.c:28:
> ../../src/gcc/dumpfile.h:248:13: note:   initializing argument 2 of ‘void
> dump_node(const_tree, dump_flags_t, FILE*)’
>  extern void dump_node (const_tree, dump_flags_t, FILE *);
>              ^~~~~~~~~
>
> I'm not quite sure why it doesn't work that way around, but reversing
> the order so that the dump_flags_t is on the left-hand-side lets it
> work (it only affects 3 sites in gcc's source tree).
>
>
>> Thanks,
>> Pedro Alves
>
> Thanks.
>
> BTW, I spotted this trivial issue in a comment in enum-flags.h whilst
> trying it out for gcc:
>
>
>
> The DEF_ENUM_FLAGS_TYPE macro should be used with a trailing
> semicolon, but the example in the comment lacks one.
>
> 	* enum-flags.h: Add trailing semicolon to example in comment.
> ---
>  include/enum-flags.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/include/enum-flags.h b/include/enum-flags.h
> index 65732c1..82568a5 100644
> --- a/include/enum-flags.h
> +++ b/include/enum-flags.h
> @@ -32,7 +32,7 @@
>         flag_val3 = 1 << 3,
>         flag_val4 = 1 << 4,
>      };
> -    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags)
> +    DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags);
>
>      some_flags f = flag_val1 | flag_val2;
>      f |= flag_val3;
> --
> 1.8.5.3
>
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment
  2018-06-05 17:32             ` Pedro Alves
@ 2018-06-06 12:54               ` David Malcolm
  0 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-06 12:54 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Trevor Saunders, Richard Biener, GCC Patches, GDB Patches

On Tue, 2018-06-05 at 18:31 +0100, Pedro Alves wrote:
> [adding gdb-patches]
> 
> On 06/05/2018 06:56 PM, David Malcolm wrote:
> > On Tue, 2018-06-05 at 17:13 +0100, Pedro Alves wrote:
> > > On 06/05/2018 03:49 PM, David Malcolm wrote:
> > > > On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
> > > > > You may want to look at gdb's enum-flags.h which I think
> > > > > already
> > > > > implements what your doing here.
> > > > 
> > > > Aha!  Thanks!
> > > > 
> > > > Browsing the git web UI, that gdb header was introduced by
> > > > Pedro
> > > > Alves
> > > > in:
> > > >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=co
> > > > mmit
> > > > diff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
> > > > and the current state is here:
> > > >   https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=bl
> > > > ob;f
> > > > =gdb/common/enum-flags.h;hb=HEAD
> > > > 
> > > > I'll try this out with GCC; hopefully it works with C++98.
> > > 
> > > It should -- it was written/added while GDB still required C++98.
> > 
> > Thanks.
> > 
> > > Note I have a WIP series here:
> > > 
> > >  https://github.com/palves/gdb/commits/palves/cxx11-enum-flags
> > > 
> > > that fixes a few things, and adds a bunch of unit tests.  In
> > > the process, it uses C++11 constructs (hence the branch's name),
> > > but I think that can be reverted to still work with C++98.
> > > 
> > > I had seen some noises recently about GCC maybe considering
> > > requiring C++11.  Is that reasonable to expect in this cycle?
> > > (GDB requires GCC 4.8 or later, FWIW.)
> > 
> > I'm expecting that GCC 9 will still require C++98.
> 
> OK.
> 
> > 
> > > Getting that branch into master had fallen lower on my TODO,
> > > but if there's interest, I can try to finish it off soon, so we
> > > can share a better baseline.  (I posted it once, but found
> > > some issues which I fixed since but never managed to repost.)
> > 
> > Interestingly, it looks like gdb is using Google Test for
> > selftests;
> > gcc is using a hand-rolled API that is similar, but has numerous
> > differences (e.g. explicit calling of test functions, rather than
> > implicit test discovery).  So that's another area of drift between
> > the projects.
> 
> Nope, the unit tests framework is hand rolled too.  See
> gdb/selftest.h/c.
> It can be invoked with gdb's "maint selftest" command.
> You can see the list of tests with "maint info selftests", and
> you can pass a filter to "maint selftest" too.

Aha.  Thanks.  Looks like gdb and gcc each have different hand-rolled
selftest APIs:
* gdb's selftest.h/c has manual test registration; test failures are
handled via exceptions
* gcc's selftest.h/c has no test registration (test functions are
called explicitly); test failures are handled via "abort"

I'm not suggesting we try to unify these APIs at this time.

> > > > 
> > > > Presumably it would be good to share this header between GCC
> > > > and
> > > > GDB;
> > > > CCing Pedro; Pedro: hi!  Does this sound sane?
> > > > (for reference, the GCC patch we're discussing here is:
> > > >   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
> > > 
> > > Sure!
> > 
> > Alternatively, if GCC needs to stay at C++98 and GDB moves on to
> > C++11,
> > then we can at least take advantage of this tested and FSF-assigned
> > C++98 code (better than writing it from scratch).
> 
> Agreed, but I'll try to see about making the fixes in the branch
> C++98 compatible.

Thanks.

> > 
> > I ran into one issue with the header, e.g. with:
> > 
> >   /* Dump flags type.  */
> >   DEF_ENUM_FLAGS_TYPE(enum dump_flag, dump_flags_t);
> > 
> > This doesn't work:
> >   TDF_SLIM | flags
> > but this does:
> >   flags | TDF_SLIM
> > where TDF_SLIM is one of the values of "enum dump_flag".
> 
> ISTR that that's fixed in the branch.

Interesting; I'll have a look.

> > BTW, I spotted this trivial issue in a comment in enum-flags.h
> > whilst
> > trying it out for gcc:
> > 
> > 
> > 
> > The DEF_ENUM_FLAGS_TYPE macro should be used with a trailing
> > semicolon, but the example in the comment lacks one.
> > 
> > 	* enum-flags.h: Add trailing semicolon to example in comment.
> 
> Thanks.  I've merged it.

Thanks.

[...snip...]

Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [committed] v2: Convert dump and optgroup flags to enums
  2018-06-05 16:17         ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) Richard Biener
@ 2018-06-08 12:59           ` David Malcolm
  0 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-08 12:59 UTC (permalink / raw)
  To: Richard Biener; +Cc: Trevor Saunders, GCC Patches, Pedro Alves, David Malcolm

On Tue, 2018-06-05 at 18:16 +0200, Richard Biener wrote:
> On June 5, 2018 4:49:21 PM GMT+02:00, David Malcolm <dmalcolm@redhat.
> com> wrote:
> > On Tue, 2018-06-05 at 04:40 -0400, Trevor Saunders wrote:
> > > On Fri, Jun 01, 2018 at 12:00:09PM +0200, Richard Biener wrote:
> > > > On Tue, May 29, 2018 at 10:32 PM David Malcolm <dmalcolm@redhat
> > > > .com
> > > > > wrote:
> > > > > 
> > > > > The dump machinery uses "int" in a few places, for two
> > > > > different
> > > > > sets of bitmasks.
> > > > > 
> > > > > This patch makes things more self-documenting and type-safe
> > > > > by
> > > > > using
> > > > > a new pair of enums: one for the dump_flags_t and another for
> > > > > the
> > > > > optgroup_flags.
> > > > 
> > > > Great!  This should also make them accessible in gdb w/o using
> > > > -g3.
> > > > 
> > > > > This requires adding some overloaded bit operations to the
> > > > > enums
> > > > > in question, which, in this patch is done for each enum .  If
> > > > > the
> > > > > basic
> > > > > idea is OK, should I add a template for this?  (with some
> > > > > kind of
> > > > > magic to express that bitmasking operations are only
> > > > > supported on
> > > > > certain opt-in enums).
> > > 
> > > You may want to look at gdb's enum-flags.h which I think already
> > > implements what your doing here.
> > 
> > Aha!  Thanks!
> > 
> > Browsing the git web UI, that gdb header was introduced by Pedro
> > Alves
> > in:
> > https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdi
> > ff;h=8d297bbf604c8318ffc72d5a7b3db654409c5ed9
> > and the current state is here:
> > https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=g
> > db/common/enum-flags.h;hb=HEAD
> > 
> > I'll try this out with GCC; hopefully it works with C++98.
> > 
> > Presumably it would be good to share this header between GCC and
> > GDB;
> > CCing Pedro; Pedro: hi!  Does this sound sane?
> > (for reference, the GCC patch we're discussing here is:
> >  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01685.html )
> > 
> > Presumably gcc's copy should live in the gcc's top-level "include"
> > subdirectory?
> > 
> > Would we need to change that "This file is part of GDB." comment,
> > and
> > the include guards' "COMMON_ENUM_FLAGS_H"?
> > 
> > > > Does C++ allow > int enums?  I think we want some way of
> > > > knowing
> > > > when either enum exceeds int (dump_flags_t was already uint64_t
> > > > but you now make it effectively int again).  That is, general
> > > > wrapping
> > > > for enum ops should chose an appropriate unsigned integer for
> > > > the operation.  So yes, a common implementation looks useful to
> > > > me.
> > > 
> > > I don't remember very well, but istr C++ will actually use a 8
> > > byte
> > > integer if the enum contains constants larger than 2^32.  Testing
> > > sizeof enum x { A =0x400000000 }; gives the desired thing for me,
> > > but
> > > it
> > > would still be good to check the standard.
> > 
> > FWIW C++11 onwards has a std::underlying_type for enums:
> >  http://en.cppreference.com/w/cpp/types/underlying_type
> > (GCC is on C++98).  The gdb header seems to emulate this via the
> > use of
> > sizeof(T) to select an appropriate integer_for_size specialization
> > and
> > thus the appropriate struct enum_underlying_type specialization (if
> > I'm
> > reading it right).
> > 
> > > Trev
> > > 
> > > 
> > > > 
> > > > I think this patch is independently useful.
> > 
> > Richard: by this, did you mean that the patch is OK for trunk as-
> > is?
> 
> Yes. 
> 
> Richard. 
> 
> > (keeping a more general bitflags enum patch as followup work)  Note
> > to
> > self: still needs bootstrap-and-testing.

Thanks.

The patch needed some trivial fixes, which seemed sufficiently obvious
as to not need further approval:

* Use dump_flags_t rather than int for "dump_kind" param for
dump_basic_block, dump_generic_expr_loc's prototype (not just
the definition), and dump_dec.
* Removed "FIXME" comments
* Remove trailing comma from last item in enum dump_flag (for -Wpedantic).
* Fixed brig-to-generic.cc and graphite-poly.c (my earlier testing didn't
have brig or graphite enabled).

For reference here's what I've committed to trunk (r261325; I'm looking
at using gdb's enum-flags.h as follow-up):

gcc/brig/ChangeLog:
	* brigfrontend/brig-to-generic.cc
	(brig_to_generic::write_globals): Use TDF_NONE rather than 0.
	(dump_function): Likewise.

gcc/c-family/ChangeLog:
	* c-pretty-print.c (c_pretty_printer::statement): Use TDF_NONE
	rather than 0.

gcc/ChangeLog:
	* cfg.c (debug): Use TDF_NONE rather than 0.
	* cfghooks.c (debug): Likewise.
	* dumpfile.c (DUMP_FILE_INFO): Likewise; also for OPTGROUP.
	(struct dump_option_value_info): Convert to...
	(struct kv_pair): ...this template type.
	(dump_options): Convert to kv_pair<dump_flags_t>; use TDF_NONE
	rather than 0.
	(optinfo_verbosity_options): Likewise.
	(optgroup_options): Convert to kv_pair<optgroup_flags_t>; use
	OPTGROUP_NONE.
	(gcc::dump_manager::dump_register): Use optgroup_flags_t rather
	than int for "optgroup_flags" param.
	(dump_generic_expr_loc): Use dump_flags_t rather than int for
	"dump_kind" param.
	(dump_dec): Likewise.
	(dump_finish): Use TDF_NONE rather than 0.
	(gcc::dump_manager::opt_info_enable_passes): Use optgroup_flags_t
	rather than int for "optgroup_flags" param.  Use TDF_NONE rather
	than 0.  Update for change to option_ptr.
	(opt_info_switch_p_1): Convert "optgroup_flags" param from int *
	to optgroup_flags_t *.  Use TDF_NONE and OPTGROUP_NONE rather than
	0.  Update for changes to optinfo_verbosity_options and
	optgroup_options.
	(opt_info_switch_p): Convert optgroup_flags from int to
	optgroup_flags_t.
	(dump_basic_block): Use dump_flags_t rather than int
	for "dump_kind" param.
	* dumpfile.h (TDF_ADDRESS, TDF_SLIM, TDF_RAW, TDF_DETAILS,
	TDF_STATS, TDF_BLOCKS, TDF_VOPS, TDF_LINENO, TDF_UID)
	TDF_STMTADDR, TDF_GRAPH, TDF_MEMSYMS, TDF_RHS_ONLY, TDF_ASMNAME,
	TDF_EH, TDF_NOUID, TDF_ALIAS, TDF_ENUMERATE_LOCALS, TDF_CSELIB,
	TDF_SCEV, TDF_GIMPLE, TDF_FOLDING, MSG_OPTIMIZED_LOCATIONS,
	MSG_MISSED_OPTIMIZATION, MSG_NOTE, MSG_ALL, TDF_COMPARE_DEBUG,
	TDF_NONE): Convert from macros to...
	(enum dump_flag): ...this new enum.
	(dump_flags_t): Update to use enum.
	(operator|, operator&, operator~, operator|=, operator&=):
	Implement for dump_flags_t.
	(OPTGROUP_NONE, OPTGROUP_IPA, OPTGROUP_LOOP, OPTGROUP_INLINE,
	OPTGROUP_OMP, OPTGROUP_VEC, OPTGROUP_OTHER, OPTGROUP_ALL):
	Convert from macros to...
	(enum optgroup_flag): ...this new enum.
	(optgroup_flags_t): New typedef.
	(operator|, operator|=): Implement for optgroup_flags_t.
	(struct dump_file_info): Convert field "alt_flags" to
	dump_flags_t.  Convert field "optgroup_flags" to
	optgroup_flags_t.
	(dump_basic_block): Use dump_flags_t rather than int for param.
	(dump_generic_expr_loc): Likewise.
	(dump_dec): Likewise.
	(dump_register): Convert param "optgroup_flags" to
	optgroup_flags_t.
	(opt_info_enable_passes): Likewise.
	* early-remat.c (early_remat::dump_edge_list): Use TDF_NONE rather
	than 0.
	* gimple-pretty-print.c (debug): Likewise.
	* gimple-ssa-store-merging.c (bswap_replace): Likewise.
	(merged_store_group::apply_stores): Likewise.
	* gimple-ssa-strength-reduction.c (insert_initializers): Likewise.
	* gimple.c (verify_gimple_pp): Likewise.
	* graphite-poly.c (print_pbb_body): Likewise.
	* passes.c (pass_manager::register_one_dump_file): Convert
	local "optgroup_flags" to optgroup_flags_t.
	* print-tree.c (print_node): Use TDF_NONE rather than 0.
	(debug): Likewise.
	(debug_body): Likewise.
	* tree-pass.h (struct pass_data): Convert field "optgroup_flags"
	to optgroup_flags_t.
	* tree-pretty-print.c (print_struct_decl): Use TDF_NONE rather
	than 0.
	* tree-ssa-math-opts.c (convert_mult_to_fma_1): Likewise.
	(convert_mult_to_fma): Likewise.
	* tree-ssa-reassoc.c (undistribute_ops_list): Likewise.
	* tree-ssa-sccvn.c (vn_eliminate): Likewise.
	* tree-vect-data-refs.c (dump_lower_bound): Convert param
	"dump_kind" to dump_flags_t.
---
 gcc/brig/brigfrontend/brig-to-generic.cc |   9 +-
 gcc/c-family/c-pretty-print.c            |   2 +-
 gcc/cfg.c                                |   4 +-
 gcc/cfghooks.c                           |   2 +-
 gcc/dumpfile.c                           |  70 +++++-----
 gcc/dumpfile.h                           | 231 +++++++++++++++++++++++--------
 gcc/early-remat.c                        |   2 +-
 gcc/gimple-pretty-print.c                |   2 +-
 gcc/gimple-ssa-store-merging.c           |   6 +-
 gcc/gimple-ssa-strength-reduction.c      |   2 +-
 gcc/gimple.c                             |   2 +-
 gcc/graphite-poly.c                      |   2 +-
 gcc/passes.c                             |   2 +-
 gcc/print-tree.c                         |   7 +-
 gcc/tree-pass.h                          |   2 +-
 gcc/tree-pretty-print.c                  |   2 +-
 gcc/tree-ssa-math-opts.c                 |   4 +-
 gcc/tree-ssa-reassoc.c                   |   2 +-
 gcc/tree-ssa-sccvn.c                     |   2 +-
 gcc/tree-vect-data-refs.c                |   2 +-
 20 files changed, 236 insertions(+), 121 deletions(-)

diff --git a/gcc/brig/brigfrontend/brig-to-generic.cc b/gcc/brig/brigfrontend/brig-to-generic.cc
index ee212b1..6629db0 100644
--- a/gcc/brig/brigfrontend/brig-to-generic.cc
+++ b/gcc/brig/brigfrontend/brig-to-generic.cc
@@ -932,8 +932,9 @@ brig_to_generic::write_globals ()
 	  fprintf (m_dump_file, "\n;; Function %s", kern_name.c_str());
 	  fprintf (m_dump_file, "\n;; enabled by -%s\n\n",
 		   dump_flag_name (TDI_original));
-	  print_generic_decl (m_dump_file, launcher, 0);
-	  print_generic_expr (m_dump_file, DECL_SAVED_TREE (launcher), 0);
+	  print_generic_decl (m_dump_file, launcher, TDF_NONE);
+	  print_generic_expr (m_dump_file, DECL_SAVED_TREE (launcher),
+			      TDF_NONE);
 	  fprintf (m_dump_file, "\n");
 	}
 
@@ -1018,8 +1019,8 @@ dump_function (FILE *dump_file, brig_function *f)
       fprintf (dump_file, "\n;; Function %s", f->m_name.c_str ());
       fprintf (dump_file, "\n;; enabled by -%s\n\n",
 	       dump_flag_name (TDI_original));
-      print_generic_decl (dump_file, f->m_func_decl, 0);
-      print_generic_expr (dump_file, f->m_current_bind_expr, 0);
+      print_generic_decl (dump_file, f->m_func_decl, TDF_NONE);
+      print_generic_expr (dump_file, f->m_current_bind_expr, TDF_NONE);
       fprintf (dump_file, "\n");
     }
 }
diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index dc76c99..efb41c5 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -2341,7 +2341,7 @@ c_pretty_printer::statement (tree stmt)
   if (pp_needs_newline (this))
     pp_newline_and_indent (this, 0);
 
-  dump_generic_node (this, stmt, pp_indentation (this), 0, true);
+  dump_generic_node (this, stmt, pp_indentation (this), TDF_NONE, true);
 }
 
 \f
diff --git a/gcc/cfg.c b/gcc/cfg.c
index 11026e7..6d55516 100644
--- a/gcc/cfg.c
+++ b/gcc/cfg.c
@@ -545,8 +545,8 @@ DEBUG_FUNCTION void
 debug (edge_def &ref)
 {
   /* FIXME (crowl): Is this desireable?  */
-  dump_edge_info (stderr, &ref, 0, false);
-  dump_edge_info (stderr, &ref, 0, true);
+  dump_edge_info (stderr, &ref, TDF_NONE, false);
+  dump_edge_info (stderr, &ref, TDF_NONE, true);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/cfghooks.c b/gcc/cfghooks.c
index 87d864c..ea106e0 100644
--- a/gcc/cfghooks.c
+++ b/gcc/cfghooks.c
@@ -288,7 +288,7 @@ dump_bb (FILE *outf, basic_block bb, int indent, dump_flags_t flags)
 DEBUG_FUNCTION void
 debug (basic_block_def &ref)
 {
-  dump_bb (stderr, &ref, 0, 0);
+  dump_bb (stderr, &ref, 0, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 0f16d4f..e94e274 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -50,8 +50,8 @@ const char *dump_file_name;
 dump_flags_t dump_flags;
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
-  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, 0, 0, 0, 0, 0, num, \
-   false, false}
+  {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
+   OPTGROUP_NONE, 0, 0, num, false, false}
 
 /* Table of tree dump switches. This must be consistent with the
    TREE_DUMP_INDEX enumeration in dumpfile.h.  */
@@ -74,15 +74,16 @@ static struct dump_file_info dump_files[TDI_end] =
 };
 
 /* Define a name->number mapping for a dump flag value.  */
-struct dump_option_value_info
+template <typename ValueType>
+struct kv_pair
 {
   const char *const name;	/* the name of the value */
-  const dump_flags_t value;	/* the value of the name */
+  const ValueType value;	/* the value of the name */
 };
 
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
-static const struct dump_option_value_info dump_options[] =
+static const kv_pair<dump_flags_t> dump_options[] =
 {
   {"address", TDF_ADDRESS},
   {"asmname", TDF_ASMNAME},
@@ -114,23 +115,23 @@ static const struct dump_option_value_info dump_options[] =
   {"all", dump_flags_t (~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_GRAPH
 			| TDF_STMTADDR | TDF_RHS_ONLY | TDF_NOUID
 			| TDF_ENUMERATE_LOCALS | TDF_SCEV | TDF_GIMPLE))},
-  {NULL, 0}
+  {NULL, TDF_NONE}
 };
 
 /* A subset of the dump_options table which is used for -fopt-info
    types. This must be consistent with the MSG_* flags in dumpfile.h.
  */
-static const struct dump_option_value_info optinfo_verbosity_options[] =
+static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 {
   {"optimized", MSG_OPTIMIZED_LOCATIONS},
   {"missed", MSG_MISSED_OPTIMIZATION},
   {"note", MSG_NOTE},
   {"all", MSG_ALL},
-  {NULL, 0}
+  {NULL, TDF_NONE}
 };
 
 /* Flags used for -fopt-info groups.  */
-static const struct dump_option_value_info optgroup_options[] =
+static const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -138,7 +139,7 @@ static const struct dump_option_value_info optgroup_options[] =
   {"omp", OPTGROUP_OMP},
   {"vec", OPTGROUP_VEC},
   {"optall", OPTGROUP_ALL},
-  {NULL, 0}
+  {NULL, OPTGROUP_NONE}
 };
 
 gcc::dump_manager::dump_manager ():
@@ -173,7 +174,8 @@ gcc::dump_manager::~dump_manager ()
 unsigned int
 gcc::dump_manager::
 dump_register (const char *suffix, const char *swtch, const char *glob,
-	       dump_kind dkind, int optgroup_flags, bool take_ownership)
+	       dump_kind dkind, optgroup_flags_t optgroup_flags,
+	       bool take_ownership)
 {
   int num = m_next_dump++;
 
@@ -425,7 +427,7 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
    location.  */
 
 void
-dump_generic_expr_loc (int dump_kind, source_location loc,
+dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
 		       dump_flags_t extra_dump_flags, tree t)
 {
   if (dump_file && (dump_kind & pflags))
@@ -492,7 +494,7 @@ dump_printf_loc (dump_flags_t dump_kind, source_location loc,
 
 template<unsigned int N, typename C>
 void
-dump_dec (int dump_kind, const poly_int<N, C> &value)
+dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
   STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
   signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
@@ -503,11 +505,11 @@ dump_dec (int dump_kind, const poly_int<N, C> &value)
     print_dec (value, alt_dump_file, sgn);
 }
 
-template void dump_dec (int, const poly_uint16 &);
-template void dump_dec (int, const poly_int64 &);
-template void dump_dec (int, const poly_uint64 &);
-template void dump_dec (int, const poly_offset_int &);
-template void dump_dec (int, const poly_widest_int &);
+template void dump_dec (dump_flags_t, const poly_uint16 &);
+template void dump_dec (dump_flags_t, const poly_int64 &);
+template void dump_dec (dump_flags_t, const poly_uint64 &);
+template void dump_dec (dump_flags_t, const poly_offset_int &);
+template void dump_dec (dump_flags_t, const poly_widest_int &);
 
 /* Start a dump for PHASE. Store user-supplied dump flags in
    *FLAG_PTR.  Return the number of streams opened.  Set globals
@@ -581,9 +583,9 @@ dump_finish (int phase)
   dfi->pstream = NULL;
   dump_file = NULL;
   alt_dump_file = NULL;
-  dump_flags = TDI_none;
-  alt_flags = 0;
-  pflags = 0;
+  dump_flags = TDF_NONE;
+  alt_flags = TDF_NONE;
+  pflags = TDF_NONE;
 }
 
 /* Begin a tree dump for PHASE. Stores any user supplied flag in
@@ -749,7 +751,7 @@ dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename)
 
 int
 gcc::dump_manager::
-opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
+opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
 			const char *filename)
 {
   int n = 0;
@@ -816,11 +818,11 @@ dump_switch_p_1 (const char *arg, struct dump_file_info *dfi, bool doglob)
     return 0;
 
   ptr = option_value;
-  flags = 0;
+  flags = TDF_NONE;
 
   while (*ptr)
     {
-      const struct dump_option_value_info *option_ptr;
+      const struct kv_pair<dump_flags_t> *option_ptr;
       const char *end_ptr;
       const char *eq_ptr;
       unsigned length;
@@ -902,8 +904,8 @@ dump_switch_p (const char *arg)
    and filename.  Return non-zero if it is a recognized switch.  */
 
 static int
-opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
-                     char **filename)
+opt_info_switch_p_1 (const char *arg, dump_flags_t *flags,
+		     optgroup_flags_t *optgroup_flags, char **filename)
 {
   const char *option_value;
   const char *ptr;
@@ -912,15 +914,14 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
   ptr = option_value;
 
   *filename = NULL;
-  *flags = 0;
-  *optgroup_flags = 0;
+  *flags = TDF_NONE;
+  *optgroup_flags = OPTGROUP_NONE;
 
   if (!ptr)
     return 1;       /* Handle '-fopt-info' without any additional options.  */
 
   while (*ptr)
     {
-      const struct dump_option_value_info *option_ptr;
       const char *end_ptr;
       const char *eq_ptr;
       unsigned length;
@@ -937,8 +938,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
 	end_ptr = ptr + strlen (ptr);
       length = end_ptr - ptr;
 
-      for (option_ptr = optinfo_verbosity_options; option_ptr->name;
-           option_ptr++)
+      for (const kv_pair<dump_flags_t> *option_ptr = optinfo_verbosity_options;
+	   option_ptr->name; option_ptr++)
 	if (strlen (option_ptr->name) == length
 	    && !memcmp (option_ptr->name, ptr, length))
           {
@@ -946,7 +947,8 @@ opt_info_switch_p_1 (const char *arg, dump_flags_t *flags, int *optgroup_flags,
 	    goto found;
           }
 
-      for (option_ptr = optgroup_options; option_ptr->name; option_ptr++)
+      for (const kv_pair<optgroup_flags_t> *option_ptr = optgroup_options;
+	   option_ptr->name; option_ptr++)
 	if (strlen (option_ptr->name) == length
 	    && !memcmp (option_ptr->name, ptr, length))
           {
@@ -981,7 +983,7 @@ int
 opt_info_switch_p (const char *arg)
 {
   dump_flags_t flags;
-  int optgroup_flags;
+  optgroup_flags_t optgroup_flags;
   char *filename;
   static char *file_seen = NULL;
   gcc::dump_manager *dumps = g->get_dumps ();
@@ -1012,7 +1014,7 @@ opt_info_switch_p (const char *arg)
 /* Print basic block on the dump streams.  */
 
 void
-dump_basic_block (int dump_kind, basic_block bb, int indent)
+dump_basic_block (dump_flags_t dump_kind, basic_block bb, int indent)
 {
   if (dump_file && (dump_kind & pflags))
     dump_bb (dump_file, bb, indent, TDF_DETAILS);
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 21803a6..153f91e 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -58,65 +58,175 @@ enum dump_kind
    the DUMP_OPTIONS array in dumpfile.c. The TDF_* flags coexist with
    MSG_* flags (for -fopt-info) and the bit values must be chosen to
    allow that.  */
-#define TDF_ADDRESS	(1 << 0)	/* dump node addresses */
-#define TDF_SLIM	(1 << 1)	/* don't go wild following links */
-#define TDF_RAW		(1 << 2)	/* don't unparse the function */
-#define TDF_DETAILS	(1 << 3)	/* show more detailed info about
-					   each pass */
-#define TDF_STATS	(1 << 4)	/* dump various statistics about
-					   each pass */
-#define TDF_BLOCKS	(1 << 5)	/* display basic block boundaries */
-#define TDF_VOPS	(1 << 6)	/* display virtual operands */
-#define TDF_LINENO	(1 << 7)	/* display statement line numbers */
-#define TDF_UID		(1 << 8)	/* display decl UIDs */
-
-#define TDF_STMTADDR	(1 << 9)       /* Address of stmt.  */
-
-#define TDF_GRAPH	(1 << 10)	/* a graph dump is being emitted */
-#define TDF_MEMSYMS	(1 << 11)	/* display memory symbols in expr.
-					   Implies TDF_VOPS.  */
-
-#define TDF_RHS_ONLY	(1 << 12)	/* a flag to only print the RHS of
-					   a gimple stmt.  */
-#define TDF_ASMNAME	(1 << 13)	/* display asm names of decls  */
-#define TDF_EH		(1 << 14)	/* display EH region number
-					   holding this gimple statement.  */
-#define TDF_NOUID	(1 << 15)	/* omit UIDs from dumps.  */
-#define TDF_ALIAS	(1 << 16)	/* display alias information  */
-#define TDF_ENUMERATE_LOCALS (1 << 17)	/* Enumerate locals by uid.  */
-#define TDF_CSELIB	(1 << 18)	/* Dump cselib details.  */
-#define TDF_SCEV	(1 << 19)	/* Dump SCEV details.  */
-#define TDF_GIMPLE	(1 << 20)	/* Dump in GIMPLE FE syntax  */
-#define TDF_FOLDING	(1 << 21)	/* Dump folding details.  */
-#define MSG_OPTIMIZED_LOCATIONS	 (1 << 22)  /* -fopt-info optimized sources */
-#define MSG_MISSED_OPTIMIZATION	 (1 << 23)  /* missed opportunities */
-#define MSG_NOTE		 (1 << 24)  /* general optimization info */
-#define MSG_ALL		(MSG_OPTIMIZED_LOCATIONS | MSG_MISSED_OPTIMIZATION \
-			 | MSG_NOTE)
-#define TDF_COMPARE_DEBUG (1 << 25)	/* Dumping for -fcompare-debug.  */
-
-
-/* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
-
-#define TDF_NONE 0
+enum dump_flag
+{
+  /* Value of TDF_NONE is used just for bits filtered by TDF_KIND_MASK.  */
+  TDF_NONE  = 0,
+
+  /* Dump node addresses.  */
+  TDF_ADDRESS = (1 << 0),
+
+  /* Don't go wild following links.  */
+  TDF_SLIM = (1 << 1),
+
+  /* Don't unparse the function.  */
+  TDF_RAW = (1 << 2),
+
+  /* Show more detailed info about each pass.  */
+  TDF_DETAILS = (1 << 3),
+
+  /* Dump various statistics about each pass.  */
+  TDF_STATS = (1 << 4),
+
+  /* Display basic block boundaries.  */
+  TDF_BLOCKS = (1 << 5),
+
+  /* Display virtual operands.  */
+  TDF_VOPS = (1 << 6),
+
+  /* Display statement line numbers.  */
+  TDF_LINENO = (1 << 7),
+
+  /* Display decl UIDs.  */
+  TDF_UID  = (1 << 8),
+
+  /* Address of stmt.  */
+  TDF_STMTADDR = (1 << 9),
+
+  /* A graph dump is being emitted.  */
+  TDF_GRAPH = (1 << 10),
+
+  /* Display memory symbols in expr.
+     Implies TDF_VOPS.  */
+  TDF_MEMSYMS = (1 << 11),
+
+  /* A flag to only print the RHS of a gimple stmt.  */
+  TDF_RHS_ONLY = (1 << 12),
+
+  /* Display asm names of decls.  */
+  TDF_ASMNAME = (1 << 13),
+
+  /* Display EH region number holding this gimple statement.  */
+  TDF_EH  = (1 << 14),
+
+  /* Omit UIDs from dumps.  */
+  TDF_NOUID = (1 << 15),
+
+  /* Display alias information.  */
+  TDF_ALIAS = (1 << 16),
+
+  /* Enumerate locals by uid.  */
+  TDF_ENUMERATE_LOCALS = (1 << 17),
+
+  /* Dump cselib details.  */
+  TDF_CSELIB = (1 << 18),
+
+  /* Dump SCEV details.  */
+  TDF_SCEV = (1 << 19),
+
+  /* Dump in GIMPLE FE syntax  */
+  TDF_GIMPLE = (1 << 20),
+
+  /* Dump folding details.  */
+  TDF_FOLDING = (1 << 21),
+
+  /* -fopt-info optimized sources.  */
+  MSG_OPTIMIZED_LOCATIONS = (1 << 22),
+
+  /* Missed opportunities.  */
+  MSG_MISSED_OPTIMIZATION = (1 << 23),
+
+  /* General optimization info.  */
+  MSG_NOTE = (1 << 24),
+
+  MSG_ALL = (MSG_OPTIMIZED_LOCATIONS
+	     | MSG_MISSED_OPTIMIZATION
+	     | MSG_NOTE),
+
+  /* Dumping for -fcompare-debug.  */
+  TDF_COMPARE_DEBUG = (1 << 25)
+};
+
+/* Dump flags type.  */
+
+typedef enum dump_flag dump_flags_t;
+
+static inline dump_flags_t
+operator| (dump_flags_t lhs, dump_flags_t rhs)
+{
+  return (dump_flags_t)((int)lhs | (int)rhs);
+}
+
+static inline dump_flags_t
+operator& (dump_flags_t lhs, dump_flags_t rhs)
+{
+  return (dump_flags_t)((int)lhs & (int)rhs);
+}
+
+static inline dump_flags_t
+operator~ (dump_flags_t flags)
+{
+  return (dump_flags_t)~((int)flags);
+}
+
+static inline dump_flags_t &
+operator|= (dump_flags_t &lhs, dump_flags_t rhs)
+{
+  lhs = (dump_flags_t)((int)lhs | (int)rhs);
+  return lhs;
+}
+
+static inline dump_flags_t &
+operator&= (dump_flags_t &lhs, dump_flags_t rhs)
+{
+  lhs = (dump_flags_t)((int)lhs & (int)rhs);
+  return lhs;
+}
 
 /* Flags to control high-level -fopt-info dumps.  Usually these flags
    define a group of passes.  An optimization pass can be part of
    multiple groups.  */
-#define OPTGROUP_NONE	     (0)
-#define OPTGROUP_IPA	     (1 << 1)	/* IPA optimization passes */
-#define OPTGROUP_LOOP	     (1 << 2)	/* Loop optimization passes */
-#define OPTGROUP_INLINE	     (1 << 3)	/* Inlining passes */
-#define OPTGROUP_OMP	     (1 << 4)	/* OMP (Offloading and Multi
-					   Processing) transformations */
-#define OPTGROUP_VEC	     (1 << 5)	/* Vectorization passes */
-#define OPTGROUP_OTHER	     (1 << 6)	/* All other passes */
-#define OPTGROUP_ALL	     (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE \
-			      | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
 
-/* Dump flags type.  */
+enum optgroup_flag
+{
+  OPTGROUP_NONE = 0,
+
+  /* IPA optimization passes */
+  OPTGROUP_IPA  = (1 << 1),
+
+  /* Loop optimization passes */
+  OPTGROUP_LOOP = (1 << 2),
+
+  /* Inlining passes */
+  OPTGROUP_INLINE = (1 << 3),
 
-typedef uint64_t dump_flags_t;
+  /* OMP (Offloading and Multi Processing) transformations */
+  OPTGROUP_OMP = (1 << 4),
+
+  /* Vectorization passes */
+  OPTGROUP_VEC = (1 << 5),
+
+  /* All other passes */
+  OPTGROUP_OTHER = (1 << 6),
+
+  OPTGROUP_ALL = (OPTGROUP_IPA | OPTGROUP_LOOP | OPTGROUP_INLINE
+		  | OPTGROUP_OMP | OPTGROUP_VEC | OPTGROUP_OTHER)
+};
+
+typedef enum optgroup_flag optgroup_flags_t;
+
+static inline optgroup_flags_t
+operator| (optgroup_flags_t lhs, optgroup_flags_t rhs)
+{
+  return (optgroup_flags_t)((int)lhs | (int)rhs);
+}
+
+static inline optgroup_flags_t &
+operator|= (optgroup_flags_t &lhs, optgroup_flags_t rhs)
+{
+  lhs = (optgroup_flags_t)((int)lhs | (int)rhs);
+  return lhs;
+}
 
 /* Define a tree dump switch.  */
 struct dump_file_info
@@ -140,9 +250,9 @@ struct dump_file_info
   /* Dump flags.  */
   dump_flags_t pflags;
   /* A pass flags for -fopt-info.  */
-  int alt_flags;
+  dump_flags_t alt_flags;
   /* Flags for -fopt-info given by a user.  */
-  int optgroup_flags;
+  optgroup_flags_t optgroup_flags;
   /* State of pass-specific stream.  */
   int pstate;
   /* State of the -fopt-info stream.  */
@@ -166,8 +276,8 @@ extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
 extern void dump_printf_loc (dump_flags_t, source_location,
                              const char *, ...) ATTRIBUTE_PRINTF_3;
 extern void dump_function (int phase, tree fn);
-extern void dump_basic_block (int, basic_block, int);
-extern void dump_generic_expr_loc (int, source_location, int, tree);
+extern void dump_basic_block (dump_flags_t, basic_block, int);
+extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
 extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
 extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
 				  gimple *, int);
@@ -176,7 +286,7 @@ extern void print_combine_total_stats (void);
 extern bool enable_rtl_dump_file (void);
 
 template<unsigned int N, typename C>
-void dump_dec (int, const poly_int<N, C> &);
+void dump_dec (dump_flags_t, const poly_int<N, C> &);
 
 /* In tree-dump.c  */
 extern void dump_node (const_tree, dump_flags_t, FILE *);
@@ -214,7 +324,8 @@ public:
      SUFFIX, SWTCH, and GLOB. */
   unsigned int
   dump_register (const char *suffix, const char *swtch, const char *glob,
-		 dump_kind dkind, int optgroup_flags, bool take_ownership);
+		 dump_kind dkind, optgroup_flags_t optgroup_flags,
+		 bool take_ownership);
 
   /* Allow languages and middle-end to register their dumps before the
      optimization passes.  */
@@ -275,7 +386,7 @@ private:
   dump_enable_all (dump_kind dkind, dump_flags_t flags, const char *filename);
 
   int
-  opt_info_enable_passes (int optgroup_flags, dump_flags_t flags,
+  opt_info_enable_passes (optgroup_flags_t optgroup_flags, dump_flags_t flags,
 			  const char *filename);
 
 private:
diff --git a/gcc/early-remat.c b/gcc/early-remat.c
index 28eb9b4..776b2d0 100644
--- a/gcc/early-remat.c
+++ b/gcc/early-remat.c
@@ -657,7 +657,7 @@ early_remat::dump_edge_list (basic_block bb, bool do_succ)
   edge e;
   edge_iterator ei;
   FOR_EACH_EDGE (e, ei, do_succ ? bb->succs : bb->preds)
-    dump_edge_info (dump_file, e, 0, do_succ);
+    dump_edge_info (dump_file, e, TDF_NONE, do_succ);
 }
 
 /* Print information about basic block BB to the dump file.  */
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index c0d6e15..405d9e3 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -153,7 +153,7 @@ print_gimple_stmt (FILE *file, gimple *g, int spc, dump_flags_t flags)
 DEBUG_FUNCTION void
 debug (gimple &ref)
 {
-  print_gimple_stmt (stderr, &ref, 0, 0);
+  print_gimple_stmt (stderr, &ref, 0, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
diff --git a/gcc/gimple-ssa-store-merging.c b/gcc/gimple-ssa-store-merging.c
index 1cefdeb..8aa24e6 100644
--- a/gcc/gimple-ssa-store-merging.c
+++ b/gcc/gimple-ssa-store-merging.c
@@ -1083,7 +1083,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
 	    print_gimple_stmt (dump_file, cur_stmt, 0);
 	  else
 	    {
-	      print_generic_expr (dump_file, tgt, 0);
+	      print_generic_expr (dump_file, tgt, TDF_NONE);
 	      fprintf (dump_file, "\n");
 	    }
 	}
@@ -1153,7 +1153,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
 	print_gimple_stmt (dump_file, cur_stmt, 0);
       else
 	{
-	  print_generic_expr (dump_file, tgt, 0);
+	  print_generic_expr (dump_file, tgt, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
     }
@@ -2020,7 +2020,7 @@ merged_store_group::apply_stores ()
 	  if (ret)
 	    {
 	      fputs ("After writing ", dump_file);
-	      print_generic_expr (dump_file, cst, 0);
+	      print_generic_expr (dump_file, cst, TDF_NONE);
 	      fprintf (dump_file, " of size " HOST_WIDE_INT_PRINT_DEC
 		       " at position %d\n", info->bitsize, pos_in_buffer);
 	      fputs ("  the merged value contains ", dump_file);
diff --git a/gcc/gimple-ssa-strength-reduction.c b/gcc/gimple-ssa-strength-reduction.c
index b86ce85..ea81adc 100644
--- a/gcc/gimple-ssa-strength-reduction.c
+++ b/gcc/gimple-ssa-strength-reduction.c
@@ -3379,7 +3379,7 @@ insert_initializers (slsr_cand_t c)
 	      fputs ("Using existing initializer: ", dump_file);
 	      print_gimple_stmt (dump_file,
 				 SSA_NAME_DEF_STMT (incr_vec[i].initializer),
-				 0, 0);
+				 0, TDF_NONE);
 	    }
 	  continue;
 	}
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 4b91151..828fa59 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -3154,7 +3154,7 @@ static void
 verify_gimple_pp (const char *expected, gimple *stmt)
 {
   pretty_printer pp;
-  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, 0 /* flags */);
+  pp_gimple_stmt_1 (&pp, stmt, 0 /* spc */, TDF_NONE /* flags */);
   ASSERT_STREQ (expected, pp_formatted_text (&pp));
 }
 
diff --git a/gcc/graphite-poly.c b/gcc/graphite-poly.c
index bede7ba..51ca038 100644
--- a/gcc/graphite-poly.c
+++ b/gcc/graphite-poly.c
@@ -402,7 +402,7 @@ static void
 print_pbb_body (FILE *file, poly_bb_p pbb)
 {
   fprintf (file, "Body (\n");
-  dump_bb (file, pbb_bb (pbb), 0, 0);
+  dump_bb (file, pbb_bb (pbb), 0, TDF_NONE);
   fprintf (file, ")\n");
 }
 
diff --git a/gcc/passes.c b/gcc/passes.c
index 2c711f0..c787ffc 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -784,7 +784,7 @@ pass_manager::register_one_dump_file (opt_pass *pass)
   char num[11];
   dump_kind dkind;
   int id;
-  int optgroup_flags = OPTGROUP_NONE;
+  optgroup_flags_t optgroup_flags = OPTGROUP_NONE;
   gcc::dump_manager *dumps = m_ctxt->get_dumps ();
 
   /* See below in next_pass_1.  */
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index caf5f26..5c736c5 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -894,7 +894,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
 	  {
 	    pretty_printer buffer;
 	    buffer.buffer->stream = file;
-	    pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4, 0);
+	    pp_gimple_stmt_1 (&buffer, SSA_NAME_DEF_STMT (node), indent + 4,
+			      TDF_NONE);
 	    pp_flush (&buffer);
 	  }
 
@@ -1039,7 +1040,7 @@ dump_tree_via_hooks (const tree_node *ptr, dump_flags_t options)
 DEBUG_FUNCTION void
 debug (const tree_node &ref)
 {
-  dump_tree_via_hooks (&ref, 0);
+  dump_tree_via_hooks (&ref, TDF_NONE);
 }
 
 DEBUG_FUNCTION void
@@ -1070,7 +1071,7 @@ DEBUG_FUNCTION void
 debug_body (const tree_node &ref)
 {
   if (TREE_CODE (&ref) == FUNCTION_DECL)
-    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, 0);
+    dump_function_to_file (const_cast <tree_node*> (&ref), stderr, TDF_NONE);
   else
     debug (ref);
 }
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 7625d19..60010c6 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -47,7 +47,7 @@ struct pass_data
   const char *name;
 
   /* The -fopt-info optimization group flags as defined in dumpfile.h. */
-  unsigned int optinfo_flags;
+  optgroup_flags_t optinfo_flags;
 
   /* The timevar id associated with this pass.  */
   /* ??? Ideally would be dynamically assigned.  */
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 125507e..9398ce8 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -3417,7 +3417,7 @@ print_struct_decl (pretty_printer *pp, const_tree node, int spc,
 		|| TREE_CODE (node) == QUAL_UNION_TYPE))
 	pp_string (pp, "union ");
 
-      dump_generic_node (pp, TYPE_NAME (node), spc, 0, false);
+      dump_generic_node (pp, TYPE_NAME (node), spc, TDF_NONE, false);
     }
 
   /* Print the contents of the structure.  */
diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
index 273396d..187ca5a 100644
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -2711,7 +2711,7 @@ convert_mult_to_fma_1 (tree mul_result, tree op1, tree op2)
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Generated FMA ");
-	  print_gimple_stmt (dump_file, gsi_stmt (gsi), 0, 0);
+	  print_gimple_stmt (dump_file, gsi_stmt (gsi), 0, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
 
@@ -3047,7 +3047,7 @@ convert_mult_to_fma (gimple *mul_stmt, tree op1, tree op2,
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Deferred generating FMA for multiplication ");
-	  print_gimple_stmt (dump_file, mul_stmt, 0, 0);
+	  print_gimple_stmt (dump_file, mul_stmt, 0, TDF_NONE);
 	  fprintf (dump_file, "\n");
 	}
 
diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c
index 38bae77..592c180 100644
--- a/gcc/tree-ssa-reassoc.c
+++ b/gcc/tree-ssa-reassoc.c
@@ -1606,7 +1606,7 @@ undistribute_ops_list (enum tree_code opcode,
     {
       fprintf (dump_file, "searching for un-distribute opportunities ");
       print_generic_expr (dump_file,
-	(*ops)[bitmap_first_set_bit (candidates)]->op, 0);
+	(*ops)[bitmap_first_set_bit (candidates)]->op, TDF_NONE);
       fprintf (dump_file, " %d\n", nr_candidates);
     }
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 4e946ba..80d9938 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5992,7 +5992,7 @@ vn_eliminate (bitmap inserted_exprs)
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Removing dead stmt ");
-	  print_gimple_stmt (dump_file, stmt, 0, 0);
+	  print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
 	}
 
       gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 0b84f23..3eb67c9 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -3254,7 +3254,7 @@ dependence_distance_ge_vf (data_dependence_relation *ddr,
 /* Dump LOWER_BOUND using flags DUMP_KIND.  Dumps are known to be enabled.  */
 
 static void
-dump_lower_bound (int dump_kind, const vec_lower_bound &lower_bound)
+dump_lower_bound (dump_flags_t dump_kind, const vec_lower_bound &lower_bound)
 {
   dump_printf (dump_kind, "%s (", lower_bound.unsigned_p ? "unsigned" : "abs");
   dump_generic_expr (dump_kind, TDF_SLIM, lower_bound.expr);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 8/8] Add lots of pointless churn to tree-vect-*.c
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
                                 ` (4 preceding siblings ...)
  2018-06-14 19:50               ` [PATCH 7/8] tree-ssa-loop-im.c port from fprintf to the " David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-14 19:50               ` [PATCH 2/8] Introduce VECT_SCOPE macro David Malcolm
  2018-06-14 19:50               ` [PATCH 3/8] v2 of optinfo, remarks and optimization records David Malcolm
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

The drawback of this v2 patch kit is that to consolidate
"dump_*" calls into optimization records we need to manually
add optinfo_guard RAII objects to group them; the natural way
to do this is to turn the:

  if (dump_enabled_p ())

into:

  if (optinfo_guard guard = optinfo_guard (some_location))

and, given how verbose that is, a macro is the least painful approach:

  IF_VECT_DUMP

which requires splitting conditions such as:

  if (!mask_type && dump_enabled_p ())
    {...suite...}

into:

  if (!mask_type)
    IF_VECT_DUMP
      {...suite...}

It's still a lot of pointless churn (and too much "magic"), and I
think it can be better handled (in the to-be-written v3 patch kit)
by using dump_*_loc calls as delimiters.

I don't care for this approach; am posting this for reference.

gcc/ChangeLog:
	* tree-vect-data-refs.c: Convert "if (dump_enabled_p ())" to
	IF_VECT_DUMP.
	* tree-vect-loop-manip.c: Likewise.
	* tree-vect-loop.c: Likewise.
	* tree-vect-patterns.c: Likewise.
	* tree-vect-slp.c: Likewise.
	* tree-vect-stmts.c: Likewise.
	* tree-vectorizer.c: Likewise.  Add a top-level VECT_SCOPE
	for "analyzing loop".
---
 gcc/tree-vect-data-refs.c  | 239 +++++++++++++++---------------
 gcc/tree-vect-loop-manip.c |  65 ++++----
 gcc/tree-vect-loop.c       | 358 +++++++++++++++++++++++----------------------
 gcc/tree-vect-patterns.c   |  56 +++----
 gcc/tree-vect-slp.c        | 127 ++++++++--------
 gcc/tree-vect-stmts.c      | 349 +++++++++++++++++++++----------------------
 gcc/tree-vectorizer.c      |  22 +--
 7 files changed, 612 insertions(+), 604 deletions(-)

diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index fbc37d9..e8607b8 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -72,7 +72,7 @@ vect_lanes_optab_supported_p (const char *name, convert_optab optab,
       limit_p = !targetm.array_mode_supported_p (mode, count);
       if (!int_mode_for_size (bits, limit_p).exists (&array_mode))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "no array mode for %s["
 			     HOST_WIDE_INT_PRINT_DEC "]\n",
@@ -83,14 +83,14 @@ vect_lanes_optab_supported_p (const char *name, convert_optab optab,
 
   if (convert_optab_handler (optab, array_mode, mode) == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "cannot use %s<%s><%s>\n", name,
                          GET_MODE_NAME (array_mode), GET_MODE_NAME (mode));
       return false;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "can use %s<%s><%s>\n", name, GET_MODE_NAME (array_mode),
                      GET_MODE_NAME (mode));
@@ -181,7 +181,7 @@ vect_check_nonzero_value (loop_vec_info loop_vinfo, tree value)
     if (checks[i] == value)
       return;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "need run-time check that ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, value);
@@ -345,7 +345,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "versioning for alias not supported for: "
@@ -360,7 +360,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "versioning for alias required: "
@@ -393,7 +393,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
       if (STMT_VINFO_GATHER_SCATTER_P (stmtinfo_a)
 	  || STMT_VINFO_GATHER_SCATTER_P (stmtinfo_b))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "versioning for alias not supported for: "
@@ -408,7 +408,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "versioning for alias required: "
@@ -433,13 +433,13 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
     {
       int dist = dist_v[loop_depth];
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "dependence distance  = %d.\n", dist);
 
       if (dist == 0)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 	                       "dependence distance == 0 between ");
@@ -470,7 +470,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  if (!vect_preserves_scalar_order_p (vect_dr_stmt(dra),
 					      vect_dr_stmt (drb)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "READ_WRITE dependence in interleaving.\n");
 	      return true;
@@ -483,7 +483,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 		vect_check_nonzero_value (loop_vinfo, indicator);
 	      else if (integer_zerop (indicator))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "access also has a zero step\n");
 		  return true;
@@ -497,7 +497,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  /* If DDR_REVERSED_P the order of the data-refs in DDR was
 	     reversed (to make distance vector positive), and the actual
 	     distance is negative.  */
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                     "dependence distance negative.\n");
 	  /* Record a negative dependence distance to later limit the
@@ -516,7 +516,7 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	  /* The dependence distance requires reduction of the maximal
 	     vectorization factor.  */
 	  *max_vf = abs (dist);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 	                     "adjusting maximal vectorization factor to %i\n",
 	                     *max_vf);
@@ -526,13 +526,13 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
 	{
 	  /* Dependence distance does not create dependence, as far as
 	     vectorization is concerned, in this case.  */
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 	                     "dependence distance >= VF.\n");
 	  continue;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	               "not vectorized, possible dependence "
@@ -625,7 +625,7 @@ vect_slp_analyze_data_ref_dependence (struct data_dependence_relation *ddr)
   /* Unknown data dependence.  */
   if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)
     {
-      if  (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "can't determine dependence between ");
@@ -635,7 +635,7 @@ vect_slp_analyze_data_ref_dependence (struct data_dependence_relation *ddr)
 	  dump_printf (MSG_MISSED_OPTIMIZATION,  "\n");
 	}
     }
-  else if (dump_enabled_p ())
+  else IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "determined dependence between ");
@@ -797,7 +797,7 @@ vect_record_base_alignment (vec_info *vinfo, gimple *stmt,
   if (!existed || entry->base_alignment < drb->base_alignment)
     {
       entry = drb;
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "recording new base alignment for ");
@@ -883,7 +883,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
   tree ref = DR_REF (dr);
   tree vectype = STMT_VINFO_VECTYPE (stmt_info);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_compute_data_ref_alignment:\n");
 
@@ -918,7 +918,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       step_preserves_misalignment_p
 	= (DR_STEP_ALIGNMENT (dr) % vector_alignment) == 0;
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  if (step_preserves_misalignment_p)
 	    dump_printf_loc (MSG_NOTE, vect_location,
@@ -940,9 +940,10 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       step_preserves_misalignment_p
 	= multiple_p (DR_STEP_ALIGNMENT (dr) * vf, vector_alignment);
 
-      if (!step_preserves_misalignment_p && dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "step doesn't divide the vector alignment.\n");
+      if (!step_preserves_misalignment_p)
+	IF_VECT_DUMP
+	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+			   "step doesn't divide the vector alignment.\n");
     }
 
   unsigned int base_alignment = drb->base_alignment;
@@ -963,7 +964,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
 	 negative when computing the starting misalignment below.  */
       || TREE_CODE (drb->step) != INTEGER_CST)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                   "Unknown alignment for access: ");
@@ -981,7 +982,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
 	  || !vect_can_force_dr_alignment_p (base,
 					     vector_alignment * BITS_PER_UNIT))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 	                       "can't force alignment of ref: ");
@@ -994,7 +995,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
       /* Force the alignment of the decl.
 	 NOTE: This is the only change to the code we make during
 	 the analysis phase, before deciding to vectorize the loop.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location, "force alignment of ");
           dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
@@ -1020,7 +1021,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
   if (!known_misalignment (misalignment, vector_alignment,
 			   &const_misalignment))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "Non-constant misalignment for access: ");
@@ -1032,7 +1033,7 @@ vect_compute_data_ref_alignment (struct data_reference *dr)
 
   SET_DR_MISALIGNMENT (dr, const_misalignment);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                        "misalign = %d bytes of ref ", DR_MISALIGNMENT (dr));
@@ -1101,7 +1102,7 @@ vect_update_misalignment_for_peel (struct data_reference *dr,
       return;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "Setting misalignment " \
 		     "to unknown (-1).\n");
   SET_DR_MISALIGNMENT (dr, DR_MISALIGNMENT_UNKNOWN);
@@ -1119,7 +1120,7 @@ verify_data_ref_alignment (data_reference_p dr)
     = vect_supportable_dr_alignment (dr, false);
   if (!supportable_dr_alignment)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  if (DR_IS_READ (dr))
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
@@ -1136,9 +1137,10 @@ verify_data_ref_alignment (data_reference_p dr)
       return false;
     }
 
-  if (supportable_dr_alignment != dr_aligned && dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "Vectorizing an unaligned access.\n");
+  if (supportable_dr_alignment != dr_aligned)
+    IF_VECT_DUMP
+      dump_printf_loc (MSG_NOTE, vect_location,
+		       "Vectorizing an unaligned access.\n");
 
   return true;
 }
@@ -1232,7 +1234,7 @@ vector_alignment_reachable_p (struct data_reference *dr)
     {
       HOST_WIDE_INT elmsize =
 		int_cst_value (TYPE_SIZE_UNIT (TREE_TYPE (vectype)));
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 	                   "data size =" HOST_WIDE_INT_PRINT_DEC, elmsize);
@@ -1241,7 +1243,7 @@ vector_alignment_reachable_p (struct data_reference *dr)
 	}
       if (DR_MISALIGNMENT (dr) % elmsize)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                     "data size does not divide the misalignment.\n");
 	  return false;
@@ -1252,7 +1254,7 @@ vector_alignment_reachable_p (struct data_reference *dr)
     {
       tree type = TREE_TYPE (DR_REF (dr));
       bool is_packed = not_size_aligned (DR_REF (dr));
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                 "Unknown misalignment, %snaturally aligned\n",
 			 is_packed ? "not " : "");
@@ -1288,7 +1290,7 @@ vect_get_data_access_cost (struct data_reference *dr,
   else
     vect_get_store_cost (stmt_info, ncopies, inside_cost, body_cost_vec);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_get_data_access_cost: inside_cost = %d, "
                      "outside_cost = %d.\n", *inside_cost, *outside_cost);
@@ -1845,7 +1847,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
         {
           if (!aligned_access_p (dr))
             {
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                  "vector alignment may not be reachable\n");
               break;
@@ -2041,7 +2043,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
 	  if (STMT_VINFO_GROUPED_ACCESS (stmt_info))
 	    npeel /= DR_GROUP_SIZE (stmt_info);
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location,
                              "Try peeling by %d\n", npeel);
         }
@@ -2076,7 +2078,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
               if (max_peel > max_allowed_peel)
                 {
                   do_peeling = false;
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_NOTE, vect_location,
                         "Disable peeling, max peels reached: %d\n", max_peel);
                 }
@@ -2126,7 +2128,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
             LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo)
 	      = DR_MISALIGNMENT (dr0);
 	  SET_DR_MISALIGNMENT (dr0, 0);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
             {
               dump_printf_loc (MSG_NOTE, vect_location,
                                "Alignment of access forced using peeling.\n");
@@ -2251,12 +2253,12 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
           stmt_vec_info stmt_info = vinfo_for_stmt (stmt);
           dr = STMT_VINFO_DATA_REF (stmt_info);
 	  SET_DR_MISALIGNMENT (dr, 0);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location,
                              "Alignment of access forced using versioning.\n");
         }
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "Versioning for alignment will be applied.\n");
 
@@ -2319,7 +2321,7 @@ vect_find_same_alignment_drs (struct data_dependence_relation *ddr)
 
   STMT_VINFO_SAME_ALIGN_REFS (stmtinfo_a).safe_push (drb);
   STMT_VINFO_SAME_ALIGN_REFS (stmtinfo_b).safe_push (dra);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "accesses have the same alignment: ");
@@ -2366,7 +2368,7 @@ vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 	      && !STMT_VINFO_GROUPED_ACCESS (stmt_info))
 	    continue;
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: can't calculate alignment "
 			     "for data ref.\n");
@@ -2400,7 +2402,7 @@ vect_slp_analyze_and_verify_node_alignment (slp_tree node)
 	  && ! vect_compute_data_ref_alignment (first_dr))
       || ! verify_data_ref_alignment (dr))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: bad data alignment in basic "
 			 "block.\n");
@@ -2470,7 +2472,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	 simply not include that gap.  */
       if ((dr_step % type_size) != 0)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 	                       "Step ");
@@ -2502,7 +2504,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	  DR_GROUP_FIRST_ELEMENT (vinfo_for_stmt (stmt)) = stmt;
 	  DR_GROUP_SIZE (vinfo_for_stmt (stmt)) = groupsize;
 	  DR_GROUP_GAP (stmt_info) = groupsize - 1;
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 	                       "Detected single element interleaving ");
@@ -2515,7 +2517,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
  	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                   "not consecutive access ");
@@ -2557,13 +2559,13 @@ vect_analyze_group_access_1 (struct data_reference *dr)
             {
               if (DR_IS_WRITE (data_ref))
                 {
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                      "Two store stmts share the same dr.\n");
                   return false;
                 }
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Two or more load stmts share the same dr.\n");
 
@@ -2591,7 +2593,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
 	      slp_impossible = true;
 	      if (DR_IS_WRITE (data_ref))
 		{
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                      "interleaved store with gaps\n");
 		  return false;
@@ -2619,7 +2621,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
          inefficient way we have to cap earlier.  See PR78699 for example.  */
       if (groupsize > 4096)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "group is too large\n");
 	  return false;
@@ -2630,7 +2632,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
       if (groupsize != count
 	  && !DR_IS_READ (dr))
         {
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "interleaved store with gaps\n");
 	  return false;
@@ -2643,7 +2645,7 @@ vect_analyze_group_access_1 (struct data_reference *dr)
       DR_GROUP_GAP (vinfo_for_stmt (stmt)) = groupsize - last_accessed_element;
 
       DR_GROUP_SIZE (vinfo_for_stmt (stmt)) = groupsize;
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "Detected interleaving ");
@@ -2722,7 +2724,7 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 
   if (loop_vinfo && !step)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                 "bad data-ref access in loop\n");
       return false;
@@ -2739,7 +2741,7 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 	 loop-carried dependencies between inner loop iterations.  */
       if (loop->safelen < 2)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "zero step in inner loop of nest\n");
 	  return false;
@@ -2756,7 +2758,7 @@ vect_analyze_data_ref_access (struct data_reference *dr)
       step = STMT_VINFO_DR_STEP (stmt_info);
       if (integer_zerop (step))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 	                     "zero step in outer loop.\n");
 	  return DR_IS_READ (dr);
@@ -2779,7 +2781,7 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 
   if (loop && nested_in_vect_loop_p (loop, stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 	                 "grouped access in outer loop.\n");
       return false;
@@ -3043,7 +3045,7 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
 		break;
 	    }
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "Detected interleaving ");
@@ -3073,7 +3075,7 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
     if (STMT_VINFO_VECTORIZABLE (vinfo_for_stmt (vect_dr_stmt (dr))) 
         && !vect_analyze_data_ref_access (dr))
       {
-	if (dump_enabled_p ())
+	IF_VECT_DUMP
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 	                   "not vectorized: complicated access pattern.\n");
 
@@ -3226,7 +3228,7 @@ dependence_distance_ge_vf (data_dependence_relation *ddr,
 	return false;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "dependence distance between ");
@@ -3268,7 +3270,7 @@ vect_check_lower_bound (loop_vec_info loop_vinfo, tree expr, bool unsigned_p,
 	  {
 	    lower_bounds[i].unsigned_p = unsigned_p;
 	    lower_bounds[i].min_value = min_value;
-	    if (dump_enabled_p ())
+	    IF_VECT_DUMP
 	      {
 		dump_printf_loc (MSG_NOTE, vect_location,
 				 "updating run-time check to ");
@@ -3280,7 +3282,7 @@ vect_check_lower_bound (loop_vec_info loop_vinfo, tree expr, bool unsigned_p,
       }
 
   vec_lower_bound lower_bound (expr, unsigned_p, min_value);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "need a run-time check that ");
       dump_lower_bound (MSG_NOTE, lower_bound);
@@ -3413,7 +3415,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	  vec_object_pair new_pair (DDR_OBJECT_A (ddr), DDR_OBJECT_B (ddr));
 	  if (!compared_objects.add (new_pair))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location, "checking that ");
 		  dump_generic_expr (MSG_NOTE, TDF_SLIM, new_pair.first);
@@ -3438,7 +3440,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	  && (vect_preserves_scalar_order_p (stmt_a, stmt_b)
 	      || vectorizable_with_step_bound_p (dr_a, dr_b, &lower_bound)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "no need for alias check between ");
@@ -3461,7 +3463,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 	      || vect_small_gap_p (loop_vinfo, dr_b, lower_bound)))
 	{
 	  bool unsigned_p = dr_known_forward_stride_p (dr_a);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "no alias between ");
 	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
@@ -3537,25 +3539,26 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 					     segment_length_b,
 					     access_size_a,
 					     access_size_b);
-	  if (res >= 0 && dump_enabled_p ())
-	    {
-	      dump_printf_loc (MSG_NOTE, vect_location,
-			       "can tell at compile time that ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
-	      dump_printf (MSG_NOTE, " and ");
-	      dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b));
-	      if (res == 0)
-		dump_printf (MSG_NOTE, " do not alias\n");
-	      else
-		dump_printf (MSG_NOTE, " alias\n");
-	    }
+	  if (res >= 0)
+	    IF_VECT_DUMP
+	      {
+		dump_printf_loc (MSG_NOTE, vect_location,
+				 "can tell at compile time that ");
+		dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_a));
+		dump_printf (MSG_NOTE, " and ");
+		dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_REF (dr_b));
+		if (res == 0)
+		  dump_printf (MSG_NOTE, " do not alias\n");
+		else
+		  dump_printf (MSG_NOTE, " alias\n");
+	      }
 
 	  if (res == 0)
 	    continue;
 
 	  if (res == 1)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_NOTE, vect_location,
 				 "not vectorized: compilation time alias.\n");
 	      return false;
@@ -3583,7 +3586,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
 		   may_alias_ddrs.length (), count);
   if ((int) count > PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "number of versioning for alias "
 			 "run-time tests exceeds %d "
@@ -3956,7 +3959,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 
   if (gimple_has_volatile_ops (stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: volatile type ");
@@ -3967,7 +3970,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 
   if (stmt_can_throw_internal (stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: statement can throw an "
@@ -3986,7 +3989,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 
   if (refs.length () > 1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: more than one data ref "
@@ -4001,7 +4004,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
 	|| (gimple_call_internal_fn (call) != IFN_MASK_LOAD
 	    && gimple_call_internal_fn (call) != IFN_MASK_STORE))
       {
-	if (dump_enabled_p ())
+	IF_VECT_DUMP
 	  {
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION,  vect_location,
 			     "not vectorized: dr in a call ");
@@ -4014,7 +4017,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
   if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF
       && DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: statement is bitfield "
@@ -4027,7 +4030,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
   if (DR_BASE_ADDRESS (dr)
       && TREE_CODE (DR_BASE_ADDRESS (dr)) == INTEGER_CST)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: base addr of dr is a "
 			 "constant\n");
@@ -4178,7 +4181,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 
 	  if (gatherscatter == SG_NONE && !simd_lane_access)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                    "not vectorized: data ref analysis "
@@ -4200,7 +4203,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	  && VAR_P (TREE_OPERAND (DR_BASE_ADDRESS (dr), 0))
 	  && DECL_NONALIASED (TREE_OPERAND (DR_BASE_ADDRESS (dr), 0)))
 	{
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                "not vectorized: base object not addressable "
@@ -4222,7 +4225,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	{
 	  if (nested_in_vect_loop_p (loop, stmt))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
                                    "not vectorized: not suitable for strided "
@@ -4256,7 +4259,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	  tree init_addr = fold_build_pointer_plus (base, init_offset);
 	  tree init_ref = build_fold_indirect_ref (init_addr);
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
                                "analyze in outer loop: ");
@@ -4269,7 +4272,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	    /* dr_analyze_innermost already explained the failure.  */
 	    return false;
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
                                "\touter base_address: ");
@@ -4311,7 +4314,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	= get_vectype_for_scalar_type (scalar_type);
       if (!STMT_VINFO_VECTYPE (stmt_info))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                "not vectorized: no vectype for stmt: ");
@@ -4340,7 +4343,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
         }
       else
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "got vectype for stmt: ");
@@ -4365,7 +4368,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
 	    {
 	      STMT_VINFO_DATA_REF (stmt_info) = NULL;
 	      free_data_ref (dr);
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   (gatherscatter == GATHER) ?
@@ -4591,7 +4594,7 @@ vect_create_addr_base_for_vector_ref (gimple *stmt,
 	mark_ptr_info_alignment_unknown (SSA_NAME_PTR_INFO (addr_base));
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "created ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, addr_base);
@@ -4718,7 +4721,7 @@ vect_create_data_ref_ptr (gimple *stmt, tree aggr_type, struct loop *at_loop,
      in LOOP.  */
   base_name = get_name (DR_BASE_ADDRESS (dr));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       dump_printf_loc (MSG_NOTE, vect_location,
@@ -5048,7 +5051,7 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
      be a power of two.  */
   if (count != 3 && exact_log2 (count) == -1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "the size of the group of accesses"
 			 " is not a power of 2 or not eqaul to 3\n");
@@ -5067,7 +5070,7 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	  unsigned int nelt;
 	  if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "cannot handle groups of 3 stores for"
 				 " variable-length vectors\n");
@@ -5094,7 +5097,7 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf (MSG_MISSED_OPTIMIZATION,
 				 "permutation op not supported by target.\n");
 		  return false;
@@ -5112,7 +5115,7 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf (MSG_MISSED_OPTIMIZATION,
 				 "permutation op not supported by target.\n");
 		  return false;
@@ -5146,7 +5149,7 @@ vect_grouped_store_supported (tree vectype, unsigned HOST_WIDE_INT count)
 	}
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf (MSG_MISSED_OPTIMIZATION,
 		 "permutaion op not supported by target.\n");
   return false;
@@ -5659,7 +5662,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
      see PR65518).  */
   if (single_element_p && maybe_gt (count, TYPE_VECTOR_SUBPARTS (vectype)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "single-element interleaving not supported "
 			 "for not adjacent vector loads\n");
@@ -5670,7 +5673,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
      be a power of two.  */
   if (count != 3 && exact_log2 (count) == -1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "the size of the group of accesses"
 			 " is not a power of 2 or not equal to 3\n");
@@ -5686,7 +5689,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	  unsigned int nelt;
 	  if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "cannot handle groups of 3 loads for"
 				 " variable-length vectors\n");
@@ -5707,7 +5710,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "shuffle of 3 loads is not supported by"
 				     " target\n");
@@ -5721,7 +5724,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
 	      indices.new_vector (sel, 2, nelt);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "shuffle of 3 loads is not supported by"
 				     " target\n");
@@ -5753,7 +5756,7 @@ vect_grouped_load_supported (tree vectype, bool single_element_p,
         }
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 		     "extract even/odd not supported by target\n");
   return false;
@@ -6096,7 +6099,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       vec_perm_indices indices (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shuffle of 2 fields structure is not \
 			      supported by target\n");
@@ -6111,7 +6114,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shuffle of 2 fields structure is not \
 			      supported by target\n");
@@ -6126,7 +6129,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shift permutation is not supported by target\n");
 	  return false;
@@ -6142,7 +6145,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "select is not supported by target\n");
 	  return false;
@@ -6206,7 +6209,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       vec_perm_indices indices (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shuffle of 3 fields structure is not \
 			      supported by target\n");
@@ -6221,7 +6224,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shift permutation is not supported by target\n");
 	  return false;
@@ -6235,7 +6238,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shift permutation is not supported by target\n");
 	  return false;
@@ -6249,7 +6252,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shift permutation is not supported by target\n");
 	  return false;
@@ -6263,7 +6266,7 @@ vect_shift_permute_load_chain (vec<tree> dr_chain,
       indices.new_vector (sel, 2, nelt);
       if (!can_vec_perm_const_p (TYPE_MODE (vectype), indices))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "shift permutation is not supported by target\n");
 	  return false;
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index 326c8dd..f9dad59 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -938,7 +938,7 @@ vect_set_loop_condition (struct loop *loop, loop_vec_info loop_vinfo,
   gsi_remove (&loop_cond_gsi, true);
   free_stmt_vec_info (orig_cond);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "New loop exit condition: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, cond_stmt, 0);
@@ -1301,7 +1301,7 @@ create_lcssa_for_virtual_phi (struct loop *loop)
    location is calculated.
    Return the loop location if succeed and NULL if not.  */
 
-source_location
+optinfo_location
 find_loop_location (struct loop *loop)
 {
   gimple *stmt = NULL;
@@ -1309,19 +1309,19 @@ find_loop_location (struct loop *loop)
   gimple_stmt_iterator si;
 
   if (!loop)
-    return UNKNOWN_LOCATION;
+    return optinfo_location ();
 
   stmt = get_loop_exit_condition (loop);
 
   if (stmt
       && LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-    return gimple_location (stmt);
+    return stmt;
 
   /* If we got here the loop is probably not "well formed",
      try to estimate the loop location */
 
   if (!loop->header)
-    return UNKNOWN_LOCATION;
+    return optinfo_location ();
 
   bb = loop->header;
 
@@ -1329,10 +1329,10 @@ find_loop_location (struct loop *loop)
     {
       stmt = gsi_stmt (si);
       if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-        return gimple_location (stmt);
+        return stmt;
     }
 
-  return UNKNOWN_LOCATION;
+  return optinfo_location ();
 }
 
 /* Return true if PHI defines an IV of the loop to be vectorized.  */
@@ -1370,14 +1370,14 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
   /* Analyze phi functions of the loop header.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "vect_can_advance_ivs_p:\n");
   for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     {
       tree evolution_part;
 
       gphi *phi = gsi.phi ();
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
           dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
           dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -1389,7 +1389,7 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 	 Skip reduction phis.  */
       if (!iv_phi_p (phi))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "reduc or virtual phi. skip.\n");
 	  continue;
@@ -1401,7 +1401,7 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 	= STMT_VINFO_LOOP_PHI_EVOLUTION_PART (vinfo_for_stmt (phi));
       if (evolution_part == NULL_TREE)
         {
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf (MSG_MISSED_OPTIMIZATION,
 			 "No access function or evolution.\n");
 	  return false;
@@ -1412,7 +1412,7 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
       if (!expr_invariant_in_loop_p (loop, evolution_part))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "evolution not invariant in loop.\n");
 	  return false;
@@ -1423,7 +1423,7 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
 
       if (tree_is_chrec (evolution_part))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "evolution is chrec.\n");
 	  return false;
@@ -1500,7 +1500,7 @@ vect_update_ivs_after_vectorizer (loop_vec_info loop_vinfo,
 
       gphi *phi = gsi.phi ();
       gphi *phi1 = gsi1.phi ();
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "vect_update_ivs_after_vectorizer: phi: ");
@@ -1510,7 +1510,7 @@ vect_update_ivs_after_vectorizer (loop_vec_info loop_vinfo,
       /* Skip reduction and virtual phis.  */
       if (!iv_phi_p (phi))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "reduc or virtual phi. skip.\n");
 	  continue;
@@ -1640,7 +1640,7 @@ vect_gen_prolog_loop_niters (loop_vec_info loop_vinfo,
     {
       int npeel = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "known peeling = %d.\n", npeel);
 
@@ -1671,7 +1671,7 @@ vect_gen_prolog_loop_niters (loop_vec_info loop_vinfo,
       *bound = align_in_elems - 1;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "niters for prolog loop: ");
@@ -1791,7 +1791,7 @@ vect_prepare_for_masked_peels (loop_vec_info loop_vinfo)
 	}
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "misalignment for fully-masked loop: ");
@@ -2494,7 +2494,7 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1,
 	}
     }
 
-  source_location loop_loc = find_loop_location (loop);
+  optinfo_location loop_loc = find_loop_location (loop);
   struct loop *scalar_loop = LOOP_VINFO_SCALAR_LOOP (loop_vinfo);
   if (prolog_peeling)
     {
@@ -2930,7 +2930,7 @@ vect_create_cond_for_alias_checks (loop_vec_info loop_vinfo, tree * cond_expr)
 
   create_runtime_alias_checks (LOOP_VINFO_LOOP (loop_vinfo),
 			       &comp_alias_ddrs, cond_expr);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "created %u versioning for alias checks.\n",
 		     comp_alias_ddrs.length ());
@@ -3068,19 +3068,18 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       loop_constraint_set (loop, LOOP_C_INFINITE);
     }
 
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
-      && dump_enabled_p ())
-    {
-      if (version_alias)
-        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                         "loop versioned for vectorization because of "
-			 "possible aliasing\n");
-      if (version_align)
-        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                         "loop versioned for vectorization to enhance "
-			 "alignment\n");
-
-    }
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION)
+    IF_VECT_DUMP
+      {
+	if (version_alias)
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
+			   "loop versioned for vectorization because of "
+			   "possible aliasing\n");
+	if (version_align)
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
+			   "loop versioned for vectorization to enhance "
+			   "alignment\n");
+      }
   free_original_copy_tables ();
 
   /* Loop versioning violates an assumption we try to maintain during
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index cdf8d09..5c498b9 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -171,7 +171,7 @@ vect_determine_vf_for_stmt_1 (stmt_vec_info stmt_info,
        && !STMT_VINFO_LIVE_P (stmt_info))
       || gimple_clobber_p (stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location, "skip.\n");
       return true;
     }
@@ -213,7 +213,7 @@ static bool
 vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
 			    vec<stmt_vec_info > *mask_producers)
 {
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "==> examining statement: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt_info->stmt, 0);
@@ -232,7 +232,7 @@ vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
 	   !gsi_end_p (si); gsi_next (&si))
 	{
 	  stmt_vec_info def_stmt_info = vinfo_for_stmt (gsi_stmt (si));
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "==> examining pattern def stmt: ");
@@ -244,7 +244,7 @@ vect_determine_vf_for_stmt (stmt_vec_info stmt_info, poly_uint64 *vf,
 	    return false;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "==> examining pattern statement: ");
@@ -307,7 +307,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	{
 	  phi = si.phi ();
 	  stmt_info = vinfo_for_stmt (phi);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "==> examining phi: ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -321,7 +321,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      gcc_assert (!STMT_VINFO_VECTYPE (stmt_info));
               scalar_type = TREE_TYPE (PHI_RESULT (phi));
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location,
                                    "get vectype for scalar type:  ");
@@ -332,7 +332,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 	      vectype = get_vectype_for_scalar_type (scalar_type);
 	      if (!vectype)
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    {
 		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                        "not vectorized: unsupported "
@@ -345,14 +345,14 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 		}
 	      STMT_VINFO_VECTYPE (stmt_info) = vectype;
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
 		  dump_generic_expr (MSG_NOTE, TDF_SLIM, vectype);
                   dump_printf (MSG_NOTE, "\n");
 		}
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location, "nunits = ");
 		  dump_dec (MSG_NOTE, TYPE_VECTOR_SUBPARTS (vectype));
@@ -374,7 +374,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
     }
 
   /* TODO: Analyze cost. Decide if worth while to vectorize.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "vectorization factor = ");
       dump_dec (MSG_NOTE, vectorization_factor);
@@ -383,7 +383,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
 
   if (known_le (vectorization_factor, 1U))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "not vectorized: unsupported data-type\n");
       return false;
@@ -430,7 +430,7 @@ vect_is_simple_iv_evolution (unsigned loop_nb, tree access_fn, tree * init,
   step_expr = evolution_part;
   init_expr = unshare_expr (initial_condition_in_loop_num (access_fn, loop_nb));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "step: ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, step_expr);
@@ -452,7 +452,7 @@ vect_is_simple_iv_evolution (unsigned loop_nb, tree access_fn, tree * init,
       && (TREE_CODE (step_expr) != REAL_CST
 	  || !flag_associative_math))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "step unknown.\n");
       return false;
@@ -489,7 +489,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       tree def = PHI_RESULT (phi);
       stmt_vec_info stmt_vinfo = vinfo_for_stmt (phi);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -507,7 +507,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       if (access_fn)
 	{
 	  STRIP_NOPS (access_fn);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
                                "Access function of PHI: ");
@@ -533,7 +533,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
 		  != NULL_TREE);
       gcc_assert (STMT_VINFO_LOOP_PHI_EVOLUTION_PART (stmt_vinfo) != NULL_TREE);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location, "Detected induction.\n");
       STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_induction_def;
     }
@@ -547,7 +547,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
       stmt_vec_info stmt_vinfo = vinfo_for_stmt (phi);
       gimple *reduc_stmt;
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location, "Analyze phi: ");
           dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -562,7 +562,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
         {
           if (double_reduc)
             {
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 dump_printf_loc (MSG_NOTE, vect_location,
 				 "Detected double reduction.\n");
 
@@ -574,7 +574,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
             {
               if (loop != LOOP_VINFO_LOOP (loop_vinfo))
                 {
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_NOTE, vect_location,
 				     "Detected vectorizable nested cycle.\n");
 
@@ -584,7 +584,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
                 }
               else
                 {
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_NOTE, vect_location,
 				     "Detected reduction.\n");
 
@@ -600,7 +600,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
             }
         }
       else
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "Unknown def-use cycle pattern.\n");
     }
@@ -1186,7 +1186,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if (loop->num_nodes != 2)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: control flow in loop.\n");
           return false;
@@ -1194,7 +1194,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if (empty_block_p (loop->header))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: empty loop.\n");
 	  return false;
@@ -1224,7 +1224,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if ((loop->inner)->inner || (loop->inner)->next)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: multiple nested loops.\n");
 	  return false;
@@ -1232,7 +1232,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if (loop->num_nodes != 5)
         {
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: control flow in loop.\n");
 	  return false;
@@ -1243,7 +1243,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 	  || !single_exit (innerloop)
 	  || single_exit (innerloop)->dest != EDGE_PRED (loop->latch, 0)->src)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: unsupported outerloop form.\n");
 	  return false;
@@ -1258,7 +1258,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 	     loop.  */
 	  || !integer_onep (inner_assumptions))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
             dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: Bad inner loop.\n");
 	  return false;
@@ -1266,14 +1266,14 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
       if (!expr_invariant_in_loop_p (loop, inner_niter))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: inner-loop count not"
                              " invariant.\n");
 	  return false;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "Considering outer-loop vectorization.\n");
     }
@@ -1281,7 +1281,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   if (!single_exit (loop)
       || EDGE_COUNT (loop->header->preds) != 2)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           if (!single_exit (loop))
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
@@ -1300,7 +1300,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   if (!empty_block_p (loop->latch)
       || !gimple_seq_empty_p (phi_nodes (loop->latch)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: latch block not empty.\n");
       return false;
@@ -1310,7 +1310,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   edge e = single_exit (loop);
   if (e->flags & EDGE_ABNORMAL)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: abnormal loop exit edge.\n");
       return false;
@@ -1320,7 +1320,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 				     number_of_iterationsm1);
   if (!*loop_cond)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: complicated exit condition.\n");
       return false;
@@ -1330,7 +1330,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
       || !*number_of_iterations
       || chrec_contains_undetermined (*number_of_iterations))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: number of iterations cannot be "
 			 "computed.\n");
@@ -1339,7 +1339,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 
   if (integer_zerop (*number_of_iterations))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: number of iterations = 0.\n");
       return false;
@@ -1381,8 +1381,8 @@ vect_analyze_loop_form (struct loop *loop)
 
   if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
     {
-      if (dump_enabled_p ())
-        {
+      IF_VECT_DUMP
+	{
           dump_printf_loc (MSG_NOTE, vect_location,
 			   "Symbolic number of iterations is ");
 	  dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
@@ -1466,7 +1466,7 @@ vect_update_vf_for_slp (loop_vec_info loop_vinfo)
     }
 
   LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "Updating vectorization factor to ");
@@ -1533,7 +1533,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
           ok = true;
 
           stmt_info = vinfo_for_stmt (phi);
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_NOTE, vect_location, "examining phi: ");
               dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -1552,7 +1552,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
               if (STMT_VINFO_LIVE_P (stmt_info)
 		  && !vect_active_double_reduction_p (stmt_info))
                 {
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "Unsupported loop-closed phi in "
 				     "outer-loop.\n");
@@ -1596,7 +1596,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
               && STMT_VINFO_DEF_TYPE (stmt_info) != vect_induction_def)
             {
               /* A scalar-dependence cycle that we don't support.  */
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "not vectorized: scalar dependence cycle.\n");
               return false;
@@ -1624,7 +1624,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
 
           if (!ok)
             {
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 {
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "not vectorized: relevant phi not "
@@ -1656,10 +1656,10 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
      touching this loop.  */
   if (!need_to_vectorize)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "All the computation can be taken out of the loop.\n");
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: redundant loop. no profit to "
 			 "vectorize.\n");
@@ -1693,7 +1693,7 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
       if (max_niter != -1
 	  && (unsigned HOST_WIDE_INT) max_niter < assumed_vf)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: iteration count smaller than "
 			     "vectorization factor.\n");
@@ -1707,10 +1707,10 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
 
   if (min_profitable_iters < 0)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: vectorization not profitable.\n");
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: vector version will never be "
 			 "profitable.\n");
@@ -1730,10 +1730,10 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
   if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo)
       && LOOP_VINFO_INT_NITERS (loop_vinfo) < th)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: vectorization not profitable.\n");
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "not vectorized: iteration count smaller than user "
 			 "specified loop bound parameter or minimum profitable "
@@ -1748,11 +1748,11 @@ vect_analyze_loop_costing (loop_vec_info loop_vinfo)
       && ((unsigned HOST_WIDE_INT) estimated_niter
 	  < MAX (th, (unsigned) min_profitable_estimate)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: estimated iteration count too "
 			 "small.\n");
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "not vectorized: estimated iteration count smaller "
 			 "than specified loop bound parameter or minimum "
@@ -1838,7 +1838,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   loop_p loop = LOOP_VINFO_LOOP (loop_vinfo);
   if (!find_loop_nest (loop, &LOOP_VINFO_LOOP_NEST (loop_vinfo)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: loop nest containing two "
 			 "or more consecutive inner loops cannot be "
@@ -1852,7 +1852,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
 				  &LOOP_VINFO_DATAREFS (loop_vinfo),
 				  &n_stmts))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: loop contains function "
 			 "calls or data references that cannot "
@@ -1866,7 +1866,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_analyze_data_refs (loop_vinfo, &min_vf);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data references.\n");
       return false;
@@ -1886,7 +1886,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_analyze_data_ref_accesses (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data access.\n");
       return false;
@@ -1897,7 +1897,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_mark_stmts_to_be_vectorized (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "unexpected pattern.\n");
       return false;
@@ -1916,7 +1916,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
       || (max_vf != MAX_VECTORIZATION_FACTOR
 	  && maybe_lt (max_vf, min_vf)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "bad data dependence.\n");
       return false;
@@ -1926,7 +1926,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   ok = vect_determine_vectorization_factor (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "can't determine vectorization factor.\n");
       return false;
@@ -1934,7 +1934,7 @@ vect_analyze_loop_2 (loop_vec_info loop_vinfo, bool &fatal)
   if (max_vf != MAX_VECTORIZATION_FACTOR
       && maybe_lt (max_vf, LOOP_VINFO_VECT_FACTOR (loop_vinfo)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data dependence.\n");
       return false;
@@ -1975,14 +1975,15 @@ start_over:
   poly_uint64 vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   gcc_assert (known_ne (vectorization_factor, 0U));
 
-  if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) && dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "vectorization_factor = ");
-      dump_dec (MSG_NOTE, vectorization_factor);
-      dump_printf (MSG_NOTE, ", niters = " HOST_WIDE_INT_PRINT_DEC "\n",
-		   LOOP_VINFO_INT_NITERS (loop_vinfo));
-    }
+  if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
+    IF_VECT_DUMP
+      {
+	dump_printf_loc (MSG_NOTE, vect_location,
+			 "vectorization_factor = ");
+	dump_dec (MSG_NOTE, vectorization_factor);
+	dump_printf (MSG_NOTE, ", niters = " HOST_WIDE_INT_PRINT_DEC "\n",
+		     LOOP_VINFO_INT_NITERS (loop_vinfo));
+      }
 
   HOST_WIDE_INT max_niter
     = likely_max_stmt_executions_int (LOOP_VINFO_LOOP (loop_vinfo));
@@ -1993,7 +1994,7 @@ start_over:
   ok = vect_analyze_data_refs_alignment (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad data alignment.\n");
       return false;
@@ -2015,7 +2016,7 @@ start_over:
     ok = vect_enhance_data_refs_alignment (loop_vinfo);
     if (!ok)
       {
-	if (dump_enabled_p ())
+	IF_VECT_DUMP
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "bad data alignment.\n");
         return false;
@@ -2038,7 +2039,7 @@ start_over:
   ok = vect_analyze_loop_operations (loop_vinfo);
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "bad operation or unsupported loop bound.\n");
       return false;
@@ -2049,7 +2050,7 @@ start_over:
   LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
     = (LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo)
        && vect_verify_full_masking (loop_vinfo));
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       if (LOOP_VINFO_FULLY_MASKED_P (loop_vinfo))
 	dump_printf_loc (MSG_NOTE, vect_location,
@@ -2071,7 +2072,7 @@ start_over:
 
       if (known_lt (wi::to_widest (scalar_niters), vf))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "loop has no enough iterations to support"
 			     " peeling for gaps.\n");
@@ -2085,7 +2086,7 @@ start_over:
     goto again;
   if (!res)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "Loop costings not worthwhile.\n");
       return false;
@@ -2123,14 +2124,14 @@ start_over:
   if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo)
       || LOOP_VINFO_PEELING_FOR_NITER (loop_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location, "epilog loop required\n");
       if (!vect_can_advance_ivs_p (loop_vinfo)
 	  || !slpeel_can_duplicate_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
 					   single_exit (LOOP_VINFO_LOOP
 							 (loop_vinfo))))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: can't create required "
 			     "epilog loop\n");
@@ -2218,7 +2219,7 @@ again:
 	}
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "re-trying with SLP disabled\n");
 
@@ -2302,7 +2303,7 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
       && loop_vec_info_for_loop (loop_outer (loop))
       && LOOP_VINFO_VECTORIZABLE_P (loop_vec_info_for_loop (loop_outer (loop))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "outer-loop already vectorized.\n");
       return NULL;
@@ -2315,7 +2316,7 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
       loop_vinfo = vect_analyze_loop_form (loop);
       if (!loop_vinfo)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "bad loop form.\n");
 	  return NULL;
@@ -2349,7 +2350,7 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
 
       /* Try the next biggest vector size.  */
       current_vector_size = vector_sizes[next_size++];
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "***** Re-trying analysis with "
@@ -2652,7 +2653,7 @@ vect_is_slp_reduction (loop_vec_info loop_info, gimple *phi,
                                   == vect_internal_def
                       && !is_loop_header_bb_p (gimple_bb (def_stmt)))))
   	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location, "swapping oprnds: ");
 		  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, next_stmt, 0);
@@ -2908,7 +2909,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 
       if (!flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "intermediate value used outside loop.\n");
 
@@ -2918,7 +2919,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       nloop_uses++;
       if (nloop_uses > 1)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "reduction value used in loop.\n");
           return NULL;
@@ -2931,7 +2932,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
   tree loop_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_e);
   if (TREE_CODE (loop_arg) != SSA_NAME)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "reduction: not ssa_name: ");
@@ -2954,7 +2955,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "reduction: unhandled reduction operation: ");
@@ -2980,7 +2981,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 	lcphis.safe_push (as_a <gphi *> (use_stmt));
       if (nloop_uses > 1)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "reduction used in loop.\n");
 	  return NULL;
@@ -2996,7 +2997,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       if (gimple_phi_num_args (def_stmt) != 1
           || TREE_CODE (op1) != SSA_NAME)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported phi node definition.\n");
 
@@ -3011,7 +3012,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
           && is_gimple_assign (def1)
 	  && flow_bb_inside_loop_p (loop->inner, gimple_bb (phi_use_stmt)))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             report_vect_op (MSG_NOTE, def_stmt,
 			    "detected double reduction: ");
 
@@ -3065,7 +3066,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
         }
       if (op3 == phi_name || op4 == phi_name)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
 			    "reduction: condition depends on previous"
 			    " iteration: ");
@@ -3077,7 +3078,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else if (!commutative_tree_code (code) || !associative_tree_code (code))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
 			"reduction: not commutative/associative: ");
       return NULL;
@@ -3089,7 +3090,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
     }
   else
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
 			"reduction: not handled operation: ");
       return NULL;
@@ -3097,7 +3098,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 
   if (TREE_CODE (op1) != SSA_NAME && TREE_CODE (op2) != SSA_NAME)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
 			"reduction: both uses not ssa_names: ");
 
@@ -3114,7 +3115,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       || (op4 && TREE_CODE (op4) == SSA_NAME
           && !types_compatible_p (type, TREE_TYPE (op4))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location,
 			   "reduction: multiple types: operation type: ");
@@ -3170,7 +3171,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
   if (code != COND_EXPR
       && ((!def1 || gimple_nop_p (def1)) && (!def2 || gimple_nop_p (def2))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	report_vect_op (MSG_NOTE, def_stmt, "reduction: no defs for operands: ");
       return NULL;
     }
@@ -3193,7 +3194,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
                           == vect_internal_def
  	              && !is_loop_header_bb_p (gimple_bb (def1)))))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	report_vect_op (MSG_NOTE, def_stmt, "detected reduction: ");
       return def_stmt;
     }
@@ -3238,7 +3239,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 		}
 	      else
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    report_vect_op (MSG_NOTE, def_stmt,
 				    "detected reduction: cannot swap operands "
 				    "for cond_expr");
@@ -3249,7 +3250,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 	    swap_ssa_operands (def_stmt, gimple_assign_rhs1_ptr (def_stmt),
 			       gimple_assign_rhs2_ptr (def_stmt));
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    report_vect_op (MSG_NOTE, def_stmt,
 			    "detected reduction: need to swap operands: ");
 
@@ -3258,7 +3259,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
         }
       else
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             report_vect_op (MSG_NOTE, def_stmt, "detected reduction: ");
         }
 
@@ -3271,7 +3272,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
       && orig_code != MINUS_EXPR
       && vect_is_slp_reduction (loop_info, phi, def_stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         report_vect_op (MSG_NOTE, def_stmt,
 			"reduction: detected reduction chain: ");
 
@@ -3293,7 +3294,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, gimple *phi,
 			    code))
     return def_stmt;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       report_vect_op (MSG_MISSED_OPTIMIZATION, def_stmt,
 		      "reduction: unknown pattern: ");
@@ -3341,7 +3342,7 @@ vect_get_known_peeling_cost (loop_vec_info loop_vinfo, int peel_iters_prologue,
   if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
     {
       *peel_iters_epilogue = assumed_vf / 2;
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "cost model: epilogue peel iters set to vf/2 "
 			 "because loop iterations are unknown .\n");
@@ -3687,7 +3688,7 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
 
   vec_outside_cost = (int)(vec_prologue_cost + vec_epilogue_cost);
   
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "Cost model analysis: \n");
       dump_printf (MSG_NOTE, "  Vector inside of loop cost: %d\n",
@@ -3742,10 +3743,10 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
   else
     {
       if (LOOP_VINFO_LOOP (loop_vinfo)->force_vectorize)
-	warning_at (vect_location, OPT_Wopenmp_simd, "vectorization "
-		    "did not happen for a simd loop");
+	warning_at (vect_location.get_location_t (), OPT_Wopenmp_simd,
+		    "vectorization did not happen for a simd loop");
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "cost model: the vector iteration cost = %d "
 			 "divided by the scalar iteration cost = %d "
@@ -3757,16 +3758,17 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
       return;
     }
 
-  dump_printf (MSG_NOTE,
-	       "  Calculated minimum iters for profitability: %d\n",
-	       min_profitable_iters);
+  IF_VECT_DUMP
+    dump_printf (MSG_NOTE,
+		 "  Calculated minimum iters for profitability: %d\n",
+		 min_profitable_iters);
 
   if (!LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
       && min_profitable_iters < (assumed_vf + peel_iters_prologue))
     /* We want the vectorized loop to execute at least once.  */
     min_profitable_iters = assumed_vf + peel_iters_prologue;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "  Runtime profitability threshold = %d\n",
                      min_profitable_iters);
@@ -3792,7 +3794,7 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
 				   - vec_inside_cost);
     }
   min_profitable_estimate = MAX (min_profitable_estimate, min_profitable_iters);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "  Static estimate profitability threshold = %d\n",
 		     min_profitable_estimate);
@@ -4008,7 +4010,7 @@ vect_model_reduction_cost (stmt_vec_info stmt_info, internal_fn reduc_fn,
 	}
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf (MSG_NOTE, 
                  "vect_model_reduction_cost: inside_cost = %d, "
                  "prologue_cost = %d, epilogue_cost = %d .\n", inside_cost,
@@ -4037,7 +4039,7 @@ vect_model_induction_cost (stmt_vec_info stmt_info, int ncopies,
   prologue_cost = record_stmt_cost (cost_vec, 2, scalar_to_vec,
 				    stmt_info, 0, vect_prologue);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_induction_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -4625,7 +4627,7 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
           add_phi_arg (as_a <gphi *> (phi), def, loop_latch_edge (loop),
 		       UNKNOWN_LOCATION);
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_NOTE, vect_location,
 			       "transform reduction: created def-use cycle: ");
@@ -5114,7 +5116,7 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
       /* Case 1:  Create:
          v_out2 = reduc_expr <v_out1>  */
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
 			 "Reduce using direct vector reduction.\n");
 
@@ -5383,7 +5385,7 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
 
           tree rhs;
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location,
 			     "Reduce using vector shifts\n");
 
@@ -5412,7 +5414,7 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
 	  /* 2.4  Extract the final scalar result.  Create:
 	     s_out3 = extract_field <v_out2, bitpos>  */
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "extract scalar result\n");
 
@@ -5436,7 +5438,7 @@ vect_create_epilog_for_reduction (vec<tree> vect_defs, gimple *stmt,
                  Create:  s = op <s, s'>  // For non SLP cases
                }  */
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location,
 			     "Reduce using scalar code.\n");
 
@@ -5759,7 +5761,7 @@ vect_finalize_reduction:
                                UNKNOWN_LOCATION);
                   add_phi_arg (vect_phi, PHI_RESULT (inner_phi),
                                loop_latch_edge (outer_loop), UNKNOWN_LOCATION);
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     {
                       dump_printf_loc (MSG_NOTE, vect_location,
 				       "created double reduction phi node: ");
@@ -6443,7 +6445,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
     {
       if (STMT_VINFO_REDUC_TYPE (stmt_info) == FOLD_LEFT_REDUCTION)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "in-order reduction chain without SLP.\n");
 	  return false;
@@ -6497,7 +6499,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	 as a reduction operation.  */
       if (reduc_index == -1)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "conditional reduction chains not supported\n");
 	  return false;
@@ -6522,7 +6524,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       else if (direct_internal_fn_supported_p (IFN_FOLD_EXTRACT_LAST,
 					       vectype_in, OPTIMIZE_FOR_SPEED))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "optimizing condition reduction with"
 			     " FOLD_EXTRACT_LAST.\n");
@@ -6563,7 +6565,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	    }
 	  if (cond_reduc_val)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_NOTE, vect_location,
 				 "condition expression based on "
 				 "integer induction.\n");
@@ -6589,7 +6591,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 				    cond_initial_val, cond_reduc_val);
 	      if (e && (integer_onep (e) || integer_zerop (e)))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_NOTE, vect_location,
 				     "condition expression based on "
 				     "compile time constant.\n");
@@ -6634,7 +6636,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 						ops[reduc_index], 0, NULL,
 						cost_vec))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported condition in reduction\n");
 	  return false;
@@ -6649,7 +6651,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  /* Shifts and rotates are only supported by vectorizable_shifts,
 	     not vectorizable_reduction.  */
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported shift or rotation.\n");
 	  return false;
@@ -6659,7 +6661,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       optab = optab_for_tree_code (code, vectype_in, optab_default);
       if (!optab)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "no optab.\n");
 
@@ -6668,14 +6670,14 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 
       if (optab_handler (optab, vec_mode) == CODE_FOR_nothing)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf (MSG_NOTE, "op not supported by target.\n");
 
 	  if (maybe_ne (GET_MODE_SIZE (vec_mode), UNITS_PER_WORD)
 	      || !vect_worthwhile_without_simd_p (loop_vinfo, code))
             return false;
 
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
   	    dump_printf (MSG_NOTE, "proceeding using word mode.\n");
         }
 
@@ -6683,7 +6685,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       if (!VECTOR_MODE_P (TYPE_MODE (vectype_in))
 	  && !vect_worthwhile_without_simd_p (loop_vinfo, code))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not worthwhile without SIMD support.\n");
 
@@ -6789,7 +6791,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	      && !direct_internal_fn_supported_p (reduc_fn, vectype_out,
 						  OPTIMIZE_FOR_SPEED))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "reduc op not supported by target.\n");
 
@@ -6800,7 +6802,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  if (!nested_cycle || double_reduc)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "no reduc code for scalar code.\n");
 
@@ -6825,7 +6827,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       && reduc_fn == IFN_LAST
       && !nunits_out.is_constant ())
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "missing target support for reduction on"
 			 " variable-length vectors.\n");
@@ -6835,7 +6837,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
   if ((double_reduc || reduction_type != TREE_CODE_REDUCTION)
       && ncopies > 1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "multiple types in double reduction or condition "
 			 "reduction.\n");
@@ -6865,7 +6867,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 		 l += a[j];
 
 	 which is a reassociation of the original operation.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "in-order double reduction not supported.\n");
 
@@ -6878,7 +6880,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
     {
       /* We cannot use in-order reductions in this case because there is
 	 an implicit reassociation of the operations involved.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "in-order unchained SLP reductions not supported.\n");
       return false;
@@ -6893,7 +6895,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       && !direct_internal_fn_supported_p (IFN_VEC_SHL_INSERT,
 					  vectype_out, OPTIMIZE_FOR_SPEED))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "reduction on variable-length vectors requires"
 			 " target support for a vector-shift-and-insert"
@@ -6916,7 +6918,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       if (!neutral_op
 	  && !can_duplicate_and_interleave_p (group_size, elt_mode))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported form of SLP reduction for"
 			     " variable-length vectors: cannot build"
@@ -6928,7 +6930,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	 up the the initial vector does too.  */
       if (!multiple_p (nunits_out, group_size))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported form of SLP reduction for"
 			     " variable-length vectors: the vector size"
@@ -6949,7 +6951,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
         ops[1] = fold_convert (TREE_TYPE (ops[0]), ops[1]);
       else
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "invalid types in dot-prod\n");
 
@@ -6963,7 +6965,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 
       if (! max_loop_iterations (loop, &ni))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "loop count not known, cannot create cond "
 			     "reduction.\n");
@@ -6978,7 +6980,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
       tree max_index = TYPE_MAX_VALUE (cr_index_scalar_type);
       if (wi::geu_p (ni, wi::to_widest (max_index)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "loop size is greater than data size.\n");
 	  return false;
@@ -7036,7 +7038,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	  || code == WIDEN_SUM_EXPR
 	  || code == SAD_EXPR))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "multi def-use cycle not possible for lane-reducing "
 			 "reduction operation\n");
@@ -7062,7 +7064,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 		  || !direct_internal_fn_supported_p (cond_fn, vectype_in,
 						      OPTIMIZE_FOR_SPEED)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "can't use a fully-masked loop because no"
 				 " conditional operation is available.\n");
@@ -7070,7 +7072,7 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	    }
 	  else if (reduc_index == -1)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "can't use a fully-masked loop for chained"
 				 " reductions.\n");
@@ -7080,17 +7082,17 @@ vectorizable_reduction (gimple *stmt, gimple_stmt_iterator *gsi,
 	    vect_record_loop_mask (loop_vinfo, masks, ncopies * vec_num,
 				   vectype_in);
 	}
-      if (dump_enabled_p ()
-	  && reduction_type == FOLD_LEFT_REDUCTION)
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "using an in-order (fold-left) reduction.\n");
+      if (reduction_type == FOLD_LEFT_REDUCTION)
+	IF_VECT_DUMP
+	  dump_printf_loc (MSG_NOTE, vect_location,
+			   "using an in-order (fold-left) reduction.\n");
       STMT_VINFO_TYPE (stmt_info) = reduc_vec_info_type;
       return true;
     }
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "transform reduction.\n");
 
   /* FORNOW: Multiple types are not supported for condition.  */
@@ -7387,7 +7389,7 @@ vectorizable_induction (gimple *phi,
 
       if (ncopies > 1)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "multiple types in nested loop.\n");
 	  return false;
@@ -7418,7 +7420,7 @@ vectorizable_induction (gimple *phi,
 	  if (!(STMT_VINFO_RELEVANT_P (exit_phi_vinfo)
 		&& !STMT_VINFO_LIVE_P (exit_phi_vinfo)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "inner-loop induction only used outside "
 				 "of the outer vectorized loop.\n");
@@ -7436,7 +7438,7 @@ vectorizable_induction (gimple *phi,
   if (slp_node && !nunits.is_constant ())
     {
       /* The current SLP code creates the initial value element-by-element.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "SLP induction not supported for variable-length"
 			 " vectors.\n");
@@ -7458,7 +7460,7 @@ vectorizable_induction (gimple *phi,
      evolution S, for a vector of 4 units, we want to compute:
      [X, X + S, X + 2*S, X + 3*S].  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "transform induction phi.\n");
 
   latch_e = loop_latch_edge (iv_loop);
@@ -7850,7 +7852,7 @@ vectorizable_induction (gimple *phi,
 		      && !STMT_VINFO_LIVE_P (stmt_vinfo));
 
 	  STMT_VINFO_VEC_STMT (stmt_vinfo) = new_stmt;
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "vector of inductions after inner-loop:");
@@ -7860,7 +7862,7 @@ vectorizable_induction (gimple *phi,
     }
 
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "transform induction: created def-use cycle: ");
@@ -7912,7 +7914,7 @@ vectorizable_live_operation (gimple *stmt,
   if (!STMT_VINFO_RELEVANT_P (stmt_info))
     {
       gcc_assert (is_simple_and_all_uses_invariant (stmt, loop_vinfo));
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "statement is simple and uses invariant.  Leaving in "
 			 "place.\n");
@@ -7940,7 +7942,7 @@ vectorizable_live_operation (gimple *stmt,
 	 that vector we need.  */
       if (!can_div_trunc_p (pos, nunits, &vec_entry, &vec_index))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "Cannot determine which vector holds the"
 			     " final result.\n");
@@ -7956,7 +7958,7 @@ vectorizable_live_operation (gimple *stmt,
 	  if (!direct_internal_fn_supported_p (IFN_EXTRACT_LAST, vectype,
 					       OPTIMIZE_FOR_SPEED))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "can't use a fully-masked loop because "
 				 "the target doesn't support extract last "
@@ -7965,7 +7967,7 @@ vectorizable_live_operation (gimple *stmt,
 	    }
 	  else if (slp_node)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "can't use a fully-masked loop because an "
 				 "SLP statement is live after the loop.\n");
@@ -7973,7 +7975,7 @@ vectorizable_live_operation (gimple *stmt,
 	    }
 	  else if (ncopies > 1)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "can't use a fully-masked loop because"
 				 " ncopies is greater than 1.\n");
@@ -8118,7 +8120,7 @@ vect_loop_kill_debug_uses (struct loop *loop, gimple *stmt)
 	    {
 	      if (gimple_debug_bind_p (ustmt))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_NOTE, vect_location,
                                      "killing debug use\n");
 
@@ -8330,7 +8332,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   if (th >= vect_vf_for_cost (loop_vinfo)
       && !LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "Profitability threshold is %d loop iterations.\n",
                          th);
@@ -8343,7 +8345,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   if (! single_pred_p (e->dest))
     {
       split_loop_exit_edge (e);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf (MSG_NOTE, "split exit edge\n");
     }
 
@@ -8377,7 +8379,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
       if (! single_pred_p (e->dest))
 	{
 	  split_loop_exit_edge (e);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf (MSG_NOTE, "split exit edge of scalar loop\n");
 	}
     }
@@ -8432,7 +8434,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	   gsi_next (&si))
         {
 	  gphi *phi = si.phi ();
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
                                "------>vectorizing phi: ");
@@ -8451,16 +8453,16 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 
 	  if (STMT_VINFO_VECTYPE (stmt_info)
 	      && (maybe_ne
-		  (TYPE_VECTOR_SUBPARTS (STMT_VINFO_VECTYPE (stmt_info)), vf))
-	      && dump_enabled_p ())
-	    dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
+		  (TYPE_VECTOR_SUBPARTS (STMT_VINFO_VECTYPE (stmt_info)), vf)))
+	    IF_VECT_DUMP
+	      dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
 
 	  if ((STMT_VINFO_DEF_TYPE (stmt_info) == vect_induction_def
 	       || STMT_VINFO_DEF_TYPE (stmt_info) == vect_reduction_def
 	       || STMT_VINFO_DEF_TYPE (stmt_info) == vect_nested_cycle)
 	      && ! PURE_SLP_STMT (stmt_info))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_NOTE, vect_location, "transform phi.\n");
 	      vect_transform_stmt (phi, NULL, NULL, NULL, NULL);
 	    }
@@ -8487,7 +8489,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 		}
 	    }
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "------>vectorizing statement: ");
@@ -8559,7 +8561,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 
 		  if (!gsi_end_p (pattern_def_si))
 		    {
-		      if (dump_enabled_p ())
+		      IF_VECT_DUMP
 			{
 			  dump_printf_loc (MSG_NOTE, vect_location,
 					   "==> vectorizing pattern def "
@@ -8586,11 +8588,11 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	      poly_uint64 nunits
 		= TYPE_VECTOR_SUBPARTS (STMT_VINFO_VECTYPE (stmt_info));
 	      if (!STMT_SLP_TYPE (stmt_info)
-		  && maybe_ne (nunits, vf)
-		  && dump_enabled_p ())
+		  && maybe_ne (nunits, vf))
+		IF_VECT_DUMP
 		  /* For SLP VF is set according to unrolling factor, and not
 		     to vector size, hence for SLP this print is not valid.  */
-		dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
+		  dump_printf_loc (MSG_NOTE, vect_location, "multiple-types.\n");
 	    }
 
 	  /* SLP. Schedule all the SLP instances when the first SLP stmt is
@@ -8619,7 +8621,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	    }
 
 	  /* -------- vectorize statement ------------ */
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location, "transform statement.\n");
 
 	  grouped_store = false;
@@ -8731,7 +8733,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 	 : wi::udiv_floor (loop->nb_iterations_estimate + bias_for_assumed,
 			   assumed_vf) - 1);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       if (!LOOP_VINFO_EPILOGUE_P (loop_vinfo))
 	{
@@ -8901,7 +8903,7 @@ optimize_mask_stores (struct loop *loop)
       make_single_succ_edge (store_bb, join_bb, EDGE_FALLTHRU);
       if (dom_info_available_p (CDI_DOMINATORS))
 	set_immediate_dominator (CDI_DOMINATORS, store_bb, bb);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "Create new block %d to sink mask stores.",
 			 store_bb->index);
@@ -8940,7 +8942,7 @@ optimize_mask_stores (struct loop *loop)
 	  gsi_move_before (&gsi_from, &gsi_to);
 	  /* Setup GSI_TO to the non-empty block start.  */
 	  gsi_to = gsi_start_bb (store_bb);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "Move stmt to created bb\n");
@@ -9008,7 +9010,7 @@ optimize_mask_stores (struct loop *loop)
 		break;
 
 	      /* Can move STMT1 to STORE_BB.  */
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location,
 				   "Move stmt to created bb\n");
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c
index 23f1ab8..8ca89bf 100644
--- a/gcc/tree-vect-patterns.c
+++ b/gcc/tree-vect-patterns.c
@@ -449,7 +449,7 @@ vect_recog_dot_prod_pattern (vec<gimple *> *stmts, tree *type_in,
   pattern_stmt = gimple_build_assign (var, DOT_PROD_EXPR,
 				      oprnd00, oprnd01, oprnd1);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_dot_prod_pattern: detected: ");
@@ -683,7 +683,7 @@ vect_recog_sad_pattern (vec<gimple *> *stmts, tree *type_in,
   gimple *pattern_stmt = gimple_build_assign (var, SAD_EXPR, sad_oprnd0,
 					      sad_oprnd1, plus_oprnd1);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_sad_pattern: detected: ");
@@ -965,7 +965,7 @@ vect_recog_widen_mult_pattern (vec<gimple *> *stmts,
 	       TYPE_UNSIGNED (type));
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_widen_mult_pattern: detected:\n");
 
@@ -1016,7 +1016,7 @@ vect_recog_widen_mult_pattern (vec<gimple *> *stmts,
 					  gimple_assign_lhs (pattern_stmt));
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -1286,7 +1286,7 @@ vect_recog_widen_sum_pattern (vec<gimple *> *stmts, tree *type_in,
   var = vect_recog_temp_ssa_var (type, NULL);
   pattern_stmt = gimple_build_assign (var, WIDEN_SUM_EXPR, oprnd0, oprnd1);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_widen_sum_pattern: detected: ");
@@ -1585,7 +1585,7 @@ vect_recog_over_widening_pattern (vec<gimple *> *stmts,
       STMT_VINFO_RELATED_STMT (vinfo_for_stmt (stmt)) = pattern_stmt;
       new_pattern_def_seq (vinfo_for_stmt (stmt), new_def_stmt);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "created pattern stmt: ");
@@ -1652,7 +1652,7 @@ vect_recog_over_widening_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_over_widening_pattern: detected: ");
@@ -1789,7 +1789,7 @@ vect_recog_widen_shift_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_widen_shift_pattern: detected:\n");
 
@@ -1822,7 +1822,7 @@ vect_recog_widen_shift_pattern (vec<gimple *> *stmts,
       STMT_VINFO_VECTYPE (new_stmt_info) = vectype;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2059,7 +2059,7 @@ vect_recog_rotate_pattern (vec<gimple *> *stmts, tree *type_in, tree *type_out)
   append_pattern_def_seq (stmt_vinfo, def_stmt);
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "vect_recog_rotate_pattern: detected:\n");
 
@@ -2067,7 +2067,7 @@ vect_recog_rotate_pattern (vec<gimple *> *stmts, tree *type_in, tree *type_out)
   var = vect_recog_temp_ssa_var (type, NULL);
   pattern_stmt = gimple_build_assign (var, BIT_IOR_EXPR, var1, var2);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2203,7 +2203,7 @@ vect_recog_vector_vector_shift_pattern (vec<gimple *> *stmts,
     }
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_vector_vector_shift_pattern: detected:\n");
 
@@ -2211,7 +2211,7 @@ vect_recog_vector_vector_shift_pattern (vec<gimple *> *stmts,
   var = vect_recog_temp_ssa_var (TREE_TYPE (oprnd0), NULL);
   pattern_stmt = gimple_build_assign (var, rhs_code, oprnd0, def);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt, 0);
 
   stmts->safe_push (last_stmt);
@@ -2580,11 +2580,11 @@ vect_recog_mult_pattern (vec<gimple *> *stmts,
     return NULL;
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "vect_recog_mult_pattern: detected:\n");
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM,
 			  pattern_stmt,0);
 
@@ -2702,7 +2702,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
 	return NULL;
 
       /* Pattern detected.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_divmod_pattern: detected:\n");
 
@@ -2788,7 +2788,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
 				   signmask);
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_gimple_stmt_loc (MSG_NOTE, vect_location, TDF_SLIM, pattern_stmt,
                               0);
 
@@ -3042,7 +3042,7 @@ vect_recog_divmod_pattern (vec<gimple *> *stmts,
     }
 
   /* Pattern detected.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_recog_divmod_pattern: detected: ");
@@ -3207,7 +3207,7 @@ vect_recog_mixed_size_cond_pattern (vec<gimple *> *stmts, tree *type_in,
   *type_in = vecitype;
   *type_out = vectype;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_recog_mixed_size_cond_pattern: detected:\n");
 
@@ -3788,7 +3788,7 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_bool_pattern: detected:\n");
 
@@ -3829,7 +3829,7 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_bool_pattern: detected:\n");
 
@@ -3888,7 +3888,7 @@ vect_recog_bool_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype;
       *type_in = vectype;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_bool_pattern: detected:\n");
       return pattern_stmt;
@@ -4025,7 +4025,7 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype1;
       *type_in = vectype1;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_mask_conversion_pattern: detected:\n");
 
@@ -4151,7 +4151,7 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
       *type_out = vectype1;
       *type_in = vectype1;
       stmts->safe_push (last_stmt);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_recog_mask_conversion_pattern: detected:\n");
 
@@ -4199,7 +4199,7 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
   *type_out = vectype1;
   *type_in = vectype1;
   stmts->safe_push (last_stmt);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "vect_recog_mask_conversion_pattern: detected:\n");
 
@@ -4384,7 +4384,7 @@ vect_try_gather_scatter_pattern (gimple *stmt, stmt_vec_info last_stmt_info,
   *type_out = vectype;
   *type_in = vectype;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "gather/scatter pattern detected:\n");
 
@@ -4538,7 +4538,7 @@ vect_pattern_recog_1 (vect_recog_func *recog_func,
     }
 
   /* Found a vectorizable pattern.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "%s pattern recognized: ", recog_func->name);
@@ -4567,7 +4567,7 @@ vect_pattern_recog_1 (vect_recog_func *recog_func,
     {
       stmt_info = vinfo_for_stmt (stmt);
       pattern_stmt = STMT_VINFO_RELATED_STMT (stmt_info);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "additional pattern stmt: ");
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index ab703da..c860ee1 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -350,7 +350,7 @@ again:
 
       if (!vect_is_simple_use (oprnd, vinfo, &def_stmt, &dt))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "Build SLP failed: can't analyze def for ");
@@ -386,7 +386,7 @@ again:
 		  goto again;
 		}
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "Build SLP failed: some of the stmts"
@@ -403,7 +403,7 @@ again:
 
           if (dt == vect_unknown_def_type)
             {
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Unsupported pattern.\n");
               return -1;
@@ -416,7 +416,7 @@ again:
 	      break;
 
 	    default:
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "unsupported defining stmt:\n");
 	      return -1;
@@ -458,7 +458,7 @@ again:
 		  goto again;
 		}
 
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Build SLP failed: different types\n");
 
@@ -471,7 +471,7 @@ again:
 		  || !can_duplicate_and_interleave_p (stmts.length (),
 						      TYPE_MODE (type))))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "Build SLP failed: invalid type of def "
@@ -498,7 +498,7 @@ again:
 
 	default:
 	  /* FORNOW: Not supported.  */
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "Build SLP failed: illegal type of def ");
@@ -517,7 +517,7 @@ again:
          we've committed to the operand order and can't swap it.  */
       if (STMT_VINFO_NUM_SLP_USES (vinfo_for_stmt (stmt)) != 0)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "Build SLP failed: cannot swap operands of "
@@ -553,7 +553,7 @@ again:
       else
 	swap_ssa_operands (stmt, gimple_assign_rhs1_ptr (stmt),
 			   gimple_assign_rhs2_ptr (stmt));
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "swapped operands to match def types in ");
@@ -578,7 +578,7 @@ vect_record_max_nunits (vec_info *vinfo, gimple *stmt, unsigned int group_size,
 {
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "Build SLP failed: unsupported data-type in ");
@@ -678,7 +678,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       swap[i] = 0;
       matches[i] = false;
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location, "Build SLP for ");
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -687,7 +687,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       /* Fail to vectorize statements marked as unvectorizable.  */
       if (!STMT_VINFO_VECTORIZABLE (vinfo_for_stmt (stmt)))
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "Build SLP failed: unvectorizable statement ");
@@ -701,7 +701,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
       lhs = gimple_get_lhs (stmt);
       if (lhs == NULL_TREE)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "Build SLP failed: not GIMPLE_ASSIGN nor "
@@ -736,7 +736,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	      || !gimple_call_nothrow_p (call_stmt)
 	      || gimple_call_chain (call_stmt))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
 				   "Build SLP failed: unsupported call type ");
@@ -764,7 +764,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	    {
 	      if (vectype == boolean_type_node)
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "Build SLP failed: shift of a"
 				     " boolean.\n");
@@ -788,7 +788,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 
 		  if (!optab)
 		    {
-		      if (dump_enabled_p ())
+		      IF_VECT_DUMP
 			dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 					 "Build SLP failed: no optab.\n");
 		      /* Fatal mismatch.  */
@@ -798,7 +798,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		  icode = (int) optab_handler (optab, vec_mode);
 		  if (icode == CODE_FOR_nothing)
 		    {
-		      if (dump_enabled_p ())
+		      IF_VECT_DUMP
 			dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 					 "Build SLP failed: "
 					 "op not supported by target.\n");
@@ -844,7 +844,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
                        || first_stmt_code == COMPONENT_REF
                        || first_stmt_code == MEM_REF)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
 				   "Build SLP failed: different operation "
@@ -862,7 +862,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	  if (need_same_oprnds
 	      && !operand_equal_p (first_op1, gimple_assign_rhs2 (stmt), 0))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
 				   "Build SLP failed: different shift "
@@ -882,7 +882,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		  || gimple_call_fntype (first_stmt)
 		     != gimple_call_fntype (stmt))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    {
 		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
 				       "Build SLP failed: different calls in ");
@@ -913,7 +913,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
                      chains in the same node.  */
                   if (prev_first_load != first_load)
                     {
-                      if (dump_enabled_p ())
+                      IF_VECT_DUMP
                         {
                           dump_printf_loc (MSG_MISSED_OPTIMIZATION,
 					   vect_location, 
@@ -935,7 +935,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	  if (TREE_CODE_CLASS (rhs_code) == tcc_reference)
 	    {
 	      /* Not grouped load.  */
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, 
 				   "Build SLP failed: not grouped load ");
@@ -955,7 +955,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	      && TREE_CODE_CLASS (rhs_code) != tcc_comparison
 	      && rhs_code != CALL_EXPR)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "Build SLP failed: operation");
@@ -993,7 +993,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 		swap[i] = 2;
 	      else
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    {
 		      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				       "Build SLP failed: different"
@@ -1027,7 +1027,7 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap,
 	    if (gimple_assign_rhs_code (stmts[i]) == alt_stmt_code)
 	      {
 		matches[i] = false;
-		if (dump_enabled_p ())
+		IF_VECT_DUMP
 		  {
 		    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				     "Build SLP failed: different operation "
@@ -1374,7 +1374,7 @@ vect_build_slp_tree_2 (vec_info *vinfo,
 		    {
 		      if (!swap_not_matching)
 			{
-			  if (dump_enabled_p ())
+			  IF_VECT_DUMP
 			    {
 			      dump_printf_loc (MSG_MISSED_OPTIMIZATION,
 					       vect_location,
@@ -1510,7 +1510,7 @@ fail:
 /* Dump a slp tree NODE using flags specified in DUMP_KIND.  */
 
 static void
-vect_print_slp_tree (dump_flags_t dump_kind, location_t loc, slp_tree node)
+vect_print_slp_tree (dump_flags_t dump_kind, optinfo_location loc, slp_tree node)
 {
   int i;
   gimple *stmt;
@@ -1686,7 +1686,7 @@ vect_supported_load_permutation_p (slp_instance slp_instn)
   slp_tree node;
   gimple *stmt, *load, *next_load;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "Load permutation ");
       FOR_EACH_VEC_ELT (SLP_INSTANCE_LOADS (slp_instn), i, node)
@@ -1865,7 +1865,7 @@ vect_split_slp_store_group (gimple *first_stmt, unsigned group1_size)
   /* DR_GROUP_GAP of the first group now has to skip over the second group too.  */
   DR_GROUP_GAP (first_vinfo) += group2_size;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "Split group into %d and %d\n",
 		     group1_size, group2_size);
 
@@ -1920,7 +1920,7 @@ vect_analyze_slp_instance (vec_info *vinfo,
 
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "Build SLP failed: unsupported data-type ");
@@ -2000,7 +2000,7 @@ vect_analyze_slp_instance (vec_info *vinfo,
 	  if (!max_nunits.is_constant (&const_max_nunits)
 	      || const_max_nunits > group_size)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Build SLP failed: store group "
 				 "size not a multiple of the vector size "
@@ -2063,7 +2063,7 @@ vect_analyze_slp_instance (vec_info *vinfo,
         {
           if (!vect_supported_load_permutation_p (new_instance))
             {
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 {
                   dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "Build SLP failed: unsupported load "
@@ -2098,7 +2098,7 @@ vect_analyze_slp_instance (vec_info *vinfo,
 	    }
 	  if (i == loads.length ())
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Built SLP cancelled: can use "
 				 "load/store-lanes\n");
@@ -2109,7 +2109,7 @@ vect_analyze_slp_instance (vec_info *vinfo,
 
       vinfo->slp_instances.safe_push (new_instance);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "Final SLP tree for instance:\n");
@@ -2250,14 +2250,15 @@ vect_make_slp_decision (loop_vec_info loop_vinfo)
 
   LOOP_VINFO_SLP_UNROLLING_FACTOR (loop_vinfo) = unrolling_factor;
 
-  if (decided_to_slp && dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_NOTE, vect_location,
-		       "Decided to SLP %d instances. Unrolling factor ",
-		       decided_to_slp);
-      dump_dec (MSG_NOTE, unrolling_factor);
-      dump_printf (MSG_NOTE, "\n");
-    }
+  if (decided_to_slp)
+    IF_VECT_DUMP
+      {
+	dump_printf_loc (MSG_NOTE, vect_location,
+			 "Decided to SLP %d instances. Unrolling factor ",
+			 decided_to_slp);
+	dump_dec (MSG_NOTE, unrolling_factor);
+	dump_printf (MSG_NOTE, "\n");
+      }
 
   return (decided_to_slp > 0);
 }
@@ -2312,7 +2313,7 @@ vect_detect_hybrid_slp_stmts (slp_tree node, unsigned i, slp_vect_type stype)
 		&& !(gimple_code (use_stmt) == GIMPLE_PHI
 		     && STMT_VINFO_DEF_TYPE (use_vinfo) == vect_reduction_def))
 	      {
-		if (dump_enabled_p ())
+		IF_VECT_DUMP
 		  {
 		    dump_printf_loc (MSG_NOTE, vect_location, "use of SLP "
 				     "def in non-SLP stmt: ");
@@ -2326,7 +2327,7 @@ vect_detect_hybrid_slp_stmts (slp_tree node, unsigned i, slp_vect_type stype)
   if (stype == hybrid
       && !HYBRID_SLP_STMT (stmt_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location, "marking hybrid: ");
 	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -2357,7 +2358,7 @@ vect_detect_hybrid_slp_1 (tree *tp, int *, void *data)
       if (flow_bb_inside_loop_p (loopp, gimple_bb (def_stmt))
 	  && PURE_SLP_STMT (vinfo_for_stmt (def_stmt)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "marking hybrid: ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, def_stmt, 0);
@@ -2774,7 +2775,7 @@ vect_bb_vectorization_profitable_p (bb_vec_info bb_vinfo)
 
   vec_outside_cost = vec_prologue_cost + vec_epilogue_cost;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "Cost model analysis: \n");
       dump_printf (MSG_NOTE, "  Vector inside of basic block cost: %d\n",
@@ -2815,7 +2816,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (n_stmts > PARAM_VALUE (PARAM_SLP_MAX_INSNS_IN_BB))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: too many instructions in "
 			 "basic block.\n");
@@ -2833,7 +2834,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_analyze_data_refs (bb_vinfo, &min_vf))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: unhandled data-ref in basic "
 			 "block.\n");
@@ -2844,7 +2845,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (BB_VINFO_DATAREFS (bb_vinfo).length () < 2)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: not enough data-refs in "
 			 "basic block.\n");
@@ -2855,7 +2856,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_analyze_data_ref_accesses (bb_vinfo))
     {
-     if (dump_enabled_p ())
+     IF_VECT_DUMP
        dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			"not vectorized: unhandled data access in "
 			"basic block.\n");
@@ -2869,7 +2870,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
      anyway.  */
   if (bb_vinfo->grouped_stores.is_empty ())
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: no grouped stores in "
 			 "basic block.\n");
@@ -2887,7 +2888,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
      trees.  */
   if (!vect_analyze_slp (bb_vinfo, n_stmts))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "Failed to SLP the basic block.\n");
@@ -2934,7 +2935,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
 
   if (!vect_slp_analyze_operations (bb_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: bad operation in basic block.\n");
 
@@ -2946,7 +2947,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
   if (!unlimited_cost_model (NULL)
       && !vect_bb_vectorization_profitable_p (bb_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not vectorized: vectorization is not "
 			 "profitable.\n");
@@ -2955,7 +2956,7 @@ vect_slp_analyze_bb_1 (gimple_stmt_iterator region_begin,
       return NULL;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "Basic block will be vectorized using SLP\n");
 
@@ -3001,7 +3002,7 @@ vect_slp_bb (basic_block bb)
 	  insns++;
 
 	  if (gimple_location (stmt) != UNKNOWN_LOCATION)
-	    vect_location = gimple_location (stmt);
+	    vect_location = stmt;
 
 	  if (!vect_find_stmt_data_reference (NULL, stmt, &datarefs))
 	    break;
@@ -3023,12 +3024,12 @@ vect_slp_bb (basic_block bb)
       if (bb_vinfo
 	  && dbg_cnt (vect_slp))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location, "SLPing BB part\n");
 
 	  vect_schedule_slp (bb_vinfo);
 
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "basic block part vectorized\n");
 
@@ -3066,7 +3067,7 @@ vect_slp_bb (basic_block bb)
 	{
 	  /* Try the next biggest vector size.  */
 	  current_vector_size = vector_sizes[next_size++];
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location,
 			       "***** Re-trying analysis with "
@@ -3730,7 +3731,7 @@ vect_transform_slp_perm_load (slp_tree node, vec<tree> dr_chain,
 	    }
 	  else
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "permutation requires at "
@@ -3752,7 +3753,7 @@ vect_transform_slp_perm_load (slp_tree node, vec<tree> dr_chain,
 	      indices.new_vector (mask, 2, nunits);
 	      if (!can_vec_perm_const_p (mode, indices))
 		{
-		  if (dump_enabled_p ())
+		  IF_VECT_DUMP
 		    {
 		      dump_printf_loc (MSG_MISSED_OPTIMIZATION,
 				       vect_location, 
@@ -3867,7 +3868,7 @@ vect_schedule_slp_instance (slp_tree node, slp_instance instance,
   if (!SLP_TREE_VEC_STMTS (node).exists ())
     SLP_TREE_VEC_STMTS (node).create (SLP_TREE_NUMBER_OF_VEC_STMTS (node));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE,vect_location,
 		       "------>vectorizing SLP node starting from: ");
@@ -4030,7 +4031,7 @@ vect_schedule_slp (vec_info *vinfo)
       /* Schedule the tree of INSTANCE.  */
       is_store = vect_schedule_slp_instance (SLP_INSTANCE_TREE (instance),
                                              instance, bst_map);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "vectorizing stmts using SLP.\n");
     }
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index d683665..c460782 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -204,7 +204,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
   bool save_live_p = STMT_VINFO_LIVE_P (stmt_info);
   gimple *pattern_stmt;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "mark relevant %d, live %d: ", relevant, live_p);
@@ -224,7 +224,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
 
       pattern_stmt = STMT_VINFO_RELATED_STMT (stmt_info);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "last stmt in pattern. don't mark"
 			 " relevant/live.\n");
@@ -242,7 +242,7 @@ vect_mark_relevant (vec<gimple *> *worklist, gimple *stmt,
   if (STMT_VINFO_RELEVANT (stmt_info) == save_relevant
       && STMT_VINFO_LIVE_P (stmt_info) == save_live_p)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "already marked relevant/live.\n");
       return;
@@ -272,7 +272,7 @@ is_simple_and_all_uses_invariant (gimple *stmt, loop_vec_info loop_vinfo)
 
       if (!vect_is_simple_use (op, loop_vinfo, &def_stmt, &dt))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "use not simple.\n");
 	  return false;
@@ -320,7 +320,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
     if (gimple_vdef (stmt)
 	&& !gimple_clobber_p (stmt))
       {
-	if (dump_enabled_p ())
+	IF_VECT_DUMP
 	  dump_printf_loc (MSG_NOTE, vect_location,
                            "vec_stmt_relevant_p: stmt has vdefs.\n");
 	*relevant = vect_used_in_scope;
@@ -334,7 +334,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
 	  basic_block bb = gimple_bb (USE_STMT (use_p));
 	  if (!flow_bb_inside_loop_p (loop, bb))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_NOTE, vect_location,
                                  "vec_stmt_relevant_p: used out of loop.\n");
 
@@ -354,7 +354,7 @@ vect_stmt_relevant_p (gimple *stmt, loop_vec_info loop_vinfo,
   if (*live_p && *relevant == vect_unused_in_scope
       && !is_simple_and_all_uses_invariant (stmt, loop_vinfo))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "vec_stmt_relevant_p: stmt live but not relevant.\n");
       *relevant = vect_used_only_live;
@@ -474,7 +474,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 
   if (!vect_is_simple_use (use, loop_vinfo, &def_stmt, &dt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "not vectorized: unsupported use in stmt.\n");
       return false;
@@ -486,7 +486,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
   def_bb = gimple_bb (def_stmt);
   if (!flow_bb_inside_loop_p (loop, def_bb))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location, "def_stmt is out of loop.\n");
       return true;
     }
@@ -504,7 +504,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
       && STMT_VINFO_DEF_TYPE (dstmt_vinfo) == vect_reduction_def
       && bb->loop_father == def_bb->loop_father)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "reduc-stmt defining reduc-phi in the same nest.\n");
       if (STMT_VINFO_IN_PATTERN_P (dstmt_vinfo))
@@ -524,7 +524,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 		...		  */
   if (flow_loop_nested_p (def_bb->loop_father, bb->loop_father))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "outer-loop def-stmt defining inner-loop stmt.\n");
 
@@ -562,7 +562,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 		stmt # use (d)		*/
   else if (flow_loop_nested_p (bb->loop_father, def_bb->loop_father))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "inner-loop def-stmt defining outer-loop stmt.\n");
 
@@ -597,7 +597,7 @@ process_use (gimple *stmt, tree use, loop_vec_info loop_vinfo,
 	   && (PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (bb->loop_father))
 	       == use))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "induction value on backedge.\n");
       return true;
@@ -651,7 +651,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
 	{
 	  phi = gsi_stmt (si);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "init: phi relevant? ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, phi, 0);
@@ -663,7 +663,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
 	{
 	  stmt = gsi_stmt (si);
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_NOTE, vect_location, "init: stmt relevant? ");
 	      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -681,7 +681,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
       ssa_op_iter iter;
 
       stmt = worklist.pop ();
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
           dump_printf_loc (MSG_NOTE, vect_location, "worklist: examine stmt: ");
           dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -713,7 +713,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
 	      {
-		if (dump_enabled_p ())
+		IF_VECT_DUMP
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "unsupported use of reduction.\n");
 		return false;
@@ -725,7 +725,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_in_outer_by_reduction
 		&& relevant != vect_used_in_outer)
               {
-                if (dump_enabled_p ())
+                IF_VECT_DUMP
                   dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                    "unsupported use of nested cycle.\n");
 
@@ -738,7 +738,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
 		&& relevant != vect_used_by_reduction
 		&& relevant != vect_used_only_live)
               {
-                if (dump_enabled_p ())
+                IF_VECT_DUMP
                   dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                    "unsupported use of double reduction.\n");
 
@@ -939,7 +939,7 @@ vect_model_simple_cost (stmt_vec_info stmt_info, int ncopies,
   inside_cost += record_stmt_cost (cost_vec, ncopies, vector_stmt,
 				   stmt_info, 0, vect_body);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_simple_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -974,7 +974,7 @@ vect_model_promotion_demotion_cost (stmt_vec_info stmt_info,
       prologue_cost += record_stmt_cost (cost_vec, 1, vector_stmt,
 					 stmt_info, 0, vect_prologue);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_promotion_demotion_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -1034,7 +1034,7 @@ vect_model_store_cost (stmt_vec_info stmt_info, int ncopies,
       inside_cost = record_stmt_cost (cost_vec, nstmts, vec_perm,
 				      stmt_info, 0, vect_body);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_model_store_cost: strided group_size = %d .\n",
                          group_size);
@@ -1064,7 +1064,7 @@ vect_model_store_cost (stmt_vec_info stmt_info, int ncopies,
 				       vec_to_scalar, stmt_info, 0, vect_body);
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_store_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -1088,7 +1088,7 @@ vect_get_store_cost (stmt_vec_info stmt_info, int ncopies,
 					  vector_store, stmt_info, 0,
 					  vect_body);
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_store_cost: aligned.\n");
         break;
@@ -1100,7 +1100,7 @@ vect_get_store_cost (stmt_vec_info stmt_info, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies,
 					  unaligned_store, stmt_info,
 					  DR_MISALIGNMENT (dr), vect_body);
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_store_cost: unaligned supported by "
                            "hardware.\n");
@@ -1111,7 +1111,7 @@ vect_get_store_cost (stmt_vec_info stmt_info, int ncopies,
       {
         *inside_cost = VECT_MAX_COST;
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "vect_model_store_cost: unsupported access.\n");
         break;
@@ -1214,7 +1214,7 @@ vect_model_load_cost (stmt_vec_info stmt_info, unsigned ncopies,
       inside_cost += record_stmt_cost (cost_vec, nstmts, vec_perm,
 				       stmt_info, 0, vect_body);
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vect_model_load_cost: strided group_size = %d .\n",
                          group_size);
@@ -1240,7 +1240,7 @@ vect_model_load_cost (stmt_vec_info stmt_info, unsigned ncopies,
     inside_cost += record_stmt_cost (cost_vec, ncopies, vec_construct,
 				     stmt_info, 0, vect_body);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vect_model_load_cost: inside_cost = %d, "
                      "prologue_cost = %d .\n", inside_cost, prologue_cost);
@@ -1266,7 +1266,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies, vector_load,
 					  stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: aligned.\n");
 
@@ -1279,7 +1279,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
 					  unaligned_load, stmt_info,
 					  DR_MISALIGNMENT (dr), vect_body);
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: unaligned supported by "
                            "hardware.\n");
@@ -1300,7 +1300,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
 	  *inside_cost += record_stmt_cost (body_cost_vec, 1, vector_stmt,
 					    stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: explicit realign\n");
 
@@ -1308,7 +1308,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
       }
     case dr_explicit_realign_optimized:
       {
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: unaligned software "
                            "pipelined.\n");
@@ -1336,7 +1336,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
 	*inside_cost += record_stmt_cost (body_cost_vec, ncopies, vec_perm,
 					  stmt_info, 0, vect_body);
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_NOTE, vect_location,
                            "vect_model_load_cost: explicit realign optimized"
                            "\n");
@@ -1348,7 +1348,7 @@ vect_get_load_cost (stmt_vec_info stmt_info, int ncopies,
       {
         *inside_cost = VECT_MAX_COST;
 
-        if (dump_enabled_p ())
+        IF_VECT_DUMP
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "vect_model_load_cost: unsupported access.\n");
         break;
@@ -1398,7 +1398,7 @@ vect_init_vector_1 (gimple *stmt, gimple *new_stmt, gimple_stmt_iterator *gsi)
        }
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "created new init_stmt: ");
@@ -1559,7 +1559,7 @@ vect_get_vec_def_for_operand (tree op, gimple *stmt, tree vectype)
   stmt_vec_info stmt_vinfo = vinfo_for_stmt (stmt);
   loop_vec_info loop_vinfo = STMT_VINFO_LOOP_VINFO (stmt_vinfo);
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_get_vec_def_for_operand: ");
@@ -1569,11 +1569,12 @@ vect_get_vec_def_for_operand (tree op, gimple *stmt, tree vectype)
 
   is_simple_use = vect_is_simple_use (op, loop_vinfo, &def_stmt, &dt);
   gcc_assert (is_simple_use);
-  if (def_stmt && dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_NOTE, vect_location, "  def_stmt =  ");
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, def_stmt, 0);
-    }
+  if (def_stmt)
+    IF_VECT_DUMP
+      {
+	dump_printf_loc (MSG_NOTE, vect_location, "  def_stmt =  ");
+	dump_gimple_stmt (MSG_NOTE, TDF_SLIM, def_stmt, 0);
+      }
 
   if (dt == vect_constant_def || dt == vect_external_def)
     {
@@ -1750,7 +1751,7 @@ vect_finish_stmt_generation_1 (gimple *stmt, gimple *vec_stmt)
 
   set_vinfo_for_stmt (vec_stmt, new_stmt_vec_info (vec_stmt, vinfo));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "add new stmt: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, vec_stmt, 0);
@@ -1887,7 +1888,7 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 	  ? !vect_load_lanes_supported (vectype, group_size, true)
 	  : !vect_store_lanes_supported (vectype, group_size, true))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "can't use a fully-masked loop because the"
 			     " target doesn't have an appropriate masked"
@@ -1911,7 +1912,7 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 						   TYPE_SIGN (offset_type),
 						   gs_info->scale))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "can't use a fully-masked loop because the"
 			     " target doesn't have an appropriate masked"
@@ -1929,7 +1930,7 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
     {
       /* Element X of the data must come from iteration i * VF + X of the
 	 scalar loop.  We need more work to support other mappings.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "can't use a fully-masked loop because an access"
 			 " isn't contiguous.\n");
@@ -1943,7 +1944,7 @@ check_load_store_masking (loop_vec_info loop_vinfo, tree vectype,
 	 GET_MODE_SIZE (vecmode)).exists (&mask_mode))
       || !can_vec_mask_load_store_p (vecmode, mask_mode, is_load))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "can't use a fully-masked loop because the target"
 			 " doesn't have the appropriate masked load or"
@@ -2009,7 +2010,7 @@ vect_truncate_gather_scatter_offset (gimple *stmt, loop_vec_info loop_vinfo,
   if (TREE_CODE (step) != INTEGER_CST)
     {
       /* ??? Perhaps we could use range information here?  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "cannot truncate variable step.\n");
       return false;
@@ -2075,10 +2076,11 @@ vect_truncate_gather_scatter_offset (gimple *stmt, loop_vec_info loop_vinfo,
       return true;
     }
 
-  if (overflow_p && dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "truncating gather/scatter offset to %d bits"
-		     " might change its value.\n", element_bits);
+  if (overflow_p)
+    IF_VECT_DUMP
+      dump_printf_loc (MSG_NOTE, vect_location,
+		       "truncating gather/scatter offset to %d bits"
+		       " might change its value.\n", element_bits);
 
   return false;
 }
@@ -2116,7 +2118,7 @@ vect_use_strided_gather_scatters_p (gimple *stmt, loop_vec_info loop_vinfo,
       gs_info->offset = fold_convert (offset_type, gs_info->offset);
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "using gather/scatter for strided/grouped access,"
 		     " scale = %d\n", gs_info->scale);
@@ -2254,7 +2256,7 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
 	    overrun_p = false;
 	  if (overrun_p && !can_overrun_p)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "Peeling for outer loop is not supported\n");
 	      return false;
@@ -2340,7 +2342,7 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
 	  enum vect_def_type dt;
 	  if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "use not simple.\n");
 	      return false;
@@ -2352,7 +2354,7 @@ get_group_load_store_type (gimple *stmt, tree vectype, bool slp,
   if (overrun_p)
     {
       gcc_assert (can_overrun_p);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "Data access with gaps requires scalar "
 			 "epilogue loop\n");
@@ -2377,7 +2379,7 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
 
   if (ncopies > 1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "multiple types with negative step.\n");
       return VMAT_ELEMENTWISE;
@@ -2387,7 +2389,7 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
   if (alignment_support_scheme != dr_aligned
       && alignment_support_scheme != dr_unaligned_supported)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "negative step but alignment required.\n");
       return VMAT_ELEMENTWISE;
@@ -2395,7 +2397,7 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
 
   if (vls_type == VLS_STORE_INVARIANT)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
 			 "negative step with invariant source;"
 			 " no permute needed.\n");
@@ -2404,7 +2406,7 @@ get_negative_load_store_type (gimple *stmt, tree vectype,
 
   if (!perm_mask_for_reverse (vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "negative step and reversing not supported.\n");
       return VMAT_ELEMENTWISE;
@@ -2443,7 +2445,7 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
 				    &gs_info->offset_dt,
 				    &gs_info->offset_vectype))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "%s index use not simple.\n",
 			     vls_type == VLS_LOAD ? "gather" : "scatter");
@@ -2485,7 +2487,7 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
        || *memory_access_type == VMAT_STRIDED_SLP)
       && !nunits.is_constant ())
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "Not using elementwise accesses due to variable "
 			 "vectorization factor.\n");
@@ -2501,7 +2503,7 @@ get_load_store_type (gimple *stmt, tree vectype, bool slp, bool masked_p,
 	   && !DR_GROUP_NEXT_ELEMENT (stmt_info)
 	   && !pow2p_hwi (DR_GROUP_SIZE (stmt_info))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not falling back to elementwise accesses\n");
       return false;
@@ -2521,7 +2523,7 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
 {
   if (!VECT_SCALAR_BOOLEAN_TYPE_P (TREE_TYPE (mask)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "mask argument is not a boolean.\n");
       return false;
@@ -2529,7 +2531,7 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
 
   if (TREE_CODE (mask) != SSA_NAME)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "mask argument is not an SSA name.\n");
       return false;
@@ -2542,7 +2544,7 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
   if (!vect_is_simple_use (mask, stmt_info->vinfo, &def_stmt, &mask_dt,
 			   &mask_vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "mask use not simple.\n");
       return false;
@@ -2554,7 +2556,7 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
 
   if (!mask_vectype || !VECTOR_BOOLEAN_TYPE_P (mask_vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "could not find an appropriate vector mask type.\n");
       return false;
@@ -2563,7 +2565,7 @@ vect_check_load_store_mask (gimple *stmt, tree mask,
   if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_vectype),
 		TYPE_VECTOR_SUBPARTS (vectype)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "vector mask type ");
@@ -2594,7 +2596,7 @@ vect_check_store_rhs (gimple *stmt, tree rhs, vect_def_type *rhs_dt_out,
      native_encode_expr can handle it.  */
   if (CONSTANT_CLASS_P (rhs) && native_encode_expr (rhs, NULL, 64) == 0)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "cannot encode constant as a byte sequence.\n");
       return false;
@@ -2607,7 +2609,7 @@ vect_check_store_rhs (gimple *stmt, tree rhs, vect_def_type *rhs_dt_out,
   if (!vect_is_simple_use (rhs, stmt_info->vinfo, &def_stmt, &rhs_dt,
 			   &rhs_vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "use not simple.\n");
       return false;
@@ -2616,7 +2618,7 @@ vect_check_store_rhs (gimple *stmt, tree rhs, vect_def_type *rhs_dt_out,
   tree vectype = STMT_VINFO_VECTYPE (stmt_info);
   if (rhs_vectype && !useless_type_conversion_p (vectype, rhs_vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "incompatible vector types.\n");
       return false;
@@ -3202,7 +3204,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       if (rhs_type
 	  && !types_compatible_p (rhs_type, TREE_TYPE (op)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "argument types differ.\n");
 	  return false;
@@ -3212,7 +3214,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt[i], &opvectype))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "use not simple.\n");
 	  return false;
@@ -3223,7 +3225,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (opvectype
 	       && opvectype != vectype_in)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "argument vector types differ.\n");
 	  return false;
@@ -3237,7 +3239,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     gcc_assert (vectype_in);
   if (!vectype_in)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "no vectype for scalar type ");
@@ -3263,7 +3265,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* We only handle functions that do not read or clobber memory.  */
   if (gimple_vuse (stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "function reads from or writes to memory.\n");
       return false;
@@ -3321,7 +3323,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 				   vectype_in, dt, cost_vec);
       else
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "function is not vectorizable.\n");
 	  return false;
@@ -3353,7 +3355,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "transform call.\n");
 
   /* Handle def.  */
@@ -3799,7 +3801,7 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
 			       &thisarginfo.vectype)
 	  || thisarginfo.dt == vect_uninitialized_def)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "use not simple.\n");
 	  return false;
@@ -3874,7 +3876,7 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
   unsigned HOST_WIDE_INT vf;
   if (!LOOP_VINFO_VECT_FACTOR (loop_vinfo).is_constant (&vf))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not considering SIMD clones; not yet supported"
 			 " for variable-width vectors.\n");
@@ -4024,7 +4026,7 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "transform call.\n");
 
   /* Handle def.  */
@@ -4644,7 +4646,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 	  || (INTEGRAL_TYPE_P (rhs_type)
 	      && !type_has_mode_precision_p (rhs_type))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "type conversion to/from bit-precision unsupported."
                          "\n");
@@ -4654,7 +4656,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
   /* Check the operands of the operation.  */
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype_in))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "use not simple.\n");
       return false;
@@ -4674,7 +4676,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 
       if (!ok)
 	{
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "use not simple.\n");
 	  return false;
@@ -4689,7 +4691,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype_in);
   if (!vectype_in)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "no vectype for scalar type ");
@@ -4703,7 +4705,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
   if (VECTOR_BOOLEAN_TYPE_P (vectype_out)
       && !VECTOR_BOOLEAN_TYPE_P (vectype_in))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "can't convert between boolean and non "
@@ -4757,7 +4759,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 	break;
       /* FALLTHRU */
     unsupported:
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "conversion not supported by target.\n");
       return false;
@@ -4881,7 +4883,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
     }
 
   /* Transform.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform conversion. ncopies = %d.\n", ncopies);
 
@@ -5227,7 +5229,7 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!vect_is_simple_use (op, vinfo, &def_stmt, &dt[0], &vectype_in))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "use not simple.\n");
       return false;
@@ -5259,7 +5261,7 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
       && (!VECTOR_BOOLEAN_TYPE_P (vectype)
 	  || !VECTOR_BOOLEAN_TYPE_P (vectype_in)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "type conversion to/from bit-precision "
                          "unsupported.\n");
@@ -5275,7 +5277,7 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
     }
 
   /* Transform.  */
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location, "transform assignment.\n");
 
   /* Handle def.  */
@@ -5424,7 +5426,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   vectype_out = STMT_VINFO_VECTYPE (stmt_info);
   if (!type_has_mode_precision_p (TREE_TYPE (scalar_dest)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "bit-precision shifts not supported.\n");
       return false;
@@ -5433,7 +5435,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   op0 = gimple_assign_rhs1 (stmt);
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "use not simple.\n");
       return false;
@@ -5446,7 +5448,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype);
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "no vectype for scalar type\n");
       return false;
@@ -5460,7 +5462,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   op1 = gimple_assign_rhs2 (stmt);
   if (!vect_is_simple_use (op1, vinfo, &def_stmt, &dt[1], &op1_vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "use not simple.\n");
       return false;
@@ -5512,7 +5514,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
     }
   else
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "operand mode requires invariant argument.\n");
       return false;
@@ -5522,7 +5524,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!scalar_shift_arg)
     {
       optab = optab_for_tree_code (code, vectype, optab_vector);
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "vector/vector shift/rotate found.\n");
 
@@ -5531,7 +5533,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       if (op1_vectype == NULL_TREE
 	  || TYPE_MODE (op1_vectype) != TYPE_MODE (vectype))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "unusable type for last operand in"
                              " vector/vector shift/rotate.\n");
@@ -5546,7 +5548,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       if (optab
           && optab_handler (optab, TYPE_MODE (vectype)) != CODE_FOR_nothing)
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location,
                              "vector/scalar shift/rotate found.\n");
         }
@@ -5559,7 +5561,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
             {
 	      scalar_shift_arg = false;
 
-              if (dump_enabled_p ())
+              IF_VECT_DUMP
                 dump_printf_loc (MSG_NOTE, vect_location,
                                  "vector/vector shift/rotate found.\n");
 
@@ -5576,7 +5578,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
 		      && TYPE_MODE (TREE_TYPE (vectype))
 			 != TYPE_MODE (TREE_TYPE (op1)))
 		    {
-                      if (dump_enabled_p ())
+                      IF_VECT_DUMP
                         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                                          "unusable type for last operand in"
                                          " vector/vector shift/rotate.\n");
@@ -5596,7 +5598,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   /* Supportable by target?  */
   if (!optab)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "no optab.\n");
       return false;
@@ -5605,7 +5607,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   icode = (int) optab_handler (optab, vec_mode);
   if (icode == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "op not supported by target.\n");
       /* Check only during analysis.  */
@@ -5613,7 +5615,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
 	  || (!vec_stmt
 	      && !vect_worthwhile_without_simd_p (vinfo, code)))
         return false;
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "proceeding using word mode.\n");
     }
@@ -5623,7 +5625,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
       && !VECTOR_MODE_P (TYPE_MODE (vectype))
       && !vect_worthwhile_without_simd_p (vinfo, code))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "not worthwhile without SIMD support.\n");
       return false;
@@ -5639,7 +5641,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform binary/unary operation.\n");
 
@@ -5661,7 +5663,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
               optab_op2_mode = insn_data[icode].operand[2].mode;
               if (!VECTOR_MODE_P (optab_op2_mode))
                 {
-                  if (dump_enabled_p ())
+                  IF_VECT_DUMP
                     dump_printf_loc (MSG_NOTE, vect_location,
                                      "operand 1 using scalar mode.\n");
                   vec_oprnd1 = op1;
@@ -5792,7 +5794,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   op_type = TREE_CODE_LENGTH (code);
   if (op_type != unary_op && op_type != binary_op && op_type != ternary_op)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "num. args = %d (not unary/binary/ternary op).\n",
                          op_type);
@@ -5811,7 +5813,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       && code != BIT_XOR_EXPR
       && code != BIT_AND_EXPR)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "bit-precision arithmetic not supported.\n");
       return false;
@@ -5820,7 +5822,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   op0 = gimple_assign_rhs1 (stmt);
   if (!vect_is_simple_use (op0, vinfo, &def_stmt, &dt[0], &vectype))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "use not simple.\n");
       return false;
@@ -5838,7 +5840,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 	{
 	  if (!VECT_SCALAR_BOOLEAN_TYPE_P (TREE_TYPE (scalar_dest)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "not supported operation on bool value.\n");
 	      return false;
@@ -5852,7 +5854,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
     gcc_assert (vectype);
   if (!vectype)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "no vectype for scalar type ");
@@ -5874,7 +5876,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       op1 = gimple_assign_rhs2 (stmt);
       if (!vect_is_simple_use (op1, vinfo, &def_stmt, &dt[1]))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "use not simple.\n");
 	  return false;
@@ -5885,7 +5887,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       op2 = gimple_assign_rhs3 (stmt);
       if (!vect_is_simple_use (op2, vinfo, &def_stmt, &dt[2]))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "use not simple.\n");
 	  return false;
@@ -5917,7 +5919,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       optab = optab_for_tree_code (code, vectype, optab_default);
       if (!optab)
 	{
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "no optab.\n");
 	  return false;
@@ -5928,14 +5930,14 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!target_support_p)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "op not supported by target.\n");
       /* Check only during analysis.  */
       if (maybe_ne (GET_MODE_SIZE (vec_mode), UNITS_PER_WORD)
 	  || (!vec_stmt && !vect_worthwhile_without_simd_p (vinfo, code)))
         return false;
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_NOTE, vect_location,
                          "proceeding using word mode.\n");
     }
@@ -5945,7 +5947,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
       && !vec_stmt
       && !vect_worthwhile_without_simd_p (vinfo, code))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "not worthwhile without SIMD support.\n");
       return false;
@@ -5961,7 +5963,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
 
   /* Transform.  */
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform binary/unary operation.\n");
 
@@ -6163,7 +6165,7 @@ get_group_alias_ptr_type (gimple *first_stmt)
       if (get_alias_set (DR_REF (first_dr))
 	  != get_alias_set (DR_REF (next_dr)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "conflicting alias set types.\n");
 	  return ptr_type_node;
@@ -6262,7 +6264,7 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (slp_node != NULL)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "SLP of masked stores not supported.\n");
 	  return false;
@@ -6308,7 +6310,7 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* FORNOW.  This restriction should be relaxed.  */
   if (loop && nested_in_vect_loop_p (loop, stmt) && ncopies > 1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "multiple types in nested loop.\n");
       return false;
@@ -6340,7 +6342,7 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (memory_access_type != VMAT_LOAD_STORE_LANES
 	       && (memory_access_type != VMAT_GATHER_SCATTER || gs_info.decl))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported access type for masked store.\n");
 	  return false;
@@ -6582,7 +6584,7 @@ vectorizable_store (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   else
     ref_type = reference_alias_ptr_type (DR_REF (first_dr));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform store. ncopies = %d\n", ncopies);
 
@@ -7434,7 +7436,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 
       if (slp_node != NULL)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "SLP of masked loads not supported.\n");
 	  return false;
@@ -7478,7 +7480,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   /* FORNOW. This restriction should be relaxed.  */
   if (nested_in_vect_loop && ncopies > 1)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "multiple types in nested loop.\n");
       return false;
@@ -7491,7 +7493,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       && maybe_gt (LOOP_VINFO_VECT_FACTOR (loop_vinfo),
 		   STMT_VINFO_MIN_NEG_DIST (stmt_info)))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "cannot perform implicit CSE when unrolling "
 			 "with negative dependence distance\n");
@@ -7505,7 +7507,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     (e.g. - data copies).  */
   if (optab_handler (mov_optab, mode) == CODE_FOR_nothing)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "Aligned load, but unsupported type.\n");
       return false;
@@ -7532,7 +7534,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	  && maybe_gt (LOOP_VINFO_VECT_FACTOR (loop_vinfo),
 		       STMT_VINFO_MIN_NEG_DIST (stmt_info)))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "cannot perform implicit CSE when performing "
 			     "group loads with negative dependence distance\n");
@@ -7547,7 +7549,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	      != STMT_SLP_TYPE (vinfo_for_stmt
 				 (DR_GROUP_SAME_DR_STMT (stmt_info)))))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "conflicting SLP types for CSEd load\n");
 	  return false;
@@ -7578,7 +7580,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 	    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
 	  if (TREE_CODE (masktype) == INTEGER_TYPE)
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				 "masked gather with integer mask not"
 				 " supported.");
@@ -7588,7 +7590,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
       else if (memory_access_type != VMAT_LOAD_STORE_LANES
 	       && memory_access_type != VMAT_GATHER_SCATTER)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "unsupported access type for masked load.\n");
 	  return false;
@@ -7615,7 +7617,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
     gcc_assert (memory_access_type
 		== STMT_VINFO_MEMORY_ACCESS_TYPE (stmt_info));
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "transform load. ncopies = %d\n", ncopies);
 
@@ -8452,7 +8454,7 @@ vectorizable_load (gimple *stmt, gimple_stmt_iterator *gsi, gimple **vec_stmt,
 		      && !nested_in_vect_loop
 		      && hoist_defs_of_uses (stmt, loop))
 		    {
-		      if (dump_enabled_p ())
+		      IF_VECT_DUMP
 			{
 			  dump_printf_loc (MSG_NOTE, vect_location,
 					   "hoisting out of the vectorized "
@@ -8714,7 +8716,7 @@ vectorizable_condition (gimple *stmt, gimple_stmt_iterator *gsi,
       /* FORNOW: not yet supported.  */
       if (STMT_VINFO_LIVE_P (stmt_info))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "value used after loop.\n");
 	  return false;
@@ -9116,7 +9118,7 @@ vectorizable_comparison (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (STMT_VINFO_LIVE_P (stmt_info))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "value used after loop.\n");
       return false;
@@ -9378,7 +9380,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
   gimple *pattern_stmt;
   gimple_seq pattern_def_seq;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "==> examining statement: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
@@ -9386,7 +9388,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 
   if (gimple_has_volatile_ops (stmt))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "not vectorized: stmt has volatile operands\n");
 
@@ -9419,7 +9421,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
           /* Analyze PATTERN_STMT instead of the original stmt.  */
           stmt = pattern_stmt;
           stmt_info = vinfo_for_stmt (pattern_stmt);
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             {
               dump_printf_loc (MSG_NOTE, vect_location,
                                "==> examining pattern statement: ");
@@ -9428,7 +9430,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
         }
       else
         {
-          if (dump_enabled_p ())
+          IF_VECT_DUMP
             dump_printf_loc (MSG_NOTE, vect_location, "irrelevant.\n");
 
           return true;
@@ -9441,7 +9443,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
                || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_stmt))))
     {
       /* Analyze PATTERN_STMT too.  */
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_NOTE, vect_location,
                            "==> examining pattern statement: ");
@@ -9466,7 +9468,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 	      || STMT_VINFO_LIVE_P (vinfo_for_stmt (pattern_def_stmt)))
 	    {
 	      /* Analyze def stmt of STMT if it's a pattern stmt.  */
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_NOTE, vect_location,
                                    "==> examining pattern def statement: ");
@@ -9560,7 +9562,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
 
   if (!ok)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "not vectorized: relevant stmt not ");
@@ -9577,7 +9579,7 @@ vect_analyze_stmt (gimple *stmt, bool *need_to_vectorize, slp_tree node,
       && STMT_VINFO_TYPE (stmt_info) != reduc_vec_info_type
       && !can_vectorize_live_stmts (stmt, NULL, node, NULL, cost_vec))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         {
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                            "not vectorized: live stmt not supported: ");
@@ -9696,7 +9698,7 @@ vect_transform_stmt (gimple *stmt, gimple_stmt_iterator *gsi,
     default:
       if (!STMT_VINFO_LIVE_P (stmt_info))
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                              "stmt not supported.\n");
 	  gcc_unreachable ();
@@ -9725,7 +9727,7 @@ vect_transform_stmt (gimple *stmt, gimple_stmt_iterator *gsi,
       tree scalar_dest;
       gimple *exit_phi;
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_NOTE, vect_location,
                          "Record the vdef for outer-loop vectorization.\n");
 
@@ -10062,7 +10064,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
   *def_stmt = NULL;
   *dt = vect_unknown_def_type;
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location,
                        "vect_is_simple_use: operand ");
@@ -10084,7 +10086,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
 
   if (TREE_CODE (operand) != SSA_NAME)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			 "not ssa-name.\n");
       return false;
@@ -10097,7 +10099,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
     }
 
   *def_stmt = SSA_NAME_DEF_STMT (operand);
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "def_stmt: ");
       dump_gimple_stmt (MSG_NOTE, TDF_SLIM, *def_stmt, 0);
@@ -10111,7 +10113,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
       *dt = STMT_VINFO_DEF_TYPE (stmt_vinfo);
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "type of def: ");
       switch (*dt)
@@ -10148,7 +10150,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
 
   if (*dt == vect_unknown_def_type)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "Unsupported pattern.\n");
       return false;
@@ -10161,7 +10163,7 @@ vect_is_simple_use (tree operand, vec_info *vinfo,
     case GIMPLE_CALL:
       break;
     default:
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                          "unsupported defining stmt:\n");
       return false;
@@ -10682,13 +10684,13 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 	     #pragma omp simd functions, and what vectorization factor
 	     it really needs can't be determined until
 	     vectorizable_simd_clone_call.  */
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_NOTE, vect_location,
 			     "defer to SIMD clone analysis.\n");
 	  return true;
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: irregular stmt.");
@@ -10699,7 +10701,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 
   if (VECTOR_MODE_P (TYPE_MODE (gimple_expr_type (stmt))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: vector stmt in loop:");
@@ -10734,14 +10736,14 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 	    scalar_type = TREE_TYPE (rhs1);
 	  else
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		dump_printf_loc (MSG_NOTE, vect_location,
 				 "pure bool operation.\n");
 	      return true;
 	    }
 	}
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "get vectype for scalar type:  ");
@@ -10751,7 +10753,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
       vectype = get_vectype_for_scalar_type (scalar_type);
       if (!vectype)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    {
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			       "not vectorized: unsupported data-type ");
@@ -10765,7 +10767,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
       if (!*stmt_vectype_out)
 	*stmt_vectype_out = vectype;
 
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
 	  dump_generic_expr (MSG_NOTE, TDF_SLIM, vectype);
@@ -10788,7 +10790,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
 	  HOST_WIDE_INT dummy;
 	  scalar_type = vect_get_smallest_scalar_type (stmt, &dummy, &dummy);
 	}
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
 			   "get vectype for scalar type:  ");
@@ -10799,7 +10801,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
     }
   if (!nunits_vectype)
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: unsupported data-type ");
@@ -10812,7 +10814,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
   if (maybe_ne (GET_MODE_SIZE (TYPE_MODE (vectype)),
 		GET_MODE_SIZE (TYPE_MODE (nunits_vectype))))
     {
-      if (dump_enabled_p ())
+      IF_VECT_DUMP
 	{
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			   "not vectorized: different sized vector "
@@ -10825,7 +10827,7 @@ vect_get_vector_types_for_stmt (stmt_vec_info stmt_info,
       return false;
     }
 
-  if (dump_enabled_p ())
+  IF_VECT_DUMP
     {
       dump_printf_loc (MSG_NOTE, vect_location, "vectype: ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, nunits_vectype);
@@ -10860,7 +10862,7 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 
       if (!mask_type)
 	{
-	  if (dump_enabled_p ())
+	  IF_VECT_DUMP
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 			     "not vectorized: unsupported mask\n");
 	  return NULL_TREE;
@@ -10878,7 +10880,7 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 	  if (!vect_is_simple_use (rhs, stmt_info->vinfo,
 				   &def_stmt, &dt, &vectype))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "not vectorized: can't compute mask type "
@@ -10900,7 +10902,7 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 	  else if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_type),
 			     TYPE_VECTOR_SUBPARTS (vectype)))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "not vectorized: different sized masks "
@@ -10917,7 +10919,7 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 	  else if (VECTOR_BOOLEAN_TYPE_P (mask_type)
 		   != VECTOR_BOOLEAN_TYPE_P (vectype))
 	    {
-	      if (dump_enabled_p ())
+	      IF_VECT_DUMP
 		{
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
 				   "not vectorized: mixed mask and "
@@ -10944,12 +10946,13 @@ vect_get_mask_type_for_stmt (stmt_vec_info stmt_info)
 
   /* No mask_type should mean loop invariant predicate.
      This is probably a subject for optimization in if-conversion.  */
-  if (!mask_type && dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-		       "not vectorized: can't compute mask type "
-		       "for statement, ");
-      dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
-    }
+  if (!mask_type)
+    IF_VECT_DUMP
+      {
+	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+			 "not vectorized: can't compute mask type "
+			 "for statement, ");
+	dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+      }
   return mask_type;
 }
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 4ebad5c..1ec318e 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -749,11 +749,12 @@ vectorize_loops (void)
 	loop_dist_alias_call = vect_loop_dist_alias_call (loop);
        vectorize_epilogue:
 	vect_location = find_loop_location (loop);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
-	    && dump_enabled_p ())
-	  dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
-                       LOCATION_FILE (vect_location),
-		       LOCATION_LINE (vect_location));
+	VECT_SCOPE ("analyzing loop");
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION)
+	  IF_VECT_DUMP
+	    dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
+			 LOCATION_FILE (vect_location.get_location_t ()),
+			 LOCATION_LINE (vect_location.get_location_t ()));
 
 	loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo);
 	loop->aux = loop_vinfo;
@@ -827,10 +828,10 @@ vectorize_loops (void)
 
 	if (loop_vectorized_call)
 	  set_uid_loop_bbs (loop_vinfo, loop_vectorized_call);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
-	    && dump_enabled_p ())
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-                           "loop vectorized\n");
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION)
+	  IF_VECT_DUMP
+	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
+			     "loop vectorized\n");
 	new_loop = vect_transform_loop (loop_vinfo);
 	num_vectorized_loops++;
 	/* Now that the loop has been vectorized, allow it to be unrolled
@@ -875,8 +876,7 @@ vectorize_loops (void)
   vect_location = optinfo_location ();
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
-  if (dump_enabled_p ()
-      || (num_vectorized_loops > 0 && dump_enabled_p ()))
+  IF_VECT_DUMP
     dump_printf_loc (MSG_NOTE, vect_location,
                      "vectorized %u loops in function.\n",
                      num_vectorized_loops);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 7/8] tree-ssa-loop-im.c port from fprintf to the dump API
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
                                 ` (3 preceding siblings ...)
  2018-06-14 19:50               ` [PATCH 6/8] ipa-inline.c/tree-inline.c: port from fprintf to dump API David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-14 19:50               ` [PATCH 8/8] Add lots of pointless churn to tree-vect-*.c David Malcolm
                                 ` (2 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

More porting from fprintf to using the dump API, so that
the messages appear in -fopt-info etc.

Note how these messages aren't consolidated into one
optimization record by the v2 patch kit.

gcc/ChangeLog:
	* tree-ssa-loop-im.c (move_computations_worker): Port from
	fprintf to the dump API.
---
 gcc/tree-ssa-loop-im.c | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
index 01a954e..b701171 100644
--- a/gcc/tree-ssa-loop-im.c
+++ b/gcc/tree-ssa-loop-im.c
@@ -1117,12 +1117,16 @@ move_computations_worker (basic_block bb)
 	  continue;
 	}
 
-      if (dump_file && (dump_flags & TDF_DETAILS))
+      if (dump_enabled_p ())
 	{
-	  fprintf (dump_file, "Moving PHI node\n");
-	  print_gimple_stmt (dump_file, stmt, 0);
-	  fprintf (dump_file, "(cost %u) out of loop %d.\n\n",
-		   cost, level->num);
+	  /* FIXME: consolidation etc.  */
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, stmt,
+			   "Moving PHI node\n");
+	  dump_gimple_stmt (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, TDF_SLIM,
+			    stmt, 0);
+	  dump_printf (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS,
+		       " (cost %u) out of loop %d.\n\n",
+		       cost, level->num);
 	}
 
       if (gimple_phi_num_args (stmt) == 1)
@@ -1186,12 +1190,14 @@ move_computations_worker (basic_block bb)
       if (gimple_code (stmt) == GIMPLE_COND)
 	continue;
 
-      if (dump_file && (dump_flags & TDF_DETAILS))
+      if (dump_enabled_p ())
 	{
-	  fprintf (dump_file, "Moving statement\n");
-	  print_gimple_stmt (dump_file, stmt, 0);
-	  fprintf (dump_file, "(cost %u) out of loop %d.\n\n",
-		   cost, level->num);
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, stmt,
+			   "Moving statement\n");
+	  dump_gimple_stmt (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS, TDF_SLIM, stmt, 0);
+	  dump_printf (MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS,
+		       "(cost %u) out of loop %d.\n\n",
+		       cost, level->num);
 	}
 
       e = loop_preheader_edge (level);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 5/8] gimple-loop-interchange.cc: use the dump API in a few places
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
  2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-14 19:50               ` [PATCH 4/8] tree-vect-loop.c: use MSG_OPTIMIZED_LOCATIONS " David Malcolm
                                 ` (5 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

Doing so makes this information appear in -fopt-info, remarks,
and optimization records, rather than just in the dump_file.

gcc/ChangeLog:
	* gimple-loop-interchange.cc (tree_loop_interchange::interchange):
	Convert fprintf calls to dump_printf calls.
	(prepare_perfect_loop_nest): Likewise.
---
 gcc/gimple-loop-interchange.cc | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index ef8df28..b40ee4b 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -1627,11 +1627,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    (unsigned) stmt_cost,
 				    iloop.m_loop->inner == NULL))
 	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file,
-		     "Loop_pair<outer:%d, inner:%d> is interchanged\n\n",
-		     oloop.m_loop->num, iloop.m_loop->num);
-
+	  if (dump_enabled_p ())
+	    dump_printf (MSG_OPTIMIZED_LOCATIONS |TDF_DETAILS,
+			 "Loop_pair<outer:%d, inner:%d> is interchanged\n\n",
+			 oloop.m_loop->num, iloop.m_loop->num);
 	  changed_p = true;
 	  interchange_loops (iloop, oloop);
 	  /* No need to update if there is no further loop interchange.  */
@@ -1640,10 +1639,10 @@ tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 	}
       else
 	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file,
-		     "Loop_pair<outer:%d, inner:%d> is not interchanged\n\n",
-		     oloop.m_loop->num, iloop.m_loop->num);
+	  if (dump_enabled_p ())
+	    dump_printf (MSG_MISSED_OPTIMIZATION |TDF_DETAILS,
+			 "Loop_pair<outer:%d, inner:%d> is not interchanged\n\n",
+			 oloop.m_loop->num, iloop.m_loop->num);
 	}
     }
   simple_dce_from_worklist (m_dce_seeds);
@@ -2028,10 +2027,10 @@ prepare_perfect_loop_nest (struct loop *loop, vec<loop_p> *loop_nest,
     if (find_loop_nest (start_loop, loop_nest)
 	&& tree_loop_interchange_compute_ddrs (*loop_nest, *datarefs, ddrs))
       {
-	if (dump_file && (dump_flags & TDF_DETAILS))
-	  fprintf (dump_file,
-		   "\nConsider loop interchange for loop_nest<%d - %d>\n",
-		   start_loop->num, innermost->num);
+	if (dump_enabled_p ())
+	  dump_printf (MSG_NOTE |TDF_DETAILS,
+		       "\nConsider loop interchange for loop_nest<%d - %d>\n",
+		       start_loop->num, innermost->num);
 
 	if (loop != start_loop)
 	  prune_access_strides_not_in_loop (start_loop, innermost, *datarefs);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 2/8] Introduce VECT_SCOPE macro
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
                                 ` (5 preceding siblings ...)
  2018-06-14 19:50               ` [PATCH 8/8] Add lots of pointless churn to tree-vect-*.c David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-15 20:11                 ` Jeff Law
  2018-06-14 19:50               ` [PATCH 3/8] v2 of optinfo, remarks and optimization records David Malcolm
  7 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

The vectorizer code has numerous instances of:

  if (dump_enabled_p ())
    dump_printf_loc (MSG_NOTE, vect_location,
                     "=== some message ===\n");

In each case, the dump_printf_loc is a MSG_NODE at vect_location.

In almost all cases the message is of the form
  "=== foo ===\n"

The exceptions are:
  "===== analyze_loop_nest =====\n"
which uses 4 equal signs rather than 3, and
  "===vect_slp_analyze_bb===\n"
which is missing the spaces.

In most cases (but not always) the message matches the function name.

This patch replaces all of these with a macro, taking the message
as an argument (and forcing the use of three dashes and a space).

The idea is to later convert this macro to use an RAII type
that pushes and pops scope, so that the nesting structure appears
in the dumpfile and -fopt-info logs (and in the remarks and
optimization records introduced later in this patch kit).

The message is usually the function name, but not always.
Should I split this out into two macros? e.g. a VECT_FUNCTION_SCOPE
that uses __FUNCTION__?

Would DUMP_VECT_SCOPE be a better name, perhaps?

gcc/ChangeLog:
	* tree-vect-data-refs.c (vect_analyze_data_ref_dependences):
	Replace dump_printf_loc call with VECT_SCOPE.
	(vect_slp_analyze_instance_dependence): Likewise.
	(vect_enhance_data_refs_alignment): Likewise.
	(vect_analyze_data_refs_alignment): Likewise.
	(vect_slp_analyze_and_verify_instance_alignment
	(vect_analyze_data_ref_accesses): Likewise.
	(vect_prune_runtime_alias_test_list): Likewise.
	(vect_analyze_data_refs): Likewise.
	* tree-vect-loop-manip.c (vect_update_inits_of_drs): Likewise.
	* tree-vect-loop.c (vect_determine_vectorization_factor): Likewise.
	(vect_analyze_scalar_cycles_1): Likewise.
	(vect_get_loop_niters): Likewise.
	(vect_analyze_loop_form_1): Likewise.
	(vect_update_vf_for_slp): Likewise.
	(vect_analyze_loop_operations): Likewise.
	(vect_analyze_loop): Likewise.
	(vectorizable_induction): Likewise.
	(vect_transform_loop): Likewise.
	* tree-vect-patterns.c (vect_pattern_recog): Likewise.
	* tree-vect-slp.c (vect_analyze_slp): Likewise.
	(vect_make_slp_decision): Likewise.
	(vect_detect_hybrid_slp): Likewise.
	(vect_slp_analyze_operations): Likewise.
	(vect_slp_bb): Likewise.
	* tree-vect-stmts.c (vect_mark_stmts_to_be_vectorized): Likewise.
	(vectorizable_bswap): Likewise.
	(vectorizable_call): Likewise.
	(vectorizable_simd_clone_call): Likewise.
	(vectorizable_conversion): Likewise.
	(vectorizable_assignment): Likewise.
	(vectorizable_shift): Likewise.
	(vectorizable_operation): Likewise.
	* tree-vectorizer.h (VECT_SCOPE): New macro.
---
 gcc/tree-vect-data-refs.c  | 32 ++++++++------------------------
 gcc/tree-vect-loop-manip.c |  4 +---
 gcc/tree-vect-loop.c       | 39 ++++++++++-----------------------------
 gcc/tree-vect-patterns.c   |  4 +---
 gcc/tree-vect-slp.c        | 18 +++++-------------
 gcc/tree-vect-stmts.c      | 32 ++++++++------------------------
 gcc/tree-vectorizer.h      | 10 ++++++++++
 7 files changed, 43 insertions(+), 96 deletions(-)

diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 3eb67c9..fbc37d9 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -562,9 +562,7 @@ vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo,
   unsigned int i;
   struct data_dependence_relation *ddr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_ref_dependences ===\n");
+  VECT_SCOPE ("vect_analyze_data_ref_dependences");
 
   LOOP_VINFO_DDRS (loop_vinfo)
     .create (LOOP_VINFO_DATAREFS (loop_vinfo).length ()
@@ -741,9 +739,7 @@ vect_slp_analyze_node_dependences (slp_instance instance, slp_tree node,
 bool
 vect_slp_analyze_instance_dependence (slp_instance instance)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_slp_analyze_instance_dependence ===\n");
+  VECT_SCOPE ("vect_slp_analyze_instance_dependence");
 
   /* The stores of this instance are at the root of the SLP tree.  */
   slp_tree store = SLP_INSTANCE_TREE (instance);
@@ -1685,9 +1681,7 @@ vect_enhance_data_refs_alignment (loop_vec_info loop_vinfo)
   unsigned int mis, same_align_drs_max = 0;
   hash_table<peel_info_hasher> peeling_htab (1);
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_enhance_data_refs_alignment ===\n");
+  VECT_SCOPE ("vect_enhance_data_refs_alignment");
 
   /* Reset data so we can safely be called multiple times.  */
   LOOP_VINFO_MAY_MISALIGN_STMTS (loop_vinfo).truncate (0);
@@ -2345,9 +2339,7 @@ vect_find_same_alignment_drs (struct data_dependence_relation *ddr)
 bool
 vect_analyze_data_refs_alignment (loop_vec_info vinfo)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_refs_alignment ===\n");
+  VECT_SCOPE ("vect_analyze_data_refs_alignment");
 
   /* Mark groups of data references with same alignment using
      data dependence information.  */
@@ -2426,9 +2418,7 @@ vect_slp_analyze_and_verify_node_alignment (slp_tree node)
 bool
 vect_slp_analyze_and_verify_instance_alignment (slp_instance instance)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_slp_analyze_and_verify_instance_alignment ===\n");
+  VECT_SCOPE ("vect_slp_analyze_and_verify_instance_alignment");
 
   slp_tree node;
   unsigned i;
@@ -2931,9 +2921,7 @@ vect_analyze_data_ref_accesses (vec_info *vinfo)
   vec<data_reference_p> datarefs = vinfo->datarefs;
   struct data_reference *dr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_data_ref_accesses ===\n");
+  VECT_SCOPE ("vect_analyze_data_ref_accesses");
 
   if (datarefs.is_empty ())
     return true;
@@ -3379,9 +3367,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
   unsigned int i;
   tree length_factor;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_prune_runtime_alias_test_list ===\n");
+  VECT_SCOPE ("vect_prune_runtime_alias_test_list");
 
   /* Step values are irrelevant for aliasing if the number of vector
      iterations is equal to the number of scalar iterations (which can
@@ -4075,9 +4061,7 @@ vect_analyze_data_refs (vec_info *vinfo, poly_uint64 *min_vf)
   struct data_reference *dr;
   tree scalar_type;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_data_refs ===\n");
+  VECT_SCOPE ("vect_analyze_data_refs");
 
   if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
     loop = LOOP_VINFO_LOOP (loop_vinfo);
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index 7443e7f..326c8dd 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -1733,9 +1733,7 @@ vect_update_inits_of_drs (loop_vec_info loop_vinfo, tree niters,
   vec<data_reference_p> datarefs = LOOP_VINFO_DATAREFS (loop_vinfo);
   struct data_reference *dr;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_update_inits_of_dr ===\n");
+  VECT_SCOPE ("vect_update_inits_of_dr");
 
   /* Adjust niters to sizetype and insert stmts on loop preheader edge.  */
   if (!types_compatible_p (sizetype, TREE_TYPE (niters)))
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 385d62f..77bd909 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -296,9 +296,7 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
   unsigned i;
   auto_vec<stmt_vec_info> mask_producers;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_determine_vectorization_factor ===\n");
+  VECT_SCOPE ("vect_determine_vectorization_factor");
 
   for (i = 0; i < nbbs; i++)
     {
@@ -479,9 +477,7 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, struct loop *loop)
   gphi_iterator gsi;
   bool double_reduc;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_analyze_scalar_cycles ===\n");
+  VECT_SCOPE ("vect_analyze_scalar_cycles");
 
   /* First - identify all inductions.  Reduction detection assumes that all the
      inductions have been identified, therefore, this order must not be
@@ -727,9 +723,7 @@ vect_get_loop_niters (struct loop *loop, tree *assumptions,
   *assumptions = boolean_true_node;
   *number_of_iterationsm1 = chrec_dont_know;
   *number_of_iterations = chrec_dont_know;
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== get_loop_niters ===\n");
+  VECT_SCOPE ("get_loop_niters");
 
   if (!exit)
     return cond;
@@ -1170,9 +1164,7 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
 			  tree *assumptions, tree *number_of_iterationsm1,
 			  tree *number_of_iterations, gcond **inner_loop_cond)
 {
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_loop_form ===\n");
+  VECT_SCOPE ("vect_analyze_loop_form");
 
   /* Different restrictions apply when we are considering an inner-most loop,
      vs. an outer (nested) loop.
@@ -1422,9 +1414,7 @@ vect_update_vf_for_slp (loop_vec_info loop_vinfo)
   poly_uint64 vectorization_factor;
   int i;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_update_vf_for_slp ===\n");
+  VECT_SCOPE ("vect_update_vf_for_slp");
 
   vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   gcc_assert (known_ne (vectorization_factor, 0U));
@@ -1527,9 +1517,7 @@ vect_analyze_loop_operations (loop_vec_info loop_vinfo)
   bool need_to_vectorize = false;
   bool ok;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_analyze_loop_operations ===\n");
+  VECT_SCOPE ("vect_analyze_loop_operations");
 
   stmt_vector_for_cost cost_vec;
   cost_vec.create (2);
@@ -2308,9 +2296,7 @@ vect_analyze_loop (struct loop *loop, loop_vec_info orig_loop_vinfo)
   targetm.vectorize.autovectorize_vector_sizes (&vector_sizes);
   unsigned int next_size = 0;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "===== analyze_loop_nest =====\n");
+  VECT_SCOPE ("analyze_loop_nest");
 
   if (loop_outer (loop)
       && loop_vec_info_for_loop (loop_outer (loop))
@@ -7460,9 +7446,7 @@ vectorizable_induction (gimple *phi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = induc_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_induction ===\n");
+      VECT_SCOPE ("vectorizable_induction");
       vect_model_induction_cost (stmt_info, ncopies, cost_vec);
       return true;
     }
@@ -8335,8 +8319,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
   bool check_profitability = false;
   unsigned int th;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vec_transform_loop ===\n");
+  VECT_SCOPE ("vec_transform_loop");
 
   /* Use the more conservative vectorization threshold.  If the number
      of iterations is constant assume the cost check has been performed
@@ -8618,9 +8601,7 @@ vect_transform_loop (loop_vec_info loop_vinfo)
 		{
 		  slp_scheduled = true;
 
-		  if (dump_enabled_p ())
-		    dump_printf_loc (MSG_NOTE, vect_location,
-				     "=== scheduling SLP instances ===\n");
+		  VECT_SCOPE ("scheduling SLP instances");
 
 		  vect_schedule_slp (loop_vinfo);
 		}
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c
index 74f08cf..23f1ab8 100644
--- a/gcc/tree-vect-patterns.c
+++ b/gcc/tree-vect-patterns.c
@@ -4669,9 +4669,7 @@ vect_pattern_recog (vec_info *vinfo)
   auto_vec<gimple *, 1> stmts_to_replace;
   gimple *stmt;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_pattern_recog ===\n");
+  VECT_SCOPE ("vect_pattern_recog");
 
   if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
     {
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 0a96a93..ab703da 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -2177,8 +2177,7 @@ vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)
   unsigned int i;
   gimple *first_element;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_analyze_slp ===\n");
+  VECT_SCOPE ("vect_analyze_slp");
 
   /* Find SLP sequences starting from groups of grouped stores.  */
   FOR_EACH_VEC_ELT (vinfo->grouped_stores, i, first_element)
@@ -2231,9 +2230,7 @@ vect_make_slp_decision (loop_vec_info loop_vinfo)
   slp_instance instance;
   int decided_to_slp = 0;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_make_slp_decision ==="
-                     "\n");
+  VECT_SCOPE ("vect_make_slp_decision");
 
   FOR_EACH_VEC_ELT (slp_instances, i, instance)
     {
@@ -2399,9 +2396,7 @@ vect_detect_hybrid_slp (loop_vec_info loop_vinfo)
   vec<slp_instance> slp_instances = LOOP_VINFO_SLP_INSTANCES (loop_vinfo);
   slp_instance instance;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "=== vect_detect_hybrid_slp ==="
-                     "\n");
+  VECT_SCOPE ("vect_detect_hybrid_slp");
 
   /* First walk all pattern stmt in the loop and mark defs of uses as
      hybrid because immediate uses in them are not recorded.  */
@@ -2622,9 +2617,7 @@ vect_slp_analyze_operations (vec_info *vinfo)
   slp_instance instance;
   int i;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-		     "=== vect_slp_analyze_operations ===\n");
+  VECT_SCOPE ("vect_slp_analyze_operations");
 
   scalar_stmts_to_slp_tree_map_t *visited
     = new scalar_stmts_to_slp_tree_map_t ();
@@ -2981,8 +2974,7 @@ vect_slp_bb (basic_block bb)
   bool any_vectorized = false;
   auto_vector_sizes vector_sizes;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location, "===vect_slp_analyze_bb===\n");
+  VECT_SCOPE ("vect_slp_analyze_bb");
 
   /* Autodetect first vector size we try.  */
   current_vector_size = 0;
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index 9f365e3..d683665 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -640,9 +640,7 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info loop_vinfo)
   bool live_p;
   enum vect_relevant relevant;
 
-  if (dump_enabled_p ())
-    dump_printf_loc (MSG_NOTE, vect_location,
-                     "=== vect_mark_stmts_to_be_vectorized ===\n");
+  VECT_SCOPE ("vect_mark_stmts_to_be_vectorized");
 
   auto_vec<gimple *, 64> worklist;
 
@@ -3027,9 +3025,7 @@ vectorizable_bswap (gimple *stmt, gimple_stmt_iterator *gsi,
   if (! vec_stmt)
     {
       STMT_VINFO_TYPE (stmt_info) = call_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location, "=== vectorizable_bswap ==="
-                         "\n");
+      VECT_SCOPE ("vectorizable_bswap");
       if (! slp_node)
 	{
 	  record_stmt_cost (cost_vec,
@@ -3346,9 +3342,7 @@ vectorizable_call (gimple *gs, gimple_stmt_iterator *gsi, gimple **vec_stmt,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = call_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location, "=== vectorizable_call ==="
-                         "\n");
+      VECT_SCOPE ("vectorizable_call");
       vect_model_simple_cost (stmt_info, ncopies, dt, ndts, slp_node, cost_vec);
       if (ifn != IFN_LAST && modifier == NARROW && !slp_node)
 	record_stmt_cost (cost_vec, ncopies / 2,
@@ -4023,9 +4017,7 @@ vectorizable_simd_clone_call (gimple *stmt, gimple_stmt_iterator *gsi,
 	    STMT_VINFO_SIMD_CLONE_INFO (stmt_info).safe_push (sll);
 	  }
       STMT_VINFO_TYPE (stmt_info) = call_simd_clone_vec_info_type;
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-			 "=== vectorizable_simd_clone_call ===\n");
+      VECT_SCOPE ("vectorizable_simd_clone_call");
 /*      vect_model_simple_cost (stmt_info, ncopies, dt, slp_node, cost_vec); */
       return true;
     }
@@ -4865,9 +4857,7 @@ vectorizable_conversion (gimple *stmt, gimple_stmt_iterator *gsi,
 
   if (!vec_stmt)		/* transformation not required.  */
     {
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_conversion ===\n");
+      VECT_SCOPE ("vectorizable_conversion");
       if (code == FIX_TRUNC_EXPR || code == FLOAT_EXPR)
         {
 	  STMT_VINFO_TYPE (stmt_info) = type_conversion_vec_info_type;
@@ -5279,9 +5269,7 @@ vectorizable_assignment (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = assignment_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_assignment ===\n");
+      VECT_SCOPE ("vectorizable_assignment");
       vect_model_simple_cost (stmt_info, ncopies, dt, ndts, slp_node, cost_vec);
       return true;
     }
@@ -5644,9 +5632,7 @@ vectorizable_shift (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = shift_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_shift ===\n");
+      VECT_SCOPE ("vectorizable_shift");
       vect_model_simple_cost (stmt_info, ncopies, dt, ndts, slp_node, cost_vec);
       return true;
     }
@@ -5968,9 +5954,7 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi,
   if (!vec_stmt) /* transformation not required.  */
     {
       STMT_VINFO_TYPE (stmt_info) = op_vec_info_type;
-      if (dump_enabled_p ())
-        dump_printf_loc (MSG_NOTE, vect_location,
-                         "=== vectorizable_operation ===\n");
+      VECT_SCOPE ("vectorizable_operation");
       vect_model_simple_cost (stmt_info, ncopies, dt, ndts, slp_node, cost_vec);
       return true;
     }
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index f4b4dec..2255d96 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1425,6 +1425,16 @@ vect_get_scalar_dr_size (struct data_reference *dr)
 /* Source location */
 extern source_location vect_location;
 
+/* If dumping is enabled, emit a MSG_NOTE at vect_location about
+   entering MSG within the vectorizer.  MSG should be a string literal. */
+
+#define VECT_SCOPE(MSG) \
+  do {						\
+    if (dump_enabled_p ())			\
+      dump_printf_loc (MSG_NOTE, vect_location, \
+		       "=== " MSG " ===\n");	\
+  } while (0)
+
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
 /*-----------------------------------------------------------------*/
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 3/8] v2 of optinfo, remarks and optimization records
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
                                 ` (6 preceding siblings ...)
  2018-06-14 19:50               ` [PATCH 2/8] Introduce VECT_SCOPE macro David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

Changed in v2:

* The patch now reuses the existing dump API (the "dump_*" functions
  in dumpfile.h), rather than inventing its own.

* I've eliminated the streaming "operator<<" API; instead the
  optimization records and remarks are constructed from "dump_*" calls.

* This patch gets rid of the optinfo-emit-fopt-info.h/cc from v1;
  rather that the v1 approach of building optinfo instances for each of
  (a) -fopt-info
  (b) JSON optimization records
  (c) remarks
  and then emitting for each of them, in the v2 approach, (a) is
  handled by the existing API, and optinfo instances are only created
  for (b) and (c)

* The existing "dump_*_loc" functions now take an optinfo_location
  rather than a location_t (there are a few places using dump_printf_loc
  with a location_t that I haven't ported yet; these are identified
  in the patch).

* I added a "dump_symtab_node" function, to experiment with capturing
  the symtab nodes in the optimization records (rather than just as
  text).

* The patch introduces a "class optinfo_guard" for consolidating
  dump calls:

    if (optinfo_guard guard = optinfo_guard (location, OPTINFO_IMPL_LOCATION))
      {
        SUITE
      }

  so that all of the dump_* calls within SUITE are consolidated
  into a single optimization record.

  This works, but I don't like the approach, as it means e.g.
  converting all of the:

    if (dump_enabled_p ())

  in the vectorization code to:

    IF_VECT_DUMP

  and will require hand-coded guards elsewhere.

  But I thought it was worth capturing my thinking here.

  In v3 of the patch kit I plan to eliminate this, in favor of the
  dump_*_loc calls introducing a new optimization record, and
  various other calls signifying the end of an optimization
  record (e.g. the emission of a diagnostic, or the closing of
  an optinfo_scope).

* Updates to the JSON format:

  * Rather than just an array of optimization records, the format now
    has a top-level tuple, beginning with a version ID, then a metadata
    object, then a tree-like record of all passes, then the optimization
    records.  The optimization records reference the passes in the pass
    tree, rather than each having a copy of the pass metadata.

  * Capturing IR within the JSON format: the v2 patch adds a new "state"
    record within the optimization records, capturing the state of each
    function after each pass has run.

Would XML be preferable to JSON? (my main reason for thinking of XML
would be to support a schema, but apparently there are JSON schemas these
days, and JSON overall is a lot simpler)

Other changes:

  * rework dump_enabled_p to handle the case where dumpfiles are
    disabled, but remarks or optimization records are enabled.  To do so,
    I changed it to a read of a "bool" global, updated whenever dump_file
    or alt_dump_file are changed (and hiding alt_dump_file; it's only
    needed within dumpfile.c).  I also added a GCC_UNLIKELY, in the hope
    of boosting performance for a non-PGO build of gcc.

  * optinfo_location: I eliminated the constructor taking a "loop *" in
    favor of retaining explicit calls to find_loop_location, and having
    that return an optinfo_location (since this seems more "heavyweight"
    than the other ctors).  I also eliminated the constructor taking just
    a basic_block pointer (to avoid having to guess a good location_t).

  * optinfo_item_text gained a trim_trailing_whitespace method to avoid
    stray newlines when emitting remarks

As before, this adds indentation to show nesting for -fopt-info:

This converts e.g. from:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note: === analyze_loop_nest ===
test.c:8:3: note: === vect_analyze_loop_form ===
test.c:8:3: note: === get_loop_niters ===
test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

to:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note:  === analyze_loop_nest ===
test.c:8:3: note:   === vect_analyze_loop_form ===
test.c:8:3: note:    === get_loop_niters ===
test.c:8:3: note:   symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note:   not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

showing the nesting of the optimization analysis and where each
message is being emitted from within it (this is of more use as
the number of messages and the nesting depth increases)

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
	optinfo-emit-json.o.
	(CFLAGS-optinfo-emit-json.o): Add -DTARGET_NAME as per toplev.o.
	(GTFILES): Add $(srcdir)/dumpfile.h.
	* cgraph.c (symbol_table::initialize): Replace assignment to
	"dump_file" with call to set_dump_file.
	(cgraph_node::get_body): Likewise.
	* cgraphunit.c (walk_polymorphic_call_targets): Update call to
	dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the gimple stmt.
	* common.opt (fremarks): New option.
	(fsave-optimization-record): New option.
	* coretypes.h (class symtab_node): New forward declaration.
	(struct cgraph_node): Likewise.
	(class varpool_node): Likewise.
	(struct kv_pair): Move here from dumpfile.c.
	* coverage.c (get_coverage_counts): Update call to
	dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the default ctor.
	* diagnostic-color.c (color_dict): Add "remark", as bold green.
	* diagnostic-core.h (remark): New decl.
	* diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
	(remark): New function.
	* diagnostic.def (DK_REMARK): New diagnostic kind.
	* doc/invoke.texi (Remarks): New section.
	(-fsave-optimization-record): New option.
	(-fremarks): New option.
	* dumpfile.c: Include "optinfo.h", "cgraph.h", "optinfo-emit-json.h".
	(alt_dump_file): Make static, and group with...
	(alt_flags): ...this definition.
	(dumps_are_enabled): New variable.
	(refresh_dumps_are_enabled): New function.
	(set_dump_file): New function.
	(set_alt_dump_file): New function.
	(struct kv_pair): Move from here to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_loc): Make static.  Add indentation based on scope depth.
	(dump_gimple_stmt): Add the stmt to any optinfo, creating one if
	need be.
	(dump_gimple_stmt_loc): Convert param "loc" from location_t to
	const optinfo_location &.  Add the stmt to any optinfo, creating one
	if need be.
	(dump_generic_expr): Add the expr to any optinfo, creating one if
	need be.
	(dump_generic_expr_loc): Delete.
	(dump_printf): Add the formatted string to any optinfo, creating
	one if need be.
	(dump_printf_loc): Likewise.  Add overload taking a
	const optinfo_location &.
	(dump_dec): Add the value to any optinfo, creating one if need be.
	(dump_symtab_node): New function.
	(dump_scope_depth): New global.
	(get_dump_scope_depth): New function.
	(dump_begin_scope): New function.
	(dump_end_scope): New function.
	(gcc::dump_manager::dump_start): Replace assignments to
	"dump_file" and "alt_dump_file" with call to set_dump_file and
	set_alt_dump_file.
	(gcc::dump_manager::dump_finish): Likewise.
	* dumpfile.h (class optinfo_location): New class.
	(class optinfo_impl_location): New forward decl.
	(dump_printf_loc): Add overload, taking a
	const optinfo_location & rather than a location_t.
	(dump_generic_expr_loc): Delete decl.
	(dump_gimple_stmt_loc): Convert 2nd param from location_t to
	const optinfo_location &.
	(dump_symtab_node): New decl.
	(get_dump_scope_depth): New decl.
	(dump_begin_scope): New decl.
	(dump_end_scope): New decl.
	(alt_dump_file): Delete decl.
	(dumps_are_enabled): New decl.
	(set_dump_file): New decl.
	(dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
	global.
	(optgroup_options): New decl.
	* gengtype.c (open_base_files): Add "dumpfile.h".
	* gimple-fold.c (fold_gimple_assign): Update call to
	dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the gimple stmt.
	(gimple_fold_call): Likewise.
	* gimple-loop-interchange.cc
	(loop_cand::analyze_iloop_reduction_var): Update for change
	to check_reduction_path.
	(tree_loop_interchange::interchange): Update for change to
	find_loop_location.  Add a usage of OPTINFO_SCOPE.
	* gimple-pretty-print.c (gimple_dump_bb_buff): Make non-static.
	* gimple-pretty-print.h (gimple_dump_bb_buff): New decl.
	* graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
	change in return-type of find_loop_location.
	(graphite_regenerate_ast_isl): Likewise.
	* graphite-optimize-isl.c (optimize_isl): Likewise.
	* graphite-scop-detection.c (debug_printer::set_dump_file):
	Replace assignment to dump_file with call to ::set_dump_file.
	* graphite.c (graphite_transform_loops): Update for change in
	return-type of find_loop_location.
	* ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
	pass in a optinfo_location rather than a location_t, via the
	gimple stmt.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
	* ipa.c (walk_polymorphic_call_targets): Likewise.
	* omp-grid.c (struct grid_prop): Convert field "target_loc" from
	location_t to optinfo_location.
	(grid_find_single_omp_among_assignments_1): Updates call to
	dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the gimple stmt.
	(grid_parallel_clauses_gridifiable): Convert "tloc" from
	location_t to optinfo_location.  Updates call to dump_printf_loc
	to pass in a optinfo_location rather than a location_t, via the
	gimple stmt.
	(grid_dist_follows_simple_pattern): Likewise.
	(grid_gfor_follows_tiling_pattern): Likewise.
	(grid_target_follows_gridifiable_pattern): Likewise.
	(grid_attempt_target_gridification): Convert initialization
	of local "grid" from memset to zero-initialization; FIXME: does
	this require C++11?  Update call to dump_printf_loc to pass in a
	optinfo_location rather than a location_t, via the gimple stmt.
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* opts.c (print_specific_help): Handle CL_REMARK.
	(common_handle_option): Likewise.
	* opts.h (CL_REMARK): New macro.
	(CL_MAX_OPTION_CLASS): Update for CL_REMARK.
	(CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD)
	(CL_PCH_IGNORE): Likewise.
	* passes.c: Include "optinfo.h" and "optinfo-emit-json.h".
	(execute_optinfo_function_dump): New function.
	(execute_one_ipa_transform_pass): Add per-function call to
	execute_optinfo_function_dump.
	(execute_one_pass): Likewise.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* selftest-run-tests.c (selftest::run_tests): Call
	optinfo_cc_tests.
	* selftest.h (optinfo_cc_tests): New decl.
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_start and
	optimization_records_finish.
	* tree-loop-distribution.c (pass_loop_distribution::execute):
	Update for change in return type of find_loop_location.
	* tree-nested.c (lower_nested_functions): Replace assignments to
	"dump_file" with calls to set_dump_file.
	* tree-parloops.c (parallelize_loops): Update for change in return
	type of find_loop_location.
	* tree-ssa-live.c: Include "optinfo.h".
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
	* tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
	"locus" from location_t to optinfo_location.
	(canonicalize_loop_induction_variables): Likewise.
	* tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
	for change in return type of find_loop_location.
	* tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
	to dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the stmt.
	* tree-ssa-sccvn.c (eliminate_dom_walker::before_dom_children):
	Likewise.
	* tree-vect-loop.c (check_reduction_path): Convert "loc" param
	from location_t to optinfo_location.
	* tree-vectorizer.c (vect_location): Convert from source_location
	to optinfo_location.
	(vectorize_loops): Update initialization of vect_location.
	(increase_alignment): Likewise.
	* tree-vectorizer.h: Include "optinfo.h".
	(vect_location): Convert from source_location
	to optinfo_location and add GTY marker.
	(VECT_SCOPE): Convert to usage of OPTINFO_SCOPE.
	(IF_VECT_DUMP): New macro.
	(find_loop_location): Convert return type from source_location to
	optinfo_location.
	(check_reduction_path): Convert 1st param from location_t to
	optinfo_location.
	* value-prof.c (check_ic_target): Update call
	to dump_printf_loc to pass in a optinfo_location rather than a
	location_t, via the call_stmt.

gcc/fortran/ChangeLog:
	* gfc-diagnostic.def (DK_REMARK): New diagnostic kind.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
	remarks_plugin.c.
	* gcc.dg/plugin/remarks-1.c: New test.
	* gcc.dg/plugin/remarks_plugin.c: New test plugin.
	* lib/gcc-dg.exp (dg-remark): New function.
---
 gcc/Makefile.in                              |   5 +
 gcc/cgraph.c                                 |   6 +-
 gcc/cgraphunit.c                             |   3 +-
 gcc/common.opt                               |   9 +
 gcc/coretypes.h                              |  15 +
 gcc/coverage.c                               |   2 +-
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 +
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  34 +-
 gcc/dumpfile.c                               | 241 +++++++++--
 gcc/dumpfile.h                               |  49 ++-
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gengtype.c                               |   3 +-
 gcc/gimple-fold.c                            |   6 +-
 gcc/gimple-loop-interchange.cc               |   7 +-
 gcc/gimple-pretty-print.c                    |   2 +-
 gcc/gimple-pretty-print.h                    |   2 +
 gcc/graphite-isl-ast-to-gimple.c             |   4 +-
 gcc/graphite-optimize-isl.c                  |   4 +-
 gcc/graphite-scop-detection.c                |   2 +-
 gcc/graphite.c                               |   2 +-
 gcc/ipa-devirt.c                             |   3 +-
 gcc/ipa-prop.c                               |  10 +-
 gcc/ipa.c                                    |   9 +-
 gcc/omp-grid.c                               |  47 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 141 ++++++
 gcc/optinfo-emit-diagnostics.h               |  26 ++
 gcc/optinfo-emit-json.cc                     | 617 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  39 ++
 gcc/optinfo-internal.h                       | 145 +++++++
 gcc/optinfo.cc                               | 298 +++++++++++++
 gcc/optinfo.h                                | 298 +++++++++++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/passes.c                                 |  20 +
 gcc/profile-count.c                          |  28 ++
 gcc/profile-count.h                          |   5 +
 gcc/selftest-run-tests.c                     |   1 +
 gcc/selftest.h                               |   1 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 ++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 159 +++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-loop-distribution.c                 |   6 +-
 gcc/tree-nested.c                            |   4 +-
 gcc/tree-parloops.c                          |   3 +-
 gcc/tree-ssa-live.c                          |   4 +-
 gcc/tree-ssa-loop-ivcanon.c                  |   8 +-
 gcc/tree-ssa-loop-ivopts.c                   |   2 +-
 gcc/tree-ssa-loop-niter.c                    |   2 +-
 gcc/tree-ssa-sccvn.c                         |   3 +-
 gcc/tree-vect-loop.c                         |   2 +-
 gcc/tree-vectorizer.c                        |   8 +-
 gcc/tree-vectorizer.h                        |  27 +-
 gcc/value-prof.c                             |   4 +-
 59 files changed, 2265 insertions(+), 138 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 9b85787..cd17591 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1421,6 +1421,9 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
+	optinfo-emit-diagnostics.o \
+	optinfo-emit-json.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
@@ -2248,6 +2251,7 @@ s-bversion: BASE-VER
 	$(STAMP) s-bversion
 
 CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
 
 pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
 		    $(srcdir)/gen-pass-instances.awk
@@ -2583,6 +2587,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/internal-fn.h \
   $(srcdir)/hsa-common.c \
   $(srcdir)/calls.c \
+  $(srcdir)/dumpfile.h \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 3899467..191280e 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -268,7 +268,7 @@ void
 symbol_table::initialize (void)
 {
   if (!dump_file)
-    dump_file = dump_begin (TDI_cgraph, NULL);
+    set_dump_file (dump_begin (TDI_cgraph, NULL));
 
   if (!ipa_clones_dump_file)
     ipa_clones_dump_file = dump_begin (TDI_clones, NULL);
@@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
       const char *saved_dump_file_name = dump_file_name;
       dump_flags_t saved_dump_flags = dump_flags;
       dump_file_name = NULL;
-      dump_file = NULL;
+      set_dump_file (NULL);
 
       push_cfun (DECL_STRUCT_FUNCTION (decl));
       execute_all_ipa_transforms ();
@@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
       updated = true;
 
       current_pass = saved_current_pass;
-      dump_file = saved_dump_file;
+      set_dump_file (saved_dump_file);
       dump_file_name = saved_dump_file_name;
       dump_flags = saved_dump_flags;
     }
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 04b6919..7cfb8a0 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 	    }
           if (dump_enabled_p ())
             {
-	      location_t locus = gimple_location_safe (edge->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->name (), target->name ());
 	    }
diff --git a/gcc/common.opt b/gcc/common.opt
index 4aebcaf..21882fc 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -506,6 +506,11 @@ Driver Negative(Qn)
 R
 Driver Joined Separate
 
+fremarks
+Common Remark Var(flag_remarks)
+Emit diagnostic remarks about optimizations
+FIXME: should this be -fdiagnostics-foo or somesuch?
+
 S
 Driver
 
@@ -1942,6 +1947,10 @@ fopt-info-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fopt-info[-<type>=filename]	Dump compiler optimization details.
 
+fsave-optimization-record
+Common Report Var(flag_save_optimization_record) Optimization
+Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..33f3d21 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@ struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node_def, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
@@ -325,6 +332,14 @@ namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 84fff13..acea676 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -342,7 +342,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
       static int warned = 0;
 
       if (!warned++ && dump_enabled_p ())
-	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, optinfo_location (),
                          (flag_guess_branch_prob
                           ? "file %s not found, execution counts estimated\n"
                           : "file %s not found, execution counts assumed to "
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 3ee21bc..bcc3767 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
+  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
+	       6, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index aa5807e..63166b8 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool remark (location_t, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index e22c17b..97ed88b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
     {
     case DK_DEBUG:
     case DK_NOTE:
+    case DK_REMARK:
     case DK_ANACHRONISM:
     case DK_WARNING:
       break;
@@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   return ret;
 }
 
+/* Emit an optimization remark at LOCATION.  This is used by the optinfo
+   framework.
+   Return true if the remark was printed, false if it was inhibited.  */
+// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
+
+bool
+remark (location_t location, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, location);
+  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
index ce3dc56..b58095d 100644
--- a/gcc/diagnostic.def
+++ b/gcc/diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b06ea6e..c880c60 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
 * Debugging Options::   Producing debuggable code.
 * Optimize Options::    How much optimization?
 * Instrumentation Options:: Enabling profiling and extra run-time error checking.
+* Remarks::             Details on how your code is being optimized.
 * Preprocessor Options:: Controlling header files and macro definitions.
                          Also, getting dependency information for Make.
 * Assembler Options::   Passing options to the assembler.
@@ -416,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
 -freorder-blocks-algorithm=@var{algorithm} @gol
 -freorder-blocks-and-partition  -freorder-functions @gol
 -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
--frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
+-frounding-math  -fsave-optimization-record @gol
+-fsched2-use-superblocks  -fsched-pressure @gol
 -fsched-spec-load  -fsched-spec-load-dangerous @gol
 -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
 -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
@@ -470,6 +472,10 @@ Objective-C and Objective-C++ Dialects}.
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
 
+@item Remarks
+@xref{Remarks,,Options to Control Remarks from the Compiler}.
+@gccoptlist{-fremarks}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -9904,6 +9910,15 @@ Future versions of GCC may provide finer control of this setting
 using C99's @code{FENV_ACCESS} pragma.  This command-line option
 will be used to specify the default state for @code{FENV_ACCESS}.
 
+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+FIXME: The precise format is not yet set in stone, but it ought
+to be stabilized and then documented somewhere.
+FIXME: should this be described here within the optimization options,
+or within the developer options?
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
@@ -12037,6 +12052,23 @@ The NOP instructions are inserted at---and maybe before, depending on
 @end table
 
 
+@node Remarks
+@section Options to Control Remarks from the Compiler
+@cindex remarks
+@cindex options, remarks
+
+These options are aimed at advanced users who may be interested
+in seeing additional diagnostics from the compiler, giving information
+on the decisions it is making on the code.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+FIXME: better description needed here.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 2f11284..ede2a25 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -29,6 +29,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "tree-cfg.h"
 #include "langhooks.h"
+#include "optinfo.h"
+#include "cgraph.h"
+#include "optinfo-emit-json.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -36,18 +39,52 @@ along with GCC; see the file COPYING3.  If not see
    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
 
 static dump_flags_t pflags;		      /* current dump_flags */
-static dump_flags_t alt_flags;		      /* current opt_info flags */
 
 static void dump_loc (dump_flags_t, FILE *, source_location);
+
+/* Current -fopt-info output stream, if any, and flags.  */
+static FILE *alt_dump_file = NULL;
+static dump_flags_t alt_flags;
+
 static FILE *dump_open_alternate_stream (struct dump_file_info *);
 
 /* These are currently used for communicating between passes.
    However, instead of accessing them directly, the passes can use
    dump_printf () for dumps.  */
 FILE *dump_file = NULL;
-FILE *alt_dump_file = NULL;
 const char *dump_file_name;
 dump_flags_t dump_flags;
+bool dumps_are_enabled = false;
+
+
+/* Update the "dumps_are_enabled" global; to be called whenever dump_file
+   or alt_dump_file change.  */
+
+static void
+refresh_dumps_are_enabled ()
+{
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
+}
+
+/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+void
+set_dump_file (FILE *new_dump_file)
+{
+  dump_file = new_dump_file;
+  refresh_dumps_are_enabled ();
+}
+
+/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+static void
+set_alt_dump_file (FILE *new_alt_dump_file)
+{
+  alt_dump_file = new_alt_dump_file;
+  refresh_dumps_are_enabled ();
+}
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
   {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
@@ -74,14 +111,6 @@ static struct dump_file_info dump_files[TDI_end] =
   DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
 };
 
-/* Define a name->number mapping for a dump flag value.  */
-template <typename ValueType>
-struct kv_pair
-{
-  const char *const name;	/* the name of the value */
-  const ValueType value;	/* the value of the name */
-};
-
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
 static const kv_pair<dump_flags_t> dump_options[] =
@@ -132,7 +161,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 };
 
 /* Flags used for -fopt-info groups.  */
-static const kv_pair<optgroup_flags_t> optgroup_options[] =
+const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -360,7 +389,7 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
 
 /* Print source location on DFILE if enabled.  */
 
-void
+static void
 dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
 {
   if (dump_kind)
@@ -373,6 +402,8 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
                  DECL_SOURCE_FILE (current_function_decl),
                  DECL_SOURCE_LINE (current_function_decl),
                  DECL_SOURCE_COLUMN (current_function_decl));
+      /* Indentation based on scope depth.  */
+      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
     }
 }
 
@@ -388,25 +419,43 @@ dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      /* Attempt to consolidate via optinfo_guard.  */
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const optinfo_location &loc,
 		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.handle_loc (loc);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
@@ -421,37 +470,59 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
 
   if (alt_dump_file && (dump_kind & alt_flags))
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
-}
 
+  if (optinfo_enabled_p ())
+    {
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, extra_dump_flags);
+    }
+}
 
 /* Similar to dump_generic_expr, except additionally print the source
    location.  */
 
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_printf (dump_flags_t dump_kind, const char *format, ...)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
+      va_list ap;
+      va_start (ap, format);
+      vfprintf (dump_file, format, ap);
+      va_end (ap);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+      va_list ap;
+      va_start (ap, format);
+      vfprintf (alt_dump_file, format, ap);
+      va_end (ap);
+    }
+  if (optinfo_enabled_p ())
+    {
+      /* Attempt to consolidate via optinfo_guard.  */
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      va_list ap;
+      va_start (ap, format);
+      info.add_printf_va (format, ap);
+      va_end (ap);
     }
 }
 
-/* Output a formatted message using FORMAT on appropriate dump streams.  */
+/* Similar to dump_printf, except source location is also printed.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_printf_loc (dump_flags_t dump_kind, source_location loc,
+		 const char *format, ...)
 {
   if (dump_file && (dump_kind & pflags))
     {
       va_list ap;
+      dump_loc (dump_kind, dump_file, loc);
       va_start (ap, format);
       vfprintf (dump_file, format, ap);
       va_end (ap);
@@ -460,22 +531,35 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
   if (alt_dump_file && (dump_kind & alt_flags))
     {
       va_list ap;
+      dump_loc (dump_kind, alt_dump_file, loc);
       va_start (ap, format);
       vfprintf (alt_dump_file, format, ap);
       va_end (ap);
     }
+  if (optinfo_enabled_p ())
+    {
+      /* Attempt to consolidate via optinfo_guard.  */
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      va_list ap;
+      va_start (ap, format);
+      info.add_printf_va (format, ap);
+      va_end (ap);
+    }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* As above, but via an optinfo_location.  */
 
 void
-dump_printf_loc (dump_flags_t dump_kind, source_location loc,
+dump_printf_loc (dump_flags_t dump_kind, const optinfo_location &loc,
 		 const char *format, ...)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
       va_list ap;
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       va_start (ap, format);
       vfprintf (dump_file, format, ap);
       va_end (ap);
@@ -484,11 +568,23 @@ dump_printf_loc (dump_flags_t dump_kind, source_location loc,
   if (alt_dump_file && (dump_kind & alt_flags))
     {
       va_list ap;
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       va_start (ap, format);
       vfprintf (alt_dump_file, format, ap);
       va_end (ap);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      /* Attempt to consolidate via optinfo_guard.  */
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.handle_loc (loc);
+      va_list ap;
+      va_start (ap, format);
+      info.add_printf_va (format, ap);
+      va_end (ap);
+    }
 }
 
 /* Output VALUE in decimal to appropriate dump streams.  */
@@ -504,6 +600,13 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
+    {
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
+    }
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -512,6 +615,79 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", node->dump_name ());
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", node->dump_name ());
+
+  if (optinfo_enabled_p ())
+    {
+      /* Attempt to consolidate via optinfo_guard.  */
+      pending_optinfo info = optinfo_guard::ensure_pending ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
+    }
+}
+
+/* The current nesting depth of dump scopes, for showing nesting
+   via indentation).  */
+
+static unsigned int dump_scope_depth;
+
+/* Get the current dump scope-nesting depth.
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+get_dump_scope_depth ()
+{
+  return dump_scope_depth;
+}
+
+/* Push a nested dump scope.  */
+
+void
+dump_begin_scope (const char *name, const optinfo_location &loc,
+		  const optinfo_impl_location &impl_location)
+{
+  /* Specialcase, to avoid going through dump_printf_loc,
+     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
+
+  if (dump_file)
+    {
+      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      fprintf (dump_file, "=== %s ===\n", name);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      pending_optinfo info (impl_location, OPTINFO_KIND_SCOPE, loc);
+      info.add_printf ("=== %s ===", name);
+    }
+
+  dump_scope_depth++;
+}
+
+/* Pop a nested optinfo scope.  */
+
+void
+dump_end_scope ()
+{
+  dump_scope_depth--;
+  optimization_records_maybe_pop_optinfo_scope ();
+}
+
 /* Start a dump for PHASE. Store user-supplied dump flags in
    *FLAG_PTR.  Return the number of streams opened.  Set globals
    DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
@@ -541,7 +717,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
         }
       free (name);
       dfi->pstream = stream;
-      dump_file = dfi->pstream;
+      set_dump_file (dfi->pstream);
       /* Initialize current dump flags. */
       pflags = dfi->pflags;
     }
@@ -551,7 +727,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
     {
       dfi->alt_stream = stream;
       count++;
-      alt_dump_file = dfi->alt_stream;
+      set_alt_dump_file (dfi->alt_stream);
       /* Initialize current -fopt-info flags. */
       alt_flags = dfi->alt_flags;
     }
@@ -582,8 +758,8 @@ dump_finish (int phase)
 
   dfi->alt_stream = NULL;
   dfi->pstream = NULL;
-  dump_file = NULL;
-  alt_dump_file = NULL;
+  set_dump_file (NULL);
+  set_alt_dump_file (NULL);
   dump_flags = TDF_NONE;
   alt_flags = TDF_NONE;
   pflags = TDF_NONE;
@@ -1021,6 +1197,7 @@ dump_basic_block (dump_flags_t dump_kind, basic_block bb, int indent)
     dump_bb (dump_file, bb, indent, TDF_DETAILS);
   if (alt_dump_file && (dump_kind & alt_flags))
     dump_bb (alt_dump_file, bb, indent, TDF_DETAILS);
+  // TODO: should this also write to optinfo?
 }
 
 /* Dump FUNCTION_DECL FN as tree dump PHASE.  */
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 4941349..6eacc1b 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -223,21 +223,58 @@ struct dump_file_info
   bool graph_dump_initialized;
 };
 
+/* A class for describing a source-code location for an optinfo,
+   with various constructors for convenience.
+   In particular, this lets us associate optinfo instances
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.
+   Currently this is GTY-marked, so that vect_location can be a
+   GC root.  */
+
+class GTY(()) optinfo_location
+{
+ public:
+  optinfo_location () : m_stmt (NULL) {}
+  optinfo_location (gimple *stmt) : m_stmt (stmt) {}
+
+  location_t get_location_t () const;
+
+  gimple *m_stmt;
+  // Or maybe a basic_block and a location_t, so that we can support RTL.
+  // Or maybe just a profile_count and a location_t.
+};
+
+class optinfo_impl_location;
+
 /* In dumpfile.c */
 extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
+/* TODO: eliminate this overload:
+   Remaining uses of source_location/location_t are in:
+   - loop-unroll.c (which is RTL-based),
+   - coverage.c, profile.c (which use input_location for error-handling),
+   - value-prof.c: stmt or current_function_decl
+ */
 extern void dump_printf_loc (dump_flags_t, source_location,
                              const char *, ...) ATTRIBUTE_PRINTF_3;
+extern void dump_printf_loc (dump_flags_t, const optinfo_location &,
+                             const char *, ...) ATTRIBUTE_PRINTF_3;
 extern void dump_function (int phase, tree fn);
 extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
 extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
+extern void dump_gimple_stmt_loc (dump_flags_t, const optinfo_location &, dump_flags_t,
 				  gimple *, int);
 extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
+
+extern unsigned int get_dump_scope_depth ();
+extern void dump_begin_scope (const char *name, const optinfo_location &loc,
+			      const optinfo_impl_location &impl_location);
+extern void dump_end_scope ();
+
 extern void print_combine_total_stats (void);
 extern bool enable_rtl_dump_file (void);
 
@@ -254,17 +291,21 @@ extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
 
 /* Global variables used to communicate with passes.  */
 extern FILE *dump_file;
-extern FILE *alt_dump_file;
 extern dump_flags_t dump_flags;
 extern const char *dump_file_name;
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
 
 /* Return true if any of the dumps is enabled, false otherwise. */
 static inline bool
 dump_enabled_p (void)
 {
-  return (dump_file || alt_dump_file);
+  return GCC_UNLIKELY (dumps_are_enabled);
 }
 
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
+
 namespace gcc {
 
 class dump_manager
diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
index 565fa83..a10b6aa 100644
--- a/gcc/fortran/gfc-diagnostic.def
+++ b/gcc/fortran/gfc-diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 0db5528..166baa2 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1724,7 +1724,8 @@ open_base_files (void)
       "tree-dfa.h", "tree-ssa.h", "reload.h", "cpp-id-data.h", "tree-chrec.h",
       "except.h", "output.h",  "cfgloop.h", "target.h", "lto-streamer.h",
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
-      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h", NULL
+      "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-offload.h",
+      "dumpfile.h", NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index c1d8442..c023274 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -347,8 +347,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 		  {
 		    if (dump_enabled_p ())
 		      {
-			location_t loc = gimple_location_safe (stmt);
-			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 					 "resolving virtual function address "
 					 "reference to function %s\n",
 					 targets.length () == 1
@@ -4060,8 +4059,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 	      tree lhs = gimple_call_lhs (stmt);
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				   "folding virtual function call to %s\n",
 		 		   targets.length () == 1
 		  		   ? targets[0]->name ()
diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index eb35263..ef8df28 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -523,7 +523,7 @@ loop_cand::analyze_iloop_reduction_var (tree var)
 
   /* Handle and verify a series of stmts feeding the reduction op.  */
   if (single_use != next_def
-      && !check_reduction_path (UNKNOWN_LOCATION, m_loop, phi, next,
+      && !check_reduction_path (optinfo_location (), m_loop, phi, next,
 				gimple_assign_rhs_code (single_use)))
     return false;
 
@@ -1578,7 +1578,10 @@ bool
 tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    vec<ddr_p> ddrs)
 {
-  location_t loc = find_loop_location (m_loop_nest[0]);
+  optinfo_location loc = find_loop_location (m_loop_nest[0]);
+
+  OPTINFO_SCOPE ("tree_loop_interchange::interchange", loc);
+
   bool changed_p = false;
   /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
      The overall effect is to push inner loop to outermost level in whole
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 405d9e3..c1f28cd 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2796,7 +2796,7 @@ dump_implicit_edges (pretty_printer *buffer, basic_block bb, int indent,
 /* Dumps basic block BB to buffer BUFFER with details described by FLAGS and
    indented by INDENT spaces.  */
 
-static void
+void
 gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
 		     dump_flags_t flags)
 {
diff --git a/gcc/gimple-pretty-print.h b/gcc/gimple-pretty-print.h
index 6ae6a3b..e7a5607 100644
--- a/gcc/gimple-pretty-print.h
+++ b/gcc/gimple-pretty-print.h
@@ -34,6 +34,8 @@ extern void print_gimple_expr (FILE *, gimple *, int, dump_flags_t = TDF_NONE);
 extern void pp_gimple_stmt_1 (pretty_printer *, gimple *, int, dump_flags_t);
 extern void gimple_dump_bb (FILE *, basic_block, int, dump_flags_t);
 extern void gimple_dump_bb_for_graph (pretty_printer *, basic_block);
+extern void gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
+				 dump_flags_t flags);
 extern void dump_ssaname_info_to_file (FILE *, tree, int);
 extern void percent_G_format (text_info *);
 
diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c
index b607b12..58ebb2d 100644
--- a/gcc/graphite-isl-ast-to-gimple.c
+++ b/gcc/graphite-isl-ast-to-gimple.c
@@ -1409,7 +1409,7 @@ scop_to_isl_ast (scop_p scop)
   isl_ctx_set_max_operations (scop->isl_context, old_max_operations);
   if (isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      optinfo_location loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -1518,7 +1518,7 @@ graphite_regenerate_ast_isl (scop_p scop)
 
   if (t.codegen_error_p ())
     {
-      location_t loc = find_loop_location
+      optinfo_location loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
 		       "loop nest not optimized, code generation error\n");
diff --git a/gcc/graphite-optimize-isl.c b/gcc/graphite-optimize-isl.c
index 456a797..0eb59e0 100644
--- a/gcc/graphite-optimize-isl.c
+++ b/gcc/graphite-optimize-isl.c
@@ -160,7 +160,7 @@ optimize_isl (scop_p scop)
   if (!scop->transformed_schedule
       || isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      optinfo_location loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -182,7 +182,7 @@ optimize_isl (scop_p scop)
 
   if (same_schedule)
     {
-      location_t loc = find_loop_location
+      optinfo_location loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_NOTE, loc,
 		       "loop nest not optimized, optimized schedule is "
diff --git a/gcc/graphite-scop-detection.c b/gcc/graphite-scop-detection.c
index 0dafc39..3c53ce0 100644
--- a/gcc/graphite-scop-detection.c
+++ b/gcc/graphite-scop-detection.c
@@ -61,7 +61,7 @@ public:
   set_dump_file (FILE *f)
   {
     gcc_assert (f);
-    dump_file = f;
+    ::set_dump_file (f);
   }
 
   friend debug_printer &
diff --git a/gcc/graphite.c b/gcc/graphite.c
index bcf4828..1358888 100644
--- a/gcc/graphite.c
+++ b/gcc/graphite.c
@@ -412,7 +412,7 @@ graphite_transform_loops (void)
 	changed = true;
 	if (graphite_regenerate_ast_isl (scop))
 	  {
-	    location_t loc = find_loop_location
+	    optinfo_location loc = find_loop_location
 	      (scops[i]->scop_info->region.entry->dest->loop_father);
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
 			     "loop nest optimized\n");
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 308b6e6..e99d8cc 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -3755,8 +3755,7 @@ ipa_devirt (void)
 	      {
 		if (dump_enabled_p ())
                   {
-                    location_t locus = gimple_location_safe (e->call_stmt);
-                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
 				     "speculatively devirtualizing call "
 				     "in %s to %s\n",
 				     n->dump_name (),
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index daada4d..bc02fd0 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -2843,8 +2843,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 	    {
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (ie->call_stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 				   "discovered direct call non-invariant %s\n",
 				   ie->caller->dump_name ());
 		}
@@ -2854,8 +2853,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 
           if (dump_enabled_p ())
 	    {
-	      location_t loc = gimple_location_safe (ie->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 			       "discovered direct call to non-function in %s, "
 			       "making it __builtin_unreachable\n",
 			       ie->caller->dump_name ());
@@ -2943,9 +2941,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
      }
   if (dump_enabled_p ())
     {
-      location_t loc = gimple_location_safe (ie->call_stmt);
-
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 		       "converting indirect call in %s to direct call to %s\n",
 		       ie->caller->name (), callee->name ());
     }
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 82fc334..3b6b5e5 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -225,13 +225,8 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
 	  if (dump_enabled_p ())
-            {
-	      location_t locus;
-	      if (edge->call_stmt)
-		locus = gimple_location (edge->call_stmt);
-	      else
-		locus = UNKNOWN_LOCATION;
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	    {
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->dump_name (),
 			       target->dump_name ());
diff --git a/gcc/omp-grid.c b/gcc/omp-grid.c
index ffa301e..5f0dac8 100644
--- a/gcc/omp-grid.c
+++ b/gcc/omp-grid.c
@@ -91,7 +91,7 @@ struct grid_prop
   bool tiling;
   /* Location of the target construct for optimization information
      messages.  */
-  location_t target_loc;
+  optinfo_location target_loc;
   /* The collapse clause of the involved loops.  Collapse value of all of them
      must be the same for gridification to take place.  */
   size_t collapse;
@@ -177,10 +177,10 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "%s construct "
 				   "contains multiple OpenMP constructs\n",
 				   name);
-		  dump_printf_loc (MSG_NOTE, gimple_location (*ret),
+		  dump_printf_loc (MSG_NOTE, *ret,
 				   "The first OpenMP construct within "
 				   "a parallel\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "The second OpenMP construct within "
 				   "a parallel\n");
 		}
@@ -195,7 +195,7 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "%s construct contains "
 			       "a complex statement\n", name);
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "gridification\n");
 	    }
@@ -286,7 +286,7 @@ grid_find_ungridifiable_statement (gimple_stmt_iterator *gsi,
    loop that is evaluated for possible gridification.  */
 
 static bool
-grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
+grid_parallel_clauses_gridifiable (gomp_parallel *par, optinfo_location tloc)
 {
   tree clauses = gimple_omp_parallel_clauses (par);
   while (clauses)
@@ -300,7 +300,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 			       GRID_MISSED_MSG_PREFIX "because there is "
 			       "a num_threads clause of the parallel "
 			       "construct\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a num_threads clause\n");
 	    }
 	  return false;
@@ -311,7 +311,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, tloc,
 			       GRID_MISSED_MSG_PREFIX "a reduction clause "
 			       "is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a reduction clause\n");
 	    }
 	  return false;
@@ -341,7 +341,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			   GRID_MISSED_MSG_PREFIX "the inner loop "
 			   "loop bounds computation contains a complex "
 			   "statement\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -361,7 +361,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the inner loop "
 				   "has a non-automatic schedule clause\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+		  dump_printf_loc (MSG_NOTE, gfor,
 				   "Loop construct has a non automatic "
 				   "schedule clause\n");
 		}
@@ -375,7 +375,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "a reduction "
 			       "clause is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct has a reduction schedule "
 			       "clause\n");
 	    }
@@ -404,7 +404,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			     GRID_MISSED_MSG_PREFIX "the inner loop contains "
 			     "statement %s which cannot be transformed\n",
 			     gimple_code_name[(int) gimple_code (bad)]);
-	  dump_printf_loc (MSG_NOTE, gimple_location (bad),
+	  dump_printf_loc (MSG_NOTE, bad,
 			   "This statement cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -422,7 +422,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 static bool
 grid_dist_follows_simple_pattern (gomp_for *dist, grid_prop *grid)
 {
-  location_t tloc = grid->target_loc;
+  optinfo_location tloc = grid->target_loc;
   gimple *stmt = grid_find_single_omp_among_assignments (gimple_omp_body (dist),
 							 grid, "distribute");
   gomp_parallel *par;
@@ -468,7 +468,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop is not "
 			   "a simple for loop\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "This statement is not a simple for loop\n");
 	}
       return false;
@@ -484,7 +484,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop does not "
 			   "have use the same collapse clause\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct uses a different collapse clause\n");
 	}
       return false;
@@ -524,7 +524,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute and "
 			       "an internal loop do not agree on tile size\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct does not seem to loop over "
 			       "a tile size\n");
 	    }
@@ -636,7 +636,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the distribute "
 				   "construct contains a try..catch region\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (try_stmt),
+		  dump_printf_loc (MSG_NOTE, try_stmt,
 				   "This statement cannot be analyzed for "
 				   "tiled gridification\n");
 		}
@@ -661,7 +661,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a call\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -677,7 +677,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a parallel "
 				   "construct contains another parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This parallel construct is nested in "
 				   "another one\n");
 		}
@@ -698,7 +698,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a loop "
 				   "construct is not nested within a parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This loop construct is not nested in "
 				   "a parallel construct\n");
 		}
@@ -714,7 +714,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a complex statement\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -734,7 +734,7 @@ grid_target_follows_gridifiable_pattern (gomp_target *target, grid_prop *grid)
   if (gimple_omp_target_kind (target) != GF_OMP_TARGET_KIND_REGION)
     return false;
 
-  location_t tloc = gimple_location (target);
+  optinfo_location tloc = target;
   grid->target_loc = tloc;
   gimple *stmt
     = grid_find_single_omp_among_assignments (gimple_omp_body (target),
@@ -1257,14 +1257,13 @@ grid_attempt_target_gridification (gomp_target *target,
 				   gbind *tgt_bind)
 {
   /* removed group_size */
-  grid_prop grid;
-  memset (&grid, 0, sizeof (grid));
+  grid_prop grid = {};
   if (!target || !grid_target_follows_gridifiable_pattern (target, &grid))
     return;
 
   location_t loc = gimple_location (target);
   if (dump_enabled_p ())
-    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, target,
 		     "Target construct will be turned into a gridified HSA "
 		     "kernel\n");
 
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 2c371e5..48ecac5 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,6 +105,7 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
+	  test_flag("Remark", flags,  " | CL_REMARK") \
 	  test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
 	sub( "^0 \\| ", "", result )
 	return result
diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
new file mode 100644
index 0000000..8b86205
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,141 @@
+/* Emit optimization information as "remark" diagnostics.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "gimple.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-internal.h"
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_translate_identifiers (&pp) = false;
+
+  bool show_color = pp_show_color (global_dc->printer);
+
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_dump_scope_depth (); i > 0; i--)
+    pp_space (&pp);
+
+  /* Print the items into PP.  */
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text
+	      = (const optinfo_item_text *)item;
+	    pp_string (&pp, as_text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree
+	      = (const optinfo_item_tree *)item;
+	    pp_begin_quote (&pp, show_color);
+	    dump_generic_node (&pp, as_tree->get_node (), 0, TDF_DETAILS,
+			       false);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    pp_begin_quote (&pp, show_color);
+	    pp_string (&pp, node->dump_name ());
+	    pp_end_quote (&pp, show_color);
+	  }
+	  break;
+	}
+    }
+
+  /* Add metadata: which pass?  */
+  if (current_pass)
+    {
+      pp_string (&pp, " [");
+      pp_string (&pp, colorize_start (show_color,
+				      diagnostic_get_color_for_kind (DK_REMARK)));
+      pp_string (&pp, "pass=");
+      pp_string (&pp, current_pass->name);
+      pp_string (&pp, colorize_stop (show_color));
+      pp_string (&pp, "]");
+    }
+
+  /* Add metadata: hotness.  */
+  gimple *stmt = optinfo->get_location ().m_stmt;
+  if (stmt)
+    if (stmt->bb)
+      if (stmt->bb->count.initialized_p ())
+	{
+	  profile_count count = stmt->bb->count;
+	  pp_string (&pp, " [");
+	  pp_string (&pp, colorize_start (show_color,
+					  diagnostic_get_color_for_kind (DK_NOTE)));
+	  pp_string (&pp, "count(");
+	  pp_string (&pp, profile_quality_as_string (count.quality ()));
+	  pp_string (&pp, ")=");
+	  pp_scalar (&pp, "%li", count.to_gcov_type ());
+	  pp_string (&pp, colorize_stop (show_color));
+	  pp_string (&pp, "]");
+	}
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
new file mode 100644
index 0000000..820cefd
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.h
@@ -0,0 +1,26 @@
+/* Emit optimization information as "remark" diagnostics.
+   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_OPTINFO_EMIT_DIAGNOSTICS_H
+#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
+
+extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..3cf4d68
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,617 @@
+/* Emit optimization information as JSON files.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+#include "version.h"
+#include "context.h"
+#include "pass_manager.h"
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+impl_location_to_json (optinfo_impl_location loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+static json::object *
+location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+static json::object *
+profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Get a string for use when referring to PASS in the saved optimization
+   records.  */
+
+json::string *
+get_id_value_for_pass (opt_pass *pass)
+{
+  pretty_printer pp;
+  /* this is host-dependent, but will be consistent for a given host.  */
+  pp_pointer (&pp, static_cast<void *> (pass));
+  return new json::string (pp_formatted_text (&pp));
+}
+
+/* Create a JSON object representing PASS.  */
+
+static json::object *
+pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("id", get_id_value_for_pass (pass));
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+static json::value *
+inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  obj->set ("fndecl",
+		    new json::string (lang_hooks.decl_printable_name (fndecl, 2)));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* The root value for the JSON file.
+   Currently the JSON values are stored in memory, and flushed when the
+   compiler exits.  It would probably be better to simply write out
+   the JSON as we go.  */
+
+static json::array *root_tuple;
+
+/* The currently open scopes, for expressing nested optimization records.  */
+
+static vec<json::array *> scopes;
+
+/* Add a json description of PASS and its siblings to ARR, recursing into
+   child passes (adding their descriptions within a "children" array).  */
+
+static void
+add_pass_list (json::array *arr, opt_pass *pass)
+{
+  do
+    {
+      json::object *pass_obj = pass_to_json (pass);
+      arr->append (pass_obj);
+      if (pass->sub)
+	{
+	  json::array *sub = new json::array ();
+	  pass_obj->set ("children", sub);
+	  add_pass_list (sub, pass->sub);
+	}
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  root_tuple = new json::array ();
+
+  /* Populate with metadata; compare with toplev.c: print_version.  */
+  json::object *metadata = new json::object ();
+  root_tuple->append (metadata);
+  metadata->set ("format", new json::string ("1"));
+  json::object *generator = new json::object ();
+  metadata->set ("generator", generator);
+  generator->set ("name", new json::string (lang_hooks.name));
+  generator->set ("pkgversion", new json::string (pkgversion_string));
+  generator->set ("version", new json::string (version_string));
+  /* TARGET_NAME is passed in by the Makefile.  */
+  generator->set ("target", new json::string (TARGET_NAME));
+
+  /* TODO: capture command-line?
+     see gen_producer_string in dwarf2out.c (currently static).  */
+
+  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
+
+  json::array *passes = new json::array ();
+  root_tuple->append (passes);
+
+  /* Call add_pass_list for all of the pass lists.  */
+  {
+#define DEF_PASS_LIST(LIST) \
+    add_pass_list (passes, g->get_passes ()->LIST);
+    GCC_PASS_LISTS
+#undef DEF_PASS_LIST
+  }
+
+  json::array *records = new json::array ();
+  root_tuple->append (records);
+
+  scopes.safe_push (records);
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!root_tuple)
+    return;
+
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      root_tuple->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+
+  delete root_tuple;
+  root_tuple = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return root_tuple != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!root_tuple)
+    return;
+
+  gimple *stmt = optinfo->get_location ().m_stmt;//get_best_stmt ();
+
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text = (const optinfo_item_text *)item;
+	    message->append (new json::string (as_text->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree = (const optinfo_item_tree *)item;
+	    tree node = as_tree->get_node ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+	         dump_generic_expr (MSG_NOTE, flags, node);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+	         print_generic_expr (alt_dump_file, node, dump_flags | extra_dump_flags);
+	       which is:
+		 maybe_init_pretty_print (file);
+		 dump_generic_node (tree_pp, node, 0, flags, false);
+		 pp_flush (tree_pp);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_translate_identifiers (&pp) = false;
+
+	    dump_generic_node (&pp, node, 0, as_tree->get_flags (), false);
+
+	    item->set ("expr", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the node.  */
+	    if (EXPR_HAS_LOCATION (node))
+	      item->set ("location", location_to_json (EXPR_LOCATION (node)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+		 dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+		 print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
+	       which is:
+		 pp_needs_newline (&buffer) = true;
+		 buffer.buffer->stream = file;
+		 pp_gimple_stmt_1 (&buffer, g, spc, flags);
+		 pp_newline_and_flush (&buffer);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    item->set ("stmt", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the stmt.  */
+	    if (gimple_location (stmt))
+	      item->set ("location", location_to_json (gimple_location (stmt)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    json::object *item = new json::object ();
+	    item->set ("name", new json::string (node->name ()));
+	    item->set ("order", new json::number (node->order));
+	    if (DECL_SOURCE_LOCATION (node->decl) != UNKNOWN_LOCATION)
+	      item->set ("location",
+			 location_to_json (DECL_SOURCE_LOCATION (node->decl)));
+	    message->append (item);
+	  }
+	  break;
+	}
+   }
+
+  if (current_pass)
+    obj->set ("pass", get_id_value_for_pass (current_pass));
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (stmt)
+    {
+      loc = gimple_location (stmt);
+      if (stmt->bb)
+	if (stmt->bb->count.initialized_p ())
+	  obj->set ("count", profile_count_to_json (stmt->bb->count));
+    }
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  /* Add to innermost scope.  */
+  gcc_assert (scopes.length () > 0);
+  scopes[scopes.length () - 1]->append (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      scopes.safe_push (children);
+    }
+}
+
+/* Handling for the end of an optinfo scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_optinfo_scope ()
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!root_tuple)
+    return;
+
+  scopes.pop ();
+}
+
+/* Generate a JSON object describing BB, adding it to CFG_OBJ.
+   Compare with dump_bb, dump_bb_info etc.  */
+
+static void
+dump_bb_to_json (basic_block bb, json::array *cfg_obj)
+{
+  json::object *bb_obj = new json::object ();
+  cfg_obj->append (bb_obj);
+
+  /* Basic metadata.  Compare with dump_bb_info.  */
+
+  bb_obj->set ("index", new json::number (bb->index));
+  if (bb->count.initialized_p ())
+    bb_obj->set ("count", profile_count_to_json (bb->count));
+
+  /* Flags.  */
+  static const char * const bb_bitnames[] =
+    {
+#define DEF_BASIC_BLOCK_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+      NULL
+#undef DEF_BASIC_BLOCK_FLAG
+    };
+  const unsigned n_bitnames = sizeof (bb_bitnames) / sizeof (char *);
+  json::array *bb_flags = new json::array ();
+  bb_obj->set ("flags", bb_flags);
+  for (unsigned i = 0; i < n_bitnames; i++)
+    if (bb->flags & (1 << i))
+      bb_flags->append (new json::string (bb_bitnames[i]));
+
+  bb_obj->set ("discriminator", new json::number (bb->discriminator));
+
+  if (bb->index >= NUM_FIXED_BLOCKS)
+    {
+      /* For now, just capture all of the statements as one string.  */
+      pretty_printer pp;
+      pp.buffer->stream = NULL;
+      pp.buffer->flush_p = false;
+      pp_needs_newline (&pp) = true;
+      gimple_dump_bb_buff (&pp, bb, 0, TDF_NONE);
+      bb_obj->set ("stmts", new json::string (pp_formatted_text (&pp)));
+    }
+
+  json::array *succs = new json::array ();
+  bb_obj->set ("succs", succs);
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      /* compare with dump_edge_info.  */
+      json::object *edge_obj = new json::object ();
+      succs->append (edge_obj);
+      edge_obj->set ("dest", new json::number (e->dest->index));
+
+      /* Edge flags.  */
+      static const char * const bitnames[] =
+	{
+#define DEF_EDGE_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+	  NULL
+#undef DEF_EDGE_FLAG
+	};
+
+      json::array *edge_flags = new json::array ();
+      edge_obj->set ("flags", edge_flags);
+
+      gcc_assert (e->flags <= EDGE_ALL_FLAGS);
+      int flags = e->flags;
+      for (int i = 0; flags; i++)
+	if (flags & (1 << i))
+	  {
+	    flags &= ~(1 << i);
+	    edge_flags->append (new json::string (bitnames[i]));
+	  }
+    }
+}
+
+/* Populate FN_OBJ based on FUN.
+   Compare with dump_function_to_file.  */
+
+static void
+dump_function_to_json (function *fun, json::object *fn_obj)
+{
+  tree arg, var, old_current_fndecl = current_function_decl;
+  struct function *dsf;
+  bool ignore_topmost_bind = false, any_var = false;
+  basic_block bb;
+  tree chain;
+  tree fndecl = fun->decl;
+
+  current_function_decl = fndecl;
+
+  if (fun && fun->decl == fndecl
+      && fun->cfg
+      && basic_block_info_for_fn (fun))
+    {
+      json::array *cfg_obj = new json::array ();
+      fn_obj->set ("cfg", cfg_obj);
+
+      FOR_ALL_BB_FN (bb, fun)
+	dump_bb_to_json (bb, cfg_obj);
+    }
+
+  current_function_decl = old_current_fndecl;
+}
+
+
+/* Add a record describing the state of FN after PASS to the queue of
+   records to be written.  */
+
+void
+optimization_records_maybe_record_function (function *fn,
+					    opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  tree fndecl = fn->decl;
+
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("state"));
+  obj->set ("pass", get_id_value_for_pass (pass));
+  const char *fnname = get_fnname_from_decl (fndecl);
+  obj->set ("function", new json::string (fnname));
+
+  if (fn->curr_properties & PROP_trees)
+    {
+      dump_function_to_json (fn, obj);
+    }
+  else
+    {
+      // TODO?  RTL equivalent is:
+      //print_rtl_with_bb (dump_file, get_insns (), dump_flags);
+    }
+
+  gcc_assert (scopes.length () == 1);
+  scopes[scopes.length () - 1]->append (obj);
+}
+
+
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..5468946
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,39 @@
+/* Emit optimization information as JSON files.
+   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_OPTINFO_EMIT_JSON_H
+#define GCC_OPTINFO_EMIT_JSON_H
+
+class optinfo;
+struct opt_pass;
+
+extern void optimization_records_start ();
+extern void optimization_records_finish ();
+
+// FIXME: maybe make this inline?
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_optinfo_scope ();
+extern void optimization_records_maybe_record_function (function *fun,
+							opt_pass *pass);
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..3f267ee
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,145 @@
+/* Implementation details of optinfo.
+   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_OPTINFO_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+  void trim_trailing_whitespace ();
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node, dump_flags_t dump_flags)
+    : m_node (node), m_dump_flags (dump_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  tree m_node;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
+    : m_stmt (stmt), m_dump_flags (dump_flags) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  gimple *m_stmt;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..9f02bfc
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,298 @@
+/* Optimization information.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "selftest.h"
+
+/* Remove any trailing whitespace characters from this text item.
+   Primarily for use in stripping trailing newline characters when
+   emitting remarks (since the diagnostic subsystem doesn't expect
+   trailing newlines in messages).  */
+
+void
+optinfo_item_text::trim_trailing_whitespace ()
+{
+  size_t len = strlen (m_text);
+  if (len == 0)
+    return;
+
+  size_t new_len = len;
+  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
+    new_len--;
+
+  if (new_len == len)
+    return;
+
+  if (m_owned)
+    m_text[new_len] = '\0';
+  else
+    {
+      m_text = xstrndup (m_text, new_len);
+      m_owned = true;
+    }
+}
+
+/* Get the source location from this optinfo_location.  */
+
+location_t
+optinfo_location::get_location_t () const
+{
+  if (m_stmt)
+    return gimple_location (m_stmt);
+  else
+    return UNKNOWN_LOCATION;
+}
+
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Eliminate any trailing whitespace.  */
+  while (m_items.length () > 0)
+    {
+      optinfo_item *last_item = m_items[m_items.length () - 1];
+      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
+	break;
+
+      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
+      last_text->trim_trailing_whitespace ();
+
+      if (strlen (last_text->get_text ()) > 0)
+	break;
+
+      m_items.pop ();
+      delete last_item;
+    }
+
+  /* Flush the pending information.  */
+  emit ();
+
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Get a location_t from an optinfo.  */
+
+location_t
+optinfo::get_location_t () const
+{
+  return m_loc.get_location_t ();
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit () const
+{
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
+
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
+}
+
+/* pending_optinfo's ctor.  */
+
+pending_optinfo::pending_optinfo (const optinfo_impl_location &impl_location,
+				  enum optinfo_kind kind,
+				  optinfo_location loc)
+  : m_optinfo (new optinfo (impl_location, kind, loc))
+{
+}
+
+/* Update the underlying optinfo's kind based on DUMP_KIND.  */
+
+void
+pending_optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_optinfo->m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_optinfo->m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    m_optinfo->m_kind = OPTINFO_KIND_NOTE;
+}
+
+/* Update the underlying optinfo's location based on LOC.  */
+
+void
+pending_optinfo::handle_loc (optinfo_location loc)
+{
+  m_optinfo->m_loc = loc;
+}
+
+/* Append a string literal to this pending_optinfo.  */
+
+void
+pending_optinfo::add_string (const char *str)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (str), false);
+  m_optinfo->m_items.safe_push (item);
+}
+
+/* Append printf-formatted text to this pending_optinfo.  */
+
+void
+pending_optinfo::add_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  add_printf_va (format, ap);
+  va_end (ap);
+}
+
+/* Append printf-formatted text to this pending_optinfo.  */
+
+void
+pending_optinfo::add_printf_va (const char *format, va_list ap)
+{
+  char *formatted_text = xvasprintf (format, ap);
+  optinfo_item *item
+    = new optinfo_item_text (formatted_text, true);
+  m_optinfo->m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this pending_optinfo.  */
+
+void
+pending_optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
+}
+
+/* Append a tree node to this pending_optinfo.  */
+
+void
+pending_optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_tree (node, dump_flags));
+}
+
+/* Append a symbol table node to this pending_optinfo.  */
+
+void
+pending_optinfo::add_symtab_node (symtab_node *node)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_symtab_node (node));
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   pending_optinfo.  */
+
+void
+pending_optinfo::add_dec (const wide_int_ref &wi, signop sgn)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (wi, buf, sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_optinfo->m_items.safe_push (item);
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  return optimization_records_enabled_p () || flag_remarks;
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return optimization_records_enabled_p ();
+}
+
+/* class optinfo_guard. */
+
+pending_optinfo *optinfo_guard::pending;
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that optinfo_item_text::trim_trailing_whitespace turns
+   INPUT into EXPECTED.  */
+
+static void
+test_trim_trailing_whitespace (const char *input, const char *expected)
+{
+  optinfo_item_text item (const_cast <char *> (input), false);
+  item.trim_trailing_whitespace ();
+  ASSERT_STREQ (item.get_text (), expected);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_cc_tests ()
+{
+  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
+  test_trim_trailing_whitespace ("", "");
+  test_trim_trailing_whitespace ("\n", "");
+  test_trim_trailing_whitespace ("foo", "foo");
+  test_trim_trailing_whitespace ("foo\n", "foo");
+  test_trim_trailing_whitespace ("foo\n\n", "foo");
+  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..23b17f8
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,298 @@
+/* Optimization information.
+   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_OPTINFO_H
+#define GCC_OPTINFO_H
+
+#include "unique-ptr.h"
+#include "dumpfile.h"
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file via "-fsave-optimization-record"
+
+   They are generated in response to calls to calls to the "dump_*" API
+   in dumpfile.h
+
+   Building an optinfo instance is non-trivial, so all usage should be
+   guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.
+
+   They're intended to be be short-lived; in particular, there's no
+   interaction with GTY: it's assumed that no GC happens during the
+   lifetime of an optinfo.  */
+
+/* Forward decls.  */
+class pending_optinfo;
+
+/* optinfo-internal.h.  */
+class optinfo_item;
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* A way to identify where in the compiler source that optimization information
+   is being emitted from.  */
+/* FIXME: taken from selftest::location; should this be refactored?  */
+
+struct optinfo_impl_location
+{
+  optinfo_impl_location (const char *file, int line, const char *function)
+    : m_file (file), m_line (line), m_function (function) {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* The current source location, expressed as an optinfo_impl_location.  */
+
+#define OPTINFO_IMPL_LOCATION \
+  (optinfo_impl_location (__FILE__, __LINE__, __FUNCTION__))
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.
+
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo
+{
+  friend class pending_optinfo;
+ public:
+  optinfo (const optinfo_impl_location &impl_location,
+	   enum optinfo_kind kind,
+	   optinfo_location loc)
+  : m_impl_location (impl_location), m_kind (kind), m_loc (loc), m_items ()
+  {}
+  ~optinfo ();
+
+  optinfo_impl_location get_impl_location () const { return m_impl_location; }
+  enum optinfo_kind get_kind () const { return m_kind; }
+  optinfo_location get_location () const { return m_loc; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const;
+
+ private:
+   void emit () const;
+
+ private:
+  optinfo_impl_location m_impl_location;
+  enum optinfo_kind m_kind;
+  optinfo_location m_loc;
+  auto_vec <optinfo_item *> m_items;
+};
+
+/* Support class for building and emitting optinfo instances.
+
+   The info is emitted to all active sinks when the pending_optinfo
+   goes out of scope.  */
+
+class pending_optinfo
+{
+ public:
+  pending_optinfo (const optinfo_impl_location &impl_location,
+		   enum optinfo_kind kind,
+		   optinfo_location loc);
+
+  void handle_dump_file_kind (dump_flags_t);
+  void handle_loc (optinfo_location loc);
+
+  /* Pre-canned ways of adding information to the optinfo.  */
+  void add_string (const char *str);
+  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
+  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
+  void add_tree (tree node, dump_flags_t dump_flags);
+  void add_symtab_node (symtab_node *node);
+  void add_dec (const wide_int_ref &wi, signop sgn);
+
+  template<unsigned int N, typename C>
+  void add_poly_int (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      add_dec (value.coeffs[0], sgn);
+    else
+      {
+	add_string ("[");
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    add_dec (value.coeffs[i], sgn);
+	    add_string (i == N - 1 ? "]" : ",");
+	  }
+      }
+  }
+
+ private:
+  // FIXME: are we allowed this yet?  or do we need a gnu::shared_ptr?
+  std::shared_ptr <optinfo> m_optinfo;
+};
+
+/* Implementation detail of the OPTINFO_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit optinfo remarks
+   about entering and exiting the body of a given function.  */
+
+class optinfo_scope
+{
+ public:
+  optinfo_scope (const char *name, optinfo_location loc,
+		 const optinfo_impl_location &impl_location)
+  : m_name (name), m_loc (loc)
+  {
+    if (dump_enabled_p ())
+      dump_begin_scope (name, loc, impl_location);
+  }
+  ~optinfo_scope ()
+  {
+    if (dump_enabled_p ())
+      dump_end_scope ();
+  }
+
+ private:
+  const char *m_name;
+  optinfo_location m_loc;
+};
+
+/* A macro for emitting an optinfo note about entering a scope,
+   pushing and popping the scope, so that all optinfos "within"
+   the scope are nested within it.  */
+
+#define OPTINFO_SCOPE(NAME, LOC) \
+  optinfo_scope scope (NAME, LOC, (OPTINFO_IMPL_LOCATION))
+
+/* A RAII class for consolidating "dump_*" calls, so that a single
+   optimization record is created holding all of the "dump_*" calls
+   made within the lifetime of the guard.
+
+   Can also be used in conditionals:
+
+     if (optinfo_guard guard = optinfo_guard (location, OPTINFO_IMPL_LOCATION))
+       {
+          SUITE
+       }
+
+   so that all dump_* calls within "SUITE" are consolidated into
+   one optimization record.  */
+
+class optinfo_guard
+{
+ public:
+  optinfo_guard (const optinfo_location &loc,
+		 const optinfo_impl_location &impl_location)
+  : m_enabled (optinfo_enabled_p ())
+  {
+    /* Early bailout.  */
+    if (GCC_LIKELY (!m_enabled))
+      return;
+
+    /* We assume this stuff gets optimized away.  */
+    gcc_assert (pending == NULL);
+    pending = new pending_optinfo (impl_location,
+				   OPTINFO_KIND_NOTE,
+				   loc);
+  }
+  ~optinfo_guard ()
+  {
+    /* Early bailout.  */
+    if (GCC_LIKELY (!m_enabled))
+      return;
+
+    gcc_assert (pending != NULL);
+    delete (pending);
+    pending = NULL;
+  }
+
+  operator bool () { return dump_enabled_p (); }
+
+  /* If there's an open guard, return its pending_optinfo (thus
+     consolidating all calls within the open guard into the same optinfo)
+
+     Otherwise, return a new pending_optinfo, which will own a
+     temporary one-time optinfo.
+
+     The various dump_* calls lazily create optinfo via:
+     via:
+
+        if (optinfo_enabled_p ())
+	  {
+	     pending_optinfo info = optinfo_guard::ensure_pending ();
+	     [...add stuff to info...]
+          }
+
+     which effectively consolidates all dump calls into the same
+     optinfo within an optinfo_guard, but also handles the case
+     where there isn't an optinfo_guard (by falling back to putting
+     each one in a separate optinfo).  */
+
+  static pending_optinfo ensure_pending ()
+  {
+    if (pending)
+      {
+	return *pending;
+      }
+    else
+      {
+	return pending_optinfo (OPTINFO_IMPL_LOCATION, // FIXME
+				OPTINFO_KIND_NOTE,
+				optinfo_location ()); // FIXME
+      }
+  }
+
+ private:
+  static pending_optinfo *pending;
+  bool m_enabled;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/opts.c b/gcc/opts.c
index 33efcc0..8029c08 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
 	case CL_WARNING:
 	  description = _("The following options control compiler warning messages");
 	  break;
+	case CL_REMARK:
+	  description = _("The following options control compiler remarks");
+	  break;
 	case CL_OPTIMIZATION:
 	  description = _("The following options control optimizations");
 	  break;
@@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
 	      { "optimizers", CL_OPTIMIZATION },
 	      { "target", CL_TARGET },
 	      { "warnings", CL_WARNING },
+	      { "remarks", CL_REMARK },
 	      { "undocumented", CL_UNDOCUMENTED },
 	      { "params", CL_PARAMS },
 	      { "joined", CL_JOINED },
diff --git a/gcc/opts.h b/gcc/opts.h
index 3c4065ea..d9df788 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -137,20 +137,21 @@ extern const unsigned int cl_lang_count;
 #define CL_DRIVER		(1U << 19) /* Driver option.  */
 #define CL_TARGET		(1U << 20) /* Target-specific option.  */
 #define CL_COMMON		(1U << 21) /* Language-independent.  */
+#define CL_REMARK		(1U << 22) /* Enables an (optional) remark.  */
 
 #define CL_MIN_OPTION_CLASS	CL_PARAMS
-#define CL_MAX_OPTION_CLASS	CL_COMMON
+#define CL_MAX_OPTION_CLASS	CL_REMARK
 
 /* From here on the bits describe attributes of the options.
    Before this point the bits have described the class of the option.
    This distinction is important because --help will not list options
    which only have these higher bits set.  */
 
-#define CL_JOINED		(1U << 22) /* If takes joined argument.  */
-#define CL_SEPARATE		(1U << 23) /* If takes a separate argument.  */
-#define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
-#define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
-#define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_JOINED		(1U << 23) /* If takes joined argument.  */
+#define CL_SEPARATE		(1U << 24) /* If takes a separate argument.  */
+#define CL_UNDOCUMENTED		(1U << 25) /* Do not output with --help.  */
+#define CL_NO_DWARF_RECORD	(1U << 26) /* Do not add to producer string.  */
+#define CL_PCH_IGNORE		(1U << 27) /* Do compare state for pch.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
diff --git a/gcc/passes.c b/gcc/passes.c
index 832f0b3..7c064aa 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h" /* for fnotice */
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
 
 using namespace gcc;
 
@@ -1734,6 +1736,19 @@ execute_function_dump (function *fn, void *data)
     }
 }
 
+/* Helper function to perform function body dump.  */
+
+static void
+execute_optinfo_function_dump (function *fn, void *data)
+{
+  opt_pass *pass = (opt_pass *)data;
+
+  if (!optimization_records_enabled_p ())
+    return;
+
+  optimization_records_maybe_record_function (fn, pass);
+}
+
 /* This function is called when an internal compiler error is encountered.
    Ensure that function dump is made available before compiler is aborted.  */
 
@@ -2202,6 +2217,8 @@ execute_one_ipa_transform_pass (struct cgraph_node *node,
 
   if (dump_file)
     do_per_function (execute_function_dump, pass);
+  if (optinfo_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
   pass_fini_dump_file (pass);
 
   current_pass = NULL;
@@ -2503,6 +2520,9 @@ execute_one_pass (opt_pass *pass)
   else if (dump_file)
     do_per_function (execute_function_dump, pass);
 
+  if (optimization_records_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
+
   if (!current_function_decl)
     symtab->process_new_functions ();
 
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@ enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@ public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 36879cf..b74c94a 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -71,6 +71,7 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   json_cc_tests ();
+  optinfo_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 173700b..d20b636 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -199,6 +199,7 @@ extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void json_cc_tests ();
+extern void optinfo_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5a19fc9..1f0a079 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -96,6 +96,8 @@ set plugin_test_list [list \
 	  must-tail-call-2.c } \
     { expensive_selftests_plugin.c \
 	  expensive-selftests-1.c } \
+    { remarks_plugin.c \
+	  remarks-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
new file mode 100644
index 0000000..9139b9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-fremarks" } */
+
+extern void test_string_literal (void);
+extern void test_tree (void);
+extern void test_gimple (int);
+extern void test_cgraph_node (void);
+extern void test_printf (void);
+extern void test_wide_int (void);
+extern void test_poly_int (void);
+extern void test_scopes (void);
+
+void test_remarks (void)
+{
+  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
+  test_tree (); /* { dg-remark "test of tree: '0'" } */
+  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
+  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
+  test_printf (); /* { dg-remark "test of optinfo printf: 42" } */
+  test_wide_int (); /* { dg-remark "test of wide int: 0" } */
+  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
+
+  test_scopes (); /* { dg-line test_scopes_line } */
+  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
new file mode 100644
index 0000000..823339d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,159 @@
+/* Test of remark-emission by optinfo.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "optinfo.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cgraph.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_remarks =
+{
+  GIMPLE_PASS, /* type */
+  "test_remarks", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_remarks : public gimple_opt_pass
+{
+public:
+  pass_test_remarks(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_remarks, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_remarks
+
+unsigned int
+pass_test_remarks::execute (function *fun)
+{
+  basic_block bb;
+
+  if (!dump_enabled_p ())
+    return 0;
+  
+  FOR_ALL_BB_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+	 !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+	gimple *stmt = gsi_stmt (gsi);
+	gcall *call = dyn_cast <gcall *> (stmt);
+	if (!call)
+	  continue;
+	tree callee_decl = gimple_call_fndecl (call);
+	if (!callee_decl)
+	  continue;
+	tree callee_name = DECL_NAME (callee_decl);
+	if (!callee_name)
+	  continue;
+	const char *callee = IDENTIFIER_POINTER (callee_name);
+
+	/* Various optinfo tests, done at callsites,
+	   controlled by the callee name.  */
+	if (strcmp (callee, "test_string_literal") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of remark for ");
+	    dump_printf (MSG_NOTE, callee);
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of tree: ");
+	    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of gimple: ");
+	    dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of callgraph node: ");
+	    dump_symtab_node (MSG_NOTE, cgraph_node::get (callee_decl));
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of optinfo printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    HOST_WIDE_INT val = 0;
+	    dump_printf_loc (MSG_NOTE, stmt,
+			     "test of wide int: " HOST_WIDE_INT_PRINT_DEC,
+			     val);
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    optinfo_guard guard (stmt, OPTINFO_IMPL_LOCATION);
+	    dump_printf_loc (MSG_NOTE, stmt, "test of poly int: ");
+	    dump_dec (MSG_NOTE, poly_int64 (42));
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    OPTINFO_SCOPE ("outer scope", stmt);
+	    {
+	      dump_printf_loc (MSG_NOTE, stmt, "at outer scope");
+	      OPTINFO_SCOPE ("middle scope", stmt);
+	      {
+		dump_printf_loc (MSG_NOTE, stmt, "at middle scope");
+		OPTINFO_SCOPE ("innermost scope", stmt);
+		dump_printf_loc (MSG_NOTE, stmt, "at innermost scope");
+	      }
+	    }
+	  }
+      }
+
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_remarks (gcc::context *ctxt)
+{
+  return new pass_test_remarks (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  pass_info.pass = make_pass_test_remarks (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index a15c5d5..906ee3b 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1154,6 +1154,15 @@ proc dg-locus { args } {
     verbose "process-message:\n${dg-messages}" 2
 }
 
+# Handle remarks.
+
+proc dg-remark { args } {
+    # Make this variable available here and to the saved proc.
+    upvar dg-messages dg-messages
+
+    process-message saved-dg-error "remark: " "$args"
+}
+
 # Check the existence of a gdb in the path, and return true if there
 # is one.
 #
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d108096..a047390 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -487,6 +488,8 @@ compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2048,6 +2051,8 @@ do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
index c6e0a60..553b8ea 100644
--- a/gcc/tree-loop-distribution.c
+++ b/gcc/tree-loop-distribution.c
@@ -3117,7 +3117,7 @@ pass_loop_distribution::execute (function *fun)
 	    break;
 
 	  const char *str = loop->inner ? " nest" : "";
-	  location_t loc = find_loop_location (loop);
+	  optinfo_location loc = find_loop_location (loop);
 	  if (!cd)
 	    {
 	      calculate_dominance_info (CDI_DOMINATORS);
@@ -3145,8 +3145,8 @@ pass_loop_distribution::execute (function *fun)
 	      break;
 	    }
 
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, "Loop%s %d not distributed.\n", str, loop->num);
+	  dump_printf_loc (MSG_MISSED_OPTIMIZATION | TDF_DETAILS, loc,
+			   "Loop%s %d not distributed.\n", str, loop->num);
 	}
     }
 
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index f1557c9..2a12eef 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -3334,7 +3334,7 @@ lower_nested_functions (tree fndecl)
 
   gimplify_all_functions (cgn);
 
-  dump_file = dump_begin (TDI_nested, &dump_flags);
+  set_dump_file (dump_begin (TDI_nested, &dump_flags));
   if (dump_file)
     fprintf (dump_file, "\n;; Function %s\n\n",
 	     lang_hooks.decl_printable_name (fndecl, 2));
@@ -3361,7 +3361,7 @@ lower_nested_functions (tree fndecl)
   if (dump_file)
     {
       dump_end (TDI_nested, dump_file);
-      dump_file = NULL;
+      set_dump_file (NULL);
     }
 }
 
diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
index aa74427..a9d084b 100644
--- a/gcc/tree-parloops.c
+++ b/gcc/tree-parloops.c
@@ -3284,7 +3284,6 @@ parallelize_loops (bool oacc_kernels_p)
   struct tree_niter_desc niter_desc;
   struct obstack parloop_obstack;
   HOST_WIDE_INT estimated;
-  source_location loop_loc;
 
   /* Do not parallelize loops in the functions created by parallelization.  */
   if (!oacc_kernels_p
@@ -3409,7 +3408,7 @@ parallelize_loops (bool oacc_kernels_p)
       changed = true;
       skip_loop = loop->inner;
 
-      loop_loc = find_loop_location (loop);
+      optinfo_location loop_loc = find_loop_location (loop);
       if (loop->inner)
 	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loop_loc,
 			 "parallelizing outer loop %d\n", loop->num);
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 7447f7a..2623d9b 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 24bf60e..e85a6ad 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -691,7 +691,7 @@ try_unroll_loop_completely (struct loop *loop,
 			    edge exit, tree niter, bool may_be_zero,
 			    enum unroll_level ul,
 			    HOST_WIDE_INT maxiter,
-			    location_t locus, bool allow_peel)
+			    optinfo_location locus, bool allow_peel)
 {
   unsigned HOST_WIDE_INT n_unroll = 0;
   bool n_unroll_found = false;
@@ -1162,7 +1162,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
   tree niter;
   HOST_WIDE_INT maxiter;
   bool modified = false;
-  location_t locus = UNKNOWN_LOCATION;
+  optinfo_location locus;
   struct tree_niter_desc niter_desc;
   bool may_be_zero = false;
 
@@ -1177,7 +1177,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	= niter_desc.may_be_zero && !integer_zerop (niter_desc.may_be_zero);
     }
   if (TREE_CODE (niter) == INTEGER_CST)
-    locus = gimple_location (last_stmt (exit->src));
+    locus = last_stmt (exit->src);
   else
     {
       /* For non-constant niter fold may_be_zero into niter again.  */
@@ -1204,7 +1204,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	niter = find_loop_niter_by_eval (loop, &exit);
 
       if (exit)
-        locus = gimple_location (last_stmt (exit->src));
+        locus = last_stmt (exit->src);
 
       if (TREE_CODE (niter) != INTEGER_CST)
 	exit = NULL;
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index b313571..d80541c 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -7525,7 +7525,7 @@ tree_ssa_iv_optimize_loop (struct ivopts_data *data, struct loop *loop)
 
   gcc_assert (!data->niters);
   data->current_loop = loop;
-  data->loop_loc = find_loop_location (loop);
+  data->loop_loc = find_loop_location (loop).get_location_t ();
   data->speed = optimize_loop_for_speed_p (loop);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 7a54c5f..3aee0f6 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -2447,7 +2447,7 @@ number_of_iterations_exit (struct loop *loop, edge exit,
     return true;
 
   if (warn)
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
+    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
 		     "missed loop optimization: niters analysis ends up "
 		     "with assumptions.\n");
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 3d025c2..e5eddf9 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5866,8 +5866,7 @@ eliminate_dom_walker::before_dom_children (basic_block b)
 		    fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
 		  if (dump_enabled_p ())
 		    {
-		      location_t loc = gimple_location (stmt);
-		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				       "converting indirect call to "
 				       "function %s\n",
 				       lang_hooks.decl_printable_name (fn, 2));
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 77bd909..e0378ee 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -2723,7 +2723,7 @@ needs_fold_left_reduction_p (tree type, tree_code code,
    reduction operation CODE has a handled computation expression.  */
 
 bool
-check_reduction_path (location_t loc, loop_p loop, gphi *phi, tree loop_arg,
+check_reduction_path (optinfo_location loc, loop_p loop, gphi *phi, tree loop_arg,
 		      enum tree_code code)
 {
   auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 8ff90b3..4ebad5c 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -81,8 +81,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 
 
-/* Loop or bb location.  */
-source_location vect_location;
+/* Loop or bb location, with hotness information.  */
+optinfo_location vect_location;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> *stmt_vec_info_vec;
@@ -872,7 +872,7 @@ vectorize_loops (void)
 	  }
       }
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = optinfo_location ();
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
   if (dump_enabled_p ()
@@ -1207,7 +1207,7 @@ increase_alignment (void)
 {
   varpool_node *vnode;
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = optinfo_location ();
   type_align_map = new hash_map<tree, unsigned>;
 
   /* Increase the alignment of all global arrays for vectorization.  */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 2255d96..8fa86ef 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-data-ref.h"
 #include "tree-hash-traits.h"
 #include "target.h"
+#include "optinfo.h"
 
 /* Used for naming of new temporaries.  */
 enum vect_var_kind {
@@ -1422,18 +1423,22 @@ vect_get_scalar_dr_size (struct data_reference *dr)
   return tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr))));
 }
 
-/* Source location */
-extern source_location vect_location;
+/* Source location + hotness information. */
 
-/* If dumping is enabled, emit a MSG_NOTE at vect_location about
-   entering MSG within the vectorizer.  MSG should be a string literal. */
+extern GTY(()) optinfo_location vect_location;
+
+/* A macro for emitting an optinfo note about entering a scope at
+   vect_location, pushing and popping the scope, so that all optinfos
+   "within" the scope are nested within it.  */
 
 #define VECT_SCOPE(MSG) \
-  do {						\
-    if (dump_enabled_p ())			\
-      dump_printf_loc (MSG_NOTE, vect_location, \
-		       "=== " MSG " ===\n");	\
-  } while (0)
+  OPTINFO_SCOPE (MSG, vect_location)
+
+/* A conditional, using RAII to consolidate all dump_ calls within the
+   suite so that a single optimization record is created, at vect_location.  */
+
+#define IF_VECT_DUMP \
+  if (optinfo_guard guard = optinfo_guard (vect_location, OPTINFO_IMPL_LOCATION))
 
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
@@ -1451,7 +1456,7 @@ extern void vect_loop_versioning (loop_vec_info, unsigned int, bool,
 extern struct loop *vect_do_peeling (loop_vec_info, tree, tree,
 				     tree *, tree *, tree *, int, bool, bool);
 extern void vect_prepare_for_masked_peels (loop_vec_info);
-extern source_location find_loop_location (struct loop *);
+extern optinfo_location find_loop_location (struct loop *);
 extern bool vect_can_advance_ivs_p (loop_vec_info);
 
 /* In tree-vect-stmts.c.  */
@@ -1567,7 +1572,7 @@ extern tree vect_create_addr_base_for_vector_ref (gimple *, gimple_seq *,
 extern gimple *vect_force_simple_reduction (loop_vec_info, gimple *,
 					    bool *, bool);
 /* Used in gimple-loop-interchange.c.  */
-extern bool check_reduction_path (location_t, loop_p, gphi *, tree,
+extern bool check_reduction_path (optinfo_location, loop_p, gphi *, tree,
 				  enum tree_code);
 /* Drive for loop analysis stage.  */
 extern loop_vec_info vect_analyze_loop (struct loop *, loop_vec_info);
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index d50a179..9ca0003 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1271,13 +1271,11 @@ find_func_by_profile_id (int profile_id)
 bool
 check_ic_target (gcall *call_stmt, struct cgraph_node *target)
 {
-   location_t locus;
    if (gimple_check_call_matching_types (call_stmt, target->decl, true))
      return true;
 
-   locus =  gimple_location (call_stmt);
    if (dump_enabled_p ())
-     dump_printf_loc (MSG_MISSED_OPTIMIZATION, locus,
+     dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                       "Skipping target %s with mismatching types for icall\n",
                       target->name ());
    return false;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 0/8] v2 of optimization records patch kit
  2018-06-04 13:20           ` Richard Biener
@ 2018-06-14 19:50             ` David Malcolm
  2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
                                 ` (7 more replies)
  2018-06-20 16:35             ` [PATCH] v3 " David Malcolm
  1 sibling, 8 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

On Mon, 2018-06-04 at 15:20 +0200, Richard Biener wrote:
> On Sat, Jun 2, 2018 at 12:22 AM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > On Fri, 2018-06-01 at 17:31 +0200, Richard Biener wrote:
> > > On June 1, 2018 3:40:15 PM GMT+02:00, David Malcolm <dmalcolm@red
> > > hat.
> > > com> wrote:
> > > > On Fri, 2018-06-01 at 11:50 +0200, Richard Biener wrote:
> > > > > On Tue, May 29, 2018 at 10:33 PM David Malcolm <dmalcolm@redh
> > > > > at.c
> > > > > om>
> > > > > wrote:
> > > > > > 
> > > > > > This was an experiment to try to capture information on a
> > > > > > loop optimization.
> > > > > > 
> > > > > > gcc/ChangeLog:
> > > > > >         * gimple-loop-interchange.cc
> > > > > > (should_interchange_loops):
> > > > > > Add
> > > > > >         optinfo note when interchange gives better data
> > > > > > locality
> > > > > > behavior.
> > > > > >         (tree_loop_interchange::interchange): Add
> > > > > > OPTINFO_SCOPE.
> > > > > >         Add optinfo for successful and unsuccessful
> > > > > > interchanges.
> > > > > >         (prepare_perfect_loop_nest): Add
> > > > > > OPTINFO_SCOPE.  Add
> > > > > >         optinfo note.
> > > > > >         (pass_linterchange::execute): Add OPTINFO_SCOPE.
> > > > > > ---
> > > > > >  gcc/gimple-loop-interchange.cc | 36
> > > > > > +++++++++++++++++++++++++++++++++++-
> > > > > >  1 file changed, 35 insertions(+), 1 deletion(-)
> > > > > > 
> > > > > > diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-
> > > > > > loop-
> > > > > > interchange.cc
> > > > > > index eb35263..cd32288 100644
> > > > > > --- a/gcc/gimple-loop-interchange.cc
> > > > > > +++ b/gcc/gimple-loop-interchange.cc
> > > > > > @@ -1556,7 +1556,19 @@ should_interchange_loops (unsigned
> > > > > > i_idx,
> > > > > > unsigned o_idx,
> > > > > >    ratio = innermost_loops_p ? INNER_STRIDE_RATIO :
> > > > > > OUTER_STRIDE_RATIO;
> > > > > >    /* Do interchange if it gives better data locality
> > > > > > behavior.  */
> > > > > >    if (wi::gtu_p (iloop_strides, wi::mul (oloop_strides,
> > > > > > ratio)))
> > > > > > -    return true;
> > > > > > +    {
> > > > > > +      if (optinfo_enabled_p ())
> > > > > > +       OPTINFO_NOTE ((gimple *)NULL) // FIXME
> > > > > > +         << "interchange gives better data locality
> > > > > > behavior:
> > > > > > "
> > > > > > +         << "iloop_strides: "
> > > > > > +         << decu (iloop_strides)
> > > > > > +         << " > (oloop_strides: "
> > > > > > +         << decu (oloop_strides)
> > > > > > +         << " * ratio: "
> > > > > > +         << decu (ratio)
> > > > > > +         << ")";
> > > > > 
> > > > > Just randomly inside the thread.
> > > > > 
> > > > > NOOOOOOOOOO!
> > > > > 
> > > > > :/
> > > > > Please do _not_ add more stream-like APIs.  How do you expect
> > > > > translators to deal with those?
> > > > > 
> > > > > Yes, I'm aware of the graphite-* ones and I dislike those
> > > > > very
> > > > > much.
> > > > > 
> > > > > What's wrong with the existing dump API?
> > > > 
> > > > The existing API suffers from a "wall of text" problem:
> > > > 
> > > > * although it's possible to filter based on various criteria
> > > > (the
> > > > optgroup tags, specific passes, success vs failure), it's not
> > > > possible
> > > > to filter base on code hotness: the -fopt-info API works purely
> > > > in
> > > > terms of location_t.  So all of the messages about the hottest
> > > > functions in the workload are intermingled with all of the
> > > > other
> > > > messages about all of the other functions.
> > > 
> > > True
> > > 
> > > > * some of the text notes refer to function entry, but all of
> > > > these
> > > > are
> > > > emitted "at the same level": there's no way to see the nesting
> > > > of
> > > > these
> > > > function-entry logs, and where other notes are in relation to
> > > > them.
> > > > For example, in:
> > > > 
> > > >  test.c:8:3: note: === analyzing loop ===
> > > >  test.c:8:3: note: === analyze_loop_nest ===
> > > >  test.c:8:3: note: === vect_analyze_loop_form ===
> > > >  test.c:8:3: note: === get_loop_niters ===
> > > > test.c:8:3: note: symbolic number of iterations is (unsigned
> > > > int)
> > > > n_9(D)
> > > > test.c:8:3: note: not vectorized: loop contains function calls
> > > > or
> > > > data
> > > > references that cannot be analyzed
> > > >  test.c:8:3: note: vectorized 0 loops in function
> > > > 
> > > > there's no way to tell that the "vect_analyze_loop_form" is in
> > > > fact
> > > > inside the call to "analyze_loop_nest", and where the "symbolic
> > > > number
> > > > of iterations" messages is coming from in relation to
> > > > them.  This
> > > > may
> > > > not seem significant here, but I'm quoting a small example;
> > > > vectorization typically leads to dozens of messages, with a
> > > > deep
> > > > nesting structure (where that structure isn't visible in the
> > > > -fopt-
> > > > info
> > > > 
> > > > output).
> > > 
> > > True. The same issue exists for diagnostics BTW. Indeed, being
> > > able
> > > to collapse 'sections' in dump files, opt-info or diagnostics
> > > sounds
> > > useful.
> > > 
> > > Note that for dump files and opt-info the level argument was sort
> > > of
> > > designed to do that.
> > 
> > Are you referring to the indentation argument here?
> 
> No, to MSG_NOTE vs. MSG_MISSED_OPTIMIZATION , etc.
> 
> > > > 
> > > > The existing API is throwing data away:
> > > > 
> > > > * as noted above, by working purely with a location_t, the
> > > > execution
> > > > count isn't associated with the messages.  The output format
> > > > purely
> > > > gives file/line/column information, but doesn't cover the
> > > > inlining
> > > > chain.   For C++ templates it doesn't specify which instance of
> > > > a
> > > > template is being optimized.
> > > 
> > > It might be useful to enhance the interface by allowing different
> > > kind of 'locations'.
> > 
> > In patch 3 of the kit there's a class optinfo_location, which can
> > be
> > constructed from:
> >   * a gimple *, or
> >   * a basic_block, or
> >   * a loop *
> > Hence you can pass in any of the above when an optinfo_location is
> > required.  Potentially there could also be a constructor taking an
> > rtx_insn * (though I'm primarily interested in gimple passes here,
> > especially inlining and vectorization).
> > 
> > Internally it's currently stored as a gimple *, but I guess it
> > could be
> > , say, a basic_block and a location_t.
> 
> Ok, so that indeed sounds what I had in mind - change the dump_*_loc
> interface to take this kind of class rather than (only) a location_t.
> 
> > > > * there's no metadata about where the messages are coming
> > > > from.  It's
> > > > easy to get at the current pass internally, but the messages
> > > > don't
> > > > capture that.  Figuring out where a message came from requires
> > > > grepping
> > > > the GCC source code.  The prototype I posted captures the
> > > > __FILE__
> > > > and
> > > > __LINE__ within the gcc source for every message emitted, and
> > > > which
> > > > pass instance emitted it.
> > > 
> > > The opt info group was supposed to captures this to the level
> > > interesting for a user.
> > 
> > A question here is who the "user" is.  I'm aiming this both at GCC
> > developers, and technically-sophisticated end-users.  As a member
> > of
> > the former category, I'd love to have an easy way to go from a
> > message
> > to the line of code that emitted it.
> > 
> > The optinfo group seems to be *very* high level: "is this a "loop"
> > message, or a "vec" message?" etc.  That is useful too (one of the
> > good
> > things about the existing system).
> 
> It somewhat mimics what other compilers offer.  Then there's the
> level
> from above but it is very coarse-grained and mixing things like
> detail (MSG_NOTE) with kind (OPTIMIZED_LOCATIONS vs. UNOPTIMIZED
> one).
> 
> So what would be useful I guess is to somehow split this so we can
> have
> a level MSG_ANALYSIS (and drop MSG_UNOPTIMIZED_LOCATIONS?) and
> a separate detail level.  So we could start information about an
> analysis
> phase (thus grouping related info), dump what failed, dump details.
> 
> The hard part here is of course taking existing verbose dumps and try
> to
> turn them into sth usable for users.  The vectorizer dumpfile is not
> really informative unless you look at the source and mix&match the
> dumping code with the surroundings...
> 
> > > > * The current output format is of the form:
> > > >     "FILE:LINE:COLUMN: free-form text\n"
> > > > This is only machine-readable up to a point: if a program is
> > > > parsing
> > > > it, all it has is the free-form text.  The prototype I posted
> > > > captures
> > > > what kinds of things are in the text (statements, trees,
> > > > symtab_nodes),
> > > > and captures location information for them, so that
> > > > visualizations
> > > > of
> > > > the dumps can provide useful links.
> > > > 
> > > > There's no API-level grouping of messages, beyond looking for
> > > > newline
> > > > characters.
> > > > 
> > > > I'm probably re-hashing a lot of the material in the cover
> > > > letter
> > > > here:
> > > > "[PATCH 00/10] RFC: Prototype of compiler-assisted performance
> > > > analysis"
> > > >  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01675.html
> > > > 
> > > > 
> > > > I'd like to provide a machine-readable output format that
> > > > covers
> > > > the
> > > > above - in particular the profile data (whilst retaining -fopt-
> > > > info
> > > > for
> > > > compatibility).  Separation of the data from its presentation.
> > > > 
> > > > Clearly you don't like the stream-like API from the prototype
> > > > :)
> > > 
> > > Yes :) I wasn't so much complaining about the content but the
> > > presentation /API.
> > 
> > (nods)
> > 
> > > > So I'm wondering what the API ought to look like, one that
> > > > would
> > > > allow
> > > > for the kind of "richer" machine-readable output.
> > > > 
> > > > Consider this "-fopt-info" code (from
> > > > "vect_create_data_ref_ptr";
> > > > this
> > > > is example 2 from the cover-letter):
> > > > 
> > > >  if (dump_enabled_p ())
> > > >     {
> > > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > > >       dump_printf_loc (MSG_NOTE, vect_location,
> > > >                        "create %s-pointer variable to type: ",
> > > >                        get_tree_code_name (TREE_CODE
> > > > (aggr_type)));
> > > >       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
> > > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > > >         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
> > > >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > > >         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
> > > >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > > >    dump_printf (MSG_NOTE, "  vectorizing a record based array
> > > > ref:
> > > > ");
> > > >       else
> > > >         dump_printf (MSG_NOTE, "  vectorizing a pointer ref:
> > > > ");
> > > >       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT
> > > > (dr));
> > > >       dump_printf (MSG_NOTE, "\n");
> > > >     }
> > > > 
> > > > where the information is built up piecewise, with conditional
> > > > logic.
> > > > (This existing code isn't particularly amenable to translation
> > > > either).
> > > > 
> > > > One option would be for "vect_location" to become something
> > > > other
> > > > than
> > > > a location_t, from which a execution count can be gained (e.g.
> > > > a
> > > > gimple
> > > > *, from which the location_t and the BB can be accessed).
> > > 
> > > Yes. Like a container that can be initiated from other kind of
> > > contexts.
> > 
> > (like the optinfo_location class described above).
> 
> Yes.
> 
> > So this might be something like:
> > 
> > extern void dump_printf_loc (optinfo_groups_t,
> >                              optinfo_location,
> >                              const char *fmt,
> >                              ...);
> > 
> > or somesuch.
> 
> Yeah.
> 
> > >   Then
> > > > "dump_printf_loc" would signify starting an optimization
> > > > record,
> > > > and
> > > > the final "\n" would signify the end of an optimization record;
> > > > the
> > > > various dump_generic_expr and dump_printf would add structured
> > > > information and text entries to theoptimization record.
> > > 
> > > A push/pop style API would maybe work as well. (pushing a level
> > > with
> > > some meta data)
> > 
> > Patch 3 in the kit has an OPTINFO_SCOPE macro which uses a RAII
> > class
> > to automatically do push/pops.
> > 
> > > > This has the advantage of retaining the look of the existing
> > > > API
> > > > (so
> > > > the existing callsites don't need changing), but it changes
> > > > their
> > > > meaning, so that as well as writing to -fopt-info's
> > > > destinations,
> > > > it's
> > > > also in a sense parsing the dump_* calls and building
> > > > optimization
> > > > records from them.
> > > > 
> > > > AIUI, dump_printf_loc can't be a macro, as it's variadic, so
> > > > this
> > > > approach doesn't allow for capturing the location within GCC
> > > > for
> > > > where
> > > > the message is emitted.
> > > 
> > > True, though we have __builtin_FILE and friends that can be used
> > > as
> > > default args.
> > 
> > If I'm understanding the idea, this means relying on implicit use
> > of
> > default arguments.   I'm not sure how compatible that is with
> > variadic
> > functions: the ellipsis has to come last.
> 
> True.
> 
> > I thought it was impossible to have default args with a variadic
> > function, but it seems that this is syntactically valid:
> > 
> > extern void dump_printf_loc (optinfo_groups_t,
> >                              optinfo_location,
> >                              const char *fmt,
> >                              const char *impl_file = __builtin_FILE
> > (),
> >                              int impl_line = __builtin_LINE (),
> >                              ...);
> > 
> > That said, the above decl seems like a bad idea: a recipe for nasty
> > surprises (what happens if the format arguments expect a const char
> > *
> > and an int?).
> 
> I'm surprised the above works ;)  Does it do what we expect?
> 
> > Idea: maybe the optinfo_location constructor could take the default
> > args for a __builtin_FILE and __builtin_LINE?  Or the
> > pending_optinfo
> > class from patch 3 of the kit.  I'll experiment with this.
> 
> Ah, nice idea, yes.
> 
> > > Note a push/pop level API can also buffer intermediate printf
> > > style
> > > output and annotate/indent it (supporting a dump file emacs/vim
> > > mode
> > > that can do collapse and expand)
> > 
> > Interesting idea.
> > 
> > I had a go at emitting text in Emacs outline-mode format.  An
> > example,
> > showing the source location and pass/hotness metadata only for top-
> > level messages:
> >  https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-elided.tx
> > t
> > and another example, showing them for all messages:
> >  https://dmalcolm.fedorapeople.org/gcc/2018-06-01/outline-unelided.
> > txt
> > 
> > and it works as-is with Emacs outline-mode (though I don't know how
> > useful it is; would want a jump-to-source option etc etc).
> > 
> > In both cases, this is prioritizing the messages, sorting from
> > hottest
> > code down to coldest code (or those messages emitted before the
> > profile
> > data was loaded), similar to the HTML report here:
> > https://dmalcolm.fedorapeople.org/gcc/2018-05-18/pgo-demo-test/pgo-
> > demo-test/
> > 
> > (These are all being generated by reading the saved optimization
> > records, rather than being emitted by the compiler itself)
> > 
> > > > Another approach: there could be something like:
> > > > 
> > > >  if (optinfo_enabled_p ())
> > > >    {
> > > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > > >       OPTINFO_VECT_NOTE note;
> > > >       note.printf ("create %s-pointer variable to type: ",
> > > >                    get_tree_code_name (TREE_CODE (aggr_type)));
> > > >       note.add_slim (aggr_type);
> > > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > > >         note.printf ("  vectorizing an array ref: ");
> > > >       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > > >         note.printf (MSG_NOTE, "  vectorizing a vector ref: ");
> > > >       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > > >    note.printf (MSG_NOTE, "  vectorizing a record based array
> > > > ref:
> > > > ");
> > > >       else
> > > >         note.printf (MSG_NOTE, "  vectorizing a pointer ref:
> > > > ");
> > > >       note.add_slim (DR_BASE_OBJECT (dr));
> > > >    }
> > > > 
> > > > which uses a macro to get the gcc source location, and avoids
> > > > the
> > > > special meaning for "\n".  This avoids the "<<" - but is kind
> > > > of
> > > > just
> > > > different syntax for the same - it's hard with this example to
> > > > avoid
> > > > it.
> > > 
> > > Maybe the following raii style that encapsulates the enabling
> > > /disabling checks?
> > > 
> > >  If (optinfo o = push (msg_optimization,...))
> > >   {
> > >      O.print (...) ;
> > >      Destructor of o 'pops'
> > >   }
> > 
> > I'm assuming that:
> > * very few users turn on the dump feature, and
> > * a goal is that we shouldn't slow down that common "no dumping"
> > case
> > when implementing dumping.
> 
> Yes.
> 
> > If so, then presumably we need a really cheap test that can easily
> > be
> > marked as cold, or optimized away.
> > 
> > How much work would be done by the call to:
> >    push (msg_optimization,...),
> > in particular evaluating the arguments?
> 
> I'd expect it to bail out quickly when !enabled()
> 
> > I was thinking:
> >    if (optinfo_enabled_p ())
> > and have it be a very cheap boolean lookup (though it isn't in the
> > current patch kit), with everything else guarded by it.
> 
> Yeah.
> 
> > Would a GCC_UNLIKELY(expr) macro be appropriate here?  (so that
> > non-PGO
> > builds of the compiler can have all this dump stuff moved into cold
> > sections)
> 
> Not sure, we could certainly experiment with that.
> 
> > > > Yet another approach: reworking things to support i18n via a
> > > > pure
> > > > printf-style interface, using, say "%S" to mean "TDF_SLIM", it
> > > > could be
> > > > like:
> > > > 
> > > >  if (optinfo_enabled_p ())
> > > >    {
> > > >       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
> > > >       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
> > > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type:
> > > > %S"
> > > >                            "  vectorizing an array ref: %S",
> > > >                            get_tree_code_name (TREE_CODE
> > > > (aggr_type))
> > > >                         aggr_type,
> > > >                            DR_BASE_OBJECT (dr));
> > > >      else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
> > > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type:
> > > > %S"
> > > >                            "  vectorizing a vector ref: %S",
> > > >                            get_tree_code_name (TREE_CODE
> > > > (aggr_type))
> > > >                         aggr_type,
> > > >                            DR_BASE_OBJECT (dr));
> > > >      else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
> > > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type:
> > > > %S"
> > > >                          "  vectorizing a record based array
> > > > ref:
> > > > %S",
> > > >                            get_tree_code_name (TREE_CODE
> > > > (aggr_type))
> > > >                         aggr_type,
> > > >                            DR_BASE_OBJECT (dr));
> > > >      else
> > > >         OPTINFO_VECT_NOTE ("create %s-pointer variable to type:
> > > > %S"
> > > >                            "  vectorizing a pointer ref: %S",
> > > >                            get_tree_code_name (TREE_CODE
> > > > (aggr_type))
> > > >                         aggr_type,
> > > >                            DR_BASE_OBJECT (dr));
> > > >    }
> > > > 
> > > > or somesuch.  The %S would allow for the data to be captured
> > > > when
> > > > emitting optimization records for the messages (not just plain
> > > > text,
> > > > e.g. locations of the expressions).
> > > 
> > > Certainly when exposing things in opt-info we have to be more
> > > disciplined. The vectorizer is a bad example here.
> > > The original goal of having one set of outputs for both dump
> > > files
> > > and opt-info is good but I guess the result is less than optimal.
> > > Maybe additional detail levels would help here
> > > (MSG_Dumpfile_only?)
> > > 
> > > > So those are some ideas.  I'm sure there are other ways to fix
> > > > the
> > > > issues with -fopt-info; I'm brainstorming here.
> > > 
> > > Likewise. As said I applaud the attempt improve the situation but
> > > I
> > > detest a stream API ;)
> > 
> > Thanks for the feedback.  I'll continue to try prototyping ideas.
> 
> Thanks a lot.
> 
> Note I think we can get incremental improvements, like committing
> the enum change and a wrapper class for the location while keeping
> the rest of the opt-info API.
> 
> Richard.

Here's an updated version of the patch kit (v2); it assumes the json
patch from v1 of the kit:
  "[PATCH 02/10] Add JSON implementation"
    https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01676.html

I got rid of the unloved streaming "operator<<" API; instead the
optimization records and remarks are constructed from the existing
"dump_*" calls.

I experimented with using an RAII class for consolidating related
"dump_*" calls into single optimization records:

    if (optinfo_guard guard = optinfo_guard (location, OPTINFO_IMPL_LOCATION))
      {
        SUITE
      }

so that all of the dump_* calls within SUITE are consolidated
into a single optimization record.

This works, but I don't like the approach, as it means e.g.
converting all of the:

    if (dump_enabled_p ())

in the vectorization code to:

    IF_VECT_DUMP

and will require hand-coded guards elsewhere; patch 8 of the kit shows
the true horror here.

But I thought it was worth capturing what was needed here.

In v3 of the patch kit (to be written) I plan to eliminate this, in
favor of the dump_*_loc calls being delimiters that introduce a new
optimization record, and various other calls signifying the end of
an optimization record (e.g. the emission of a diagnostic, or the
closing of an optinfo_scope).

There are some more detailed notes in patch 3 within the kit.

I haven't attempted to bootstrap this yet (and it still relies on
C++11 in a few places), but I thought it was best to post this
work-in-progress.

Hopefully I'm on the right track here.  As before, an HTML
visualization of some optimization records emitted via this patch
kit can be seen here:

  https://dmalcolm.fedorapeople.org/gcc/2018-06-14/pgo-demo-test/

(though it looks a lot like the last demo; it's mostly internally
that things have changed).

Thoughts?

David Malcolm (8):
  Add GCC_LIKELY and GCC_UNLIKELY
  Introduce VECT_SCOPE macro
  v2 of optinfo, remarks and optimization records
  tree-vect-loop.c: use MSG_OPTIMIZED_LOCATIONS in a few places
  gimple-loop-interchange.cc: use the dump API in a few places
  ipa-inline.c/tree-inline.c: port from fprintf to dump API
  tree-ssa-loop-im.c port from fprintf to the dump API
  Add lots of pointless churn to tree-vect-*.c

 gcc/Makefile.in                              |   5 +
 gcc/cgraph.c                                 |   6 +-
 gcc/cgraphunit.c                             |   3 +-
 gcc/common.opt                               |   9 +
 gcc/coretypes.h                              |  15 +
 gcc/coverage.c                               |   2 +-
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 +
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  34 +-
 gcc/dumpfile.c                               | 241 +++++++++--
 gcc/dumpfile.h                               |  49 ++-
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gengtype.c                               |   3 +-
 gcc/gimple-fold.c                            |   6 +-
 gcc/gimple-loop-interchange.cc               |  32 +-
 gcc/gimple-pretty-print.c                    |   2 +-
 gcc/gimple-pretty-print.h                    |   2 +
 gcc/graphite-isl-ast-to-gimple.c             |   4 +-
 gcc/graphite-optimize-isl.c                  |   4 +-
 gcc/graphite-scop-detection.c                |   2 +-
 gcc/graphite.c                               |   2 +-
 gcc/ipa-devirt.c                             |   3 +-
 gcc/ipa-inline.c                             |  57 ++-
 gcc/ipa-prop.c                               |  10 +-
 gcc/ipa.c                                    |   9 +-
 gcc/omp-grid.c                               |  47 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 141 ++++++
 gcc/optinfo-emit-diagnostics.h               |  26 ++
 gcc/optinfo-emit-json.cc                     | 617 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  39 ++
 gcc/optinfo-internal.h                       | 145 +++++++
 gcc/optinfo.cc                               | 298 +++++++++++++
 gcc/optinfo.h                                | 298 +++++++++++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/passes.c                                 |  20 +
 gcc/profile-count.c                          |  28 ++
 gcc/profile-count.h                          |   5 +
 gcc/selftest-run-tests.c                     |   1 +
 gcc/selftest.h                               |   1 +
 gcc/system.h                                 |  12 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 ++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 159 +++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-inline.c                            |  18 +-
 gcc/tree-loop-distribution.c                 |   6 +-
 gcc/tree-nested.c                            |   4 +-
 gcc/tree-parloops.c                          |   3 +-
 gcc/tree-ssa-live.c                          |   4 +-
 gcc/tree-ssa-loop-im.c                       |  26 +-
 gcc/tree-ssa-loop-ivcanon.c                  |   8 +-
 gcc/tree-ssa-loop-ivopts.c                   |   2 +-
 gcc/tree-ssa-loop-niter.c                    |   2 +-
 gcc/tree-ssa-sccvn.c                         |   3 +-
 gcc/tree-vect-data-refs.c                    | 271 ++++++------
 gcc/tree-vect-loop-manip.c                   |  69 ++-
 gcc/tree-vect-loop.c                         | 411 +++++++++---------
 gcc/tree-vect-patterns.c                     |  60 ++-
 gcc/tree-vect-slp.c                          | 145 +++----
 gcc/tree-vect-stmts.c                        | 381 ++++++++---------
 gcc/tree-vectorizer.c                        |  30 +-
 gcc/tree-vectorizer.h                        |  23 +-
 gcc/value-prof.c                             |   4 +-
 68 files changed, 3011 insertions(+), 883 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 6/8] ipa-inline.c/tree-inline.c: port from fprintf to dump API
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
                                 ` (2 preceding siblings ...)
  2018-06-14 19:50               ` [PATCH 4/8] tree-vect-loop.c: use MSG_OPTIMIZED_LOCATIONS " David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-14 19:50               ` [PATCH 7/8] tree-ssa-loop-im.c port from fprintf to the " David Malcolm
                                 ` (3 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

This patch ports from fprintf to using the dump API in a few places

Doing so makes this information appear in -fopt-info, remarks,
and optimization records, rather than just in the dump_file.

In early_inline_small_functions I experimented with two approaches:
simply porting from fprintf to dump_printf_loc, and also breaking
things out into dump_symtab_node calls.  The results are identical
for the dump file and for -fopt-info.  For remarks, the node names
are quoted in the diagnostic output; for optimization records, the
fact that they are symtab nodes is captured, along with metadata
such as the locations of the functions.  Though this approach is
less amenable to translation (if we want to translate these messages).

gcc/ChangeLog:
	* ipa-inline.c: Include "optinfo.h".
	(report_inline_failed_reason): Port from fprintf to dump API.
	(flatten_function): Likewise.
	(early_inline_small_functions): Likewise, experimenting with two
	approaches.
	* tree-inline.c (expand_call_inline): Port from fprintf to dump
	API.
---
 gcc/ipa-inline.c  | 57 +++++++++++++++++++++++++++++++++++++++----------------
 gcc/tree-inline.c | 18 +++++++++++-------
 2 files changed, 52 insertions(+), 23 deletions(-)

diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 7e4468a..1aeaa95 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -120,6 +120,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "optinfo.h"
 
 typedef fibonacci_heap <sreal, cgraph_edge> edge_heap_t;
 typedef fibonacci_node <sreal, cgraph_edge> edge_heap_node_t;
@@ -227,20 +228,21 @@ caller_growth_limits (struct cgraph_edge *e)
 static void
 report_inline_failed_reason (struct cgraph_edge *e)
 {
-  if (dump_file)
+  if (dump_enabled_p ())
     {
-      fprintf (dump_file, "  not inlinable: %s -> %s, %s\n",
-	       e->caller->dump_name (),
-	       e->callee->dump_name (),
-	       cgraph_inline_failed_string (e->inline_failed));
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, e->call_stmt,
+		       "  not inlinable: %s -> %s, %s\n",
+		       e->caller->dump_name (),
+		       e->callee->dump_name (),
+		       cgraph_inline_failed_string (e->inline_failed));
       if ((e->inline_failed == CIF_TARGET_OPTION_MISMATCH
 	   || e->inline_failed == CIF_OPTIMIZATION_MISMATCH)
 	  && e->caller->lto_file_data
 	  && e->callee->ultimate_alias_target ()->lto_file_data)
 	{
-	  fprintf (dump_file, "  LTO objects: %s, %s\n",
-		   e->caller->lto_file_data->file_name,
-		   e->callee->ultimate_alias_target ()->lto_file_data->file_name);
+	  dump_printf (MSG_MISSED_OPTIMIZATION, "  LTO objects: %s, %s\n",
+		       e->caller->lto_file_data->file_name,
+		       e->callee->ultimate_alias_target ()->lto_file_data->file_name);
 	}
       if (e->inline_failed == CIF_TARGET_OPTION_MISMATCH)
 	cl_target_option_print_diff
@@ -2172,10 +2174,11 @@ flatten_function (struct cgraph_node *node, bool early)
 
       /* Inline the edge and flatten the inline clone.  Avoid
          recursing through the original node if the node was cloned.  */
-      if (dump_file)
-	fprintf (dump_file, " Inlining %s into %s.\n",
-		 xstrdup_for_dump (callee->name ()),
-		 xstrdup_for_dump (e->caller->name ()));
+      if (dump_enabled_p ())
+	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
+			 " Inlining %s into %s.\n",
+			 xstrdup_for_dump (callee->name ()),
+			 xstrdup_for_dump (e->caller->name ()));
       orig_callee = callee;
       inline_call (e, true, NULL, NULL, false);
       if (e->callee != orig_callee)
@@ -2682,10 +2685,32 @@ early_inline_small_functions (struct cgraph_node *node)
       if (!want_early_inline_function_p (e))
 	continue;
 
-      if (dump_file)
-	fprintf (dump_file, " Inlining %s into %s.\n",
-		 xstrdup_for_dump (callee->name ()),
-		 xstrdup_for_dump (e->caller->name ()));
+      /* FIXME:  if (dump_enabled_p ()) would be simpler.  */
+      if (optinfo_guard guard = optinfo_guard (e->call_stmt,
+					       OPTINFO_IMPL_LOCATION))
+	{
+	  if (1)
+	    {
+	      /* Use of dump_symtab_node, which thus captures the nodes
+		 in the optimization record.  */
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
+			       " Inlining ");
+	      dump_symtab_node (MSG_OPTIMIZED_LOCATIONS, callee);
+	      dump_printf (MSG_OPTIMIZED_LOCATIONS,
+			       " into ");
+	      dump_symtab_node (MSG_OPTIMIZED_LOCATIONS, e->caller);
+	      dump_printf (MSG_OPTIMIZED_LOCATIONS, ".\n");
+	    }
+	  else
+	    {
+	      /* printf-style printing, which doesn't capture the
+		 symtab_nodes themselves.  */
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
+			       " Inlining %s into %s.\n",
+			       xstrdup_for_dump (callee->name ()),
+			       xstrdup_for_dump (e->caller->name ()));
+	    }
+	}
       inline_call (e, true, NULL, NULL, false);
       inlined = true;
     }
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index ca4dc32..45027dc 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -4627,14 +4627,18 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id)
   /* Add local vars in this inlined callee to caller.  */
   add_local_variables (id->src_cfun, cfun, id);
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
+  if (dump_enabled_p ())
     {
-      fprintf (dump_file, "Inlining %s to %s with frequency %4.2f\n",
-	       id->src_node->dump_name (),
-	       id->dst_node->dump_name (),
-	       cg_edge->sreal_frequency ().to_double ());
-      id->src_node->dump (dump_file);
-      id->dst_node->dump (dump_file);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, call_stmt,
+		       "Inlining %s to %s with frequency %4.2f\n",
+		       id->src_node->dump_name (),
+		       id->dst_node->dump_name (),
+		       cg_edge->sreal_frequency ().to_double ());
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  id->src_node->dump (dump_file);
+	  id->dst_node->dump (dump_file);
+	}
     }
 
   /* This is it.  Duplicate the callee body.  Assume callee is
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-15  6:10                 ` Alexander Monakov
  2018-06-15 16:31                 ` Jeff Law
  2018-06-14 19:50               ` [PATCH 5/8] gimple-loop-interchange.cc: use the dump API in a few places David Malcolm
                                 ` (6 subsequent siblings)
  7 siblings, 2 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

The idea is to later use these macros to mark the
  if (dump_enabled_p ())
parts of the compiler as cold, in the hope of helping non-PGO builds
of gcc.

I haven't measured it yet, though.

gcc/ChangeLog:
	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros, adapted from
	libgfortran.h.
---
 gcc/system.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/gcc/system.h b/gcc/system.h
index 88dffcc..195acb8 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -720,6 +720,18 @@ extern int vsnprintf (char *, size_t, const char *, va_list);
 #define __builtin_expect(a, b) (a)
 #endif
 
+/* The following macros can be used to annotate conditions which are likely or
+   unlikely to be true.  Avoid using them when a condition is only slightly
+   more likely/less unlikely than average to avoid the performance penalties of
+   branch misprediction. In addition, as __builtin_expect overrides the compiler
+   heuristic, do not use in conditions where one of the branches ends with a
+   call to a function with __attribute__((noreturn)): the compiler internal
+   heuristic will mark this branch as much less likely as GCC_UNLIKELY() would
+   do.  */
+
+#define GCC_LIKELY(X)       __builtin_expect(!!(X), 1)
+#define GCC_UNLIKELY(X)     __builtin_expect(!!(X), 0)
+
 /* Some of the headers included by <memory> can use "abort" within a
    namespace, e.g. "_VSTD::abort();", which fails after we use the
    preprocessor to redefine "abort" as "fancy_abort" below.
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 4/8] tree-vect-loop.c: use MSG_OPTIMIZED_LOCATIONS in a few places
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
  2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
  2018-06-14 19:50               ` [PATCH 5/8] gimple-loop-interchange.cc: use the dump API in a few places David Malcolm
@ 2018-06-14 19:50               ` David Malcolm
  2018-06-14 19:50               ` [PATCH 6/8] ipa-inline.c/tree-inline.c: port from fprintf to dump API David Malcolm
                                 ` (4 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-14 19:50 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

I noticed when reviewing an HTML report generated from optimization
records generated in turn from the existing API calls that we
sometimes use MSG_NOTE when MSG_OPTIMIZED_LOCATIONS would be more
appropriate (to denote a successful optimization).

gcc/ChangeLog:
	* tree-vect-loop.c (vect_transform_loop): Use
	MSG_OPTIMIZED_LOCATIONS rather than MSG_NOTE when reporting on
	vectorized loops and epilogues.
---
 gcc/tree-vect-loop.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index e0378ee..cdf8d09 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -8735,19 +8735,19 @@ vect_transform_loop (loop_vec_info loop_vinfo)
     {
       if (!LOOP_VINFO_EPILOGUE_P (loop_vinfo))
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
 			   "LOOP VECTORIZED\n");
 	  if (loop->inner)
-	    dump_printf_loc (MSG_NOTE, vect_location,
+	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
 			     "OUTER LOOP VECTORIZED\n");
-	  dump_printf (MSG_NOTE, "\n");
+	  dump_printf (MSG_OPTIMIZED_LOCATIONS, "\n");
 	}
       else
 	{
-	  dump_printf_loc (MSG_NOTE, vect_location,
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
 			   "LOOP EPILOGUE VECTORIZED (VS=");
-	  dump_dec (MSG_NOTE, current_vector_size);
-	  dump_printf (MSG_NOTE, ")\n");
+	  dump_dec (MSG_OPTIMIZED_LOCATIONS, current_vector_size);
+	  dump_printf (MSG_OPTIMIZED_LOCATIONS, ")\n");
 	}
     }
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
@ 2018-06-15  6:10                 ` Alexander Monakov
  2018-06-15 16:31                 ` Jeff Law
  1 sibling, 0 replies; 80+ messages in thread
From: Alexander Monakov @ 2018-06-15  6:10 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, GCC Patches

On Thu, 14 Jun 2018, David Malcolm wrote:

> The idea is to later use these macros to mark the
>   if (dump_enabled_p ())
> parts of the compiler as cold, in the hope of helping non-PGO builds
> of gcc.

I think a cleaner way to achieve that would be to mark a function called
under the predicate with ATTRIBUTE_COLD: if you have

    if (dump_enabled_p ())
      {
        ...
        dump_something ();
	...
      }

then declaring dump_something with __attribute__((cold)) informs the
compiler that the branch leading to it is unlikely. It also moves the
function body to the "cold" text region, but that may be acceptable.

Or, perhaps even simpler, you can

    #define dump_enabled_p() __builtin_expect (dump_enabled_p (), 0)

> 	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros, adapted from
> 	libgfortran.h.

I like the idea of these macros, but can their spelling use lowercase please,
like the Linux kernel, Glibc (and even libgfortran.h) spell their variants.

> +/* The following macros can be used to annotate conditions which are likely or
> +   unlikely to be true.  Avoid using them when a condition is only slightly
> +   more likely/less unlikely than average to avoid the performance penalties of
> +   branch misprediction. In addition, as __builtin_expect overrides the compiler
> +   heuristic, do not use in conditions where one of the branches ends with a
> +   call to a function with __attribute__((noreturn)): the compiler internal
> +   heuristic will mark this branch as much less likely as GCC_UNLIKELY() would
> +   do.  */

I realize this is copied verbatim from libgfortran.h, but I think the comment
is not accurately worded and should not be duplicated. I'd suggest simply

/* Shorthands for informing compiler's static branch prediction.  */

> +#define GCC_LIKELY(X)       __builtin_expect(!!(X), 1)
> +#define GCC_UNLIKELY(X)     __builtin_expect(!!(X), 0)

The '!!'s are not necessary here, as far as I can tell.

Alexander

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
  2018-06-15  6:10                 ` Alexander Monakov
@ 2018-06-15 16:31                 ` Jeff Law
  2018-06-15 16:35                   ` Jakub Jelinek
  1 sibling, 1 reply; 80+ messages in thread
From: Jeff Law @ 2018-06-15 16:31 UTC (permalink / raw)
  To: David Malcolm, Richard Biener; +Cc: GCC Patches

On 06/14/2018 02:32 PM, David Malcolm wrote:
> The idea is to later use these macros to mark the
>   if (dump_enabled_p ())
> parts of the compiler as cold, in the hope of helping non-PGO builds
> of gcc.
> 
> I haven't measured it yet, though.
> 
> gcc/ChangeLog:
> 	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros, adapted from
> 	libgfortran.h.
ISTM if we're going to bother with this stuff that we should try to be
consistent between glibc and gcc.  Anything else seems like utter
madness to me.

Jeff

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-15 16:31                 ` Jeff Law
@ 2018-06-15 16:35                   ` Jakub Jelinek
  2018-06-15 16:36                     ` Jeff Law
  0 siblings, 1 reply; 80+ messages in thread
From: Jakub Jelinek @ 2018-06-15 16:35 UTC (permalink / raw)
  To: Jeff Law; +Cc: David Malcolm, Richard Biener, GCC Patches

On Fri, Jun 15, 2018 at 10:31:26AM -0600, Jeff Law wrote:
> On 06/14/2018 02:32 PM, David Malcolm wrote:
> > The idea is to later use these macros to mark the
> >   if (dump_enabled_p ())
> > parts of the compiler as cold, in the hope of helping non-PGO builds
> > of gcc.
> > 
> > I haven't measured it yet, though.
> > 
> > gcc/ChangeLog:
> > 	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros, adapted from
> > 	libgfortran.h.
> ISTM if we're going to bother with this stuff that we should try to be
> consistent between glibc and gcc.  Anything else seems like utter
> madness to me.

Do we really need these macros at all?  We are already using
__builtin_expect directly in gcc/ subdirectory (with system.h providing
a dummy macro if not supported by the host compiler).
And I bet GCC developers are all familiar with __builtin_expect.

	Jakub

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-15 16:35                   ` Jakub Jelinek
@ 2018-06-15 16:36                     ` Jeff Law
  2018-06-15 20:04                       ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Jeff Law @ 2018-06-15 16:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: David Malcolm, Richard Biener, GCC Patches

On 06/15/2018 10:35 AM, Jakub Jelinek wrote:
> On Fri, Jun 15, 2018 at 10:31:26AM -0600, Jeff Law wrote:
>> On 06/14/2018 02:32 PM, David Malcolm wrote:
>>> The idea is to later use these macros to mark the
>>>   if (dump_enabled_p ())
>>> parts of the compiler as cold, in the hope of helping non-PGO builds
>>> of gcc.
>>>
>>> I haven't measured it yet, though.
>>>
>>> gcc/ChangeLog:
>>> 	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros, adapted from
>>> 	libgfortran.h.
>> ISTM if we're going to bother with this stuff that we should try to be
>> consistent between glibc and gcc.  Anything else seems like utter
>> madness to me.
> 
> Do we really need these macros at all? 
I certainly question this as well.


 We are already using
> __builtin_expect directly in gcc/ subdirectory (with system.h providing
> a dummy macro if not supported by the host compiler).
> And I bet GCC developers are all familiar with __builtin_expect.
Yup.  But I doubt we want to litter the sources with references to them.

jeff

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY
  2018-06-15 16:36                     ` Jeff Law
@ 2018-06-15 20:04                       ` David Malcolm
  0 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-15 20:04 UTC (permalink / raw)
  To: Jeff Law, Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On Fri, 2018-06-15 at 10:36 -0600, Jeff Law wrote:
> On 06/15/2018 10:35 AM, Jakub Jelinek wrote:
> > On Fri, Jun 15, 2018 at 10:31:26AM -0600, Jeff Law wrote:
> > > On 06/14/2018 02:32 PM, David Malcolm wrote:
> > > > The idea is to later use these macros to mark the
> > > >   if (dump_enabled_p ())
> > > > parts of the compiler as cold, in the hope of helping non-PGO
> > > > builds
> > > > of gcc.
> > > > 
> > > > I haven't measured it yet, though.
> > > > 
> > > > gcc/ChangeLog:
> > > > 	* system.h (GCC_LIKELY, GCC_UNLIKELY): New macros,
> > > > adapted from
> > > > 	libgfortran.h.
> > > 
> > > ISTM if we're going to bother with this stuff that we should try
> > > to be
> > > consistent between glibc and gcc.  Anything else seems like utter
> > > madness to me.
> > 
> > Do we really need these macros at all? 
> 
> I certainly question this as well.
> 
> 
>  We are already using
> > __builtin_expect directly in gcc/ subdirectory (with system.h
> > providing
> > a dummy macro if not supported by the host compiler).
> > And I bet GCC developers are all familiar with __builtin_expect.
> 
> Yup.  But I doubt we want to litter the sources with references to
> them.

Indeed.  There's only one place where I was thinking of adding it
(within dump_enabled_p), and as I said I haven't measured the impact
yet.
 
I'll drop this patch from the v3 patch kit.

Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/8] Introduce VECT_SCOPE macro
  2018-06-14 19:50               ` [PATCH 2/8] Introduce VECT_SCOPE macro David Malcolm
@ 2018-06-15 20:11                 ` Jeff Law
  2018-06-18  9:26                   ` Richard Biener
  2018-06-18 19:07                   ` David Malcolm
  0 siblings, 2 replies; 80+ messages in thread
From: Jeff Law @ 2018-06-15 20:11 UTC (permalink / raw)
  To: David Malcolm, Richard Biener; +Cc: GCC Patches

On 06/14/2018 02:32 PM, David Malcolm wrote:
> The vectorizer code has numerous instances of:
> 
>   if (dump_enabled_p ())
>     dump_printf_loc (MSG_NOTE, vect_location,
>                      "=== some message ===\n");
> 
> In each case, the dump_printf_loc is a MSG_NODE at vect_location.
> 
> In almost all cases the message is of the form
>   "=== foo ===\n"
> 
> The exceptions are:
>   "===== analyze_loop_nest =====\n"
> which uses 4 equal signs rather than 3, and
>   "===vect_slp_analyze_bb===\n"
> which is missing the spaces.
> 
> In most cases (but not always) the message matches the function name.
> 
> This patch replaces all of these with a macro, taking the message
> as an argument (and forcing the use of three dashes and a space).
> 
> The idea is to later convert this macro to use an RAII type
> that pushes and pops scope, so that the nesting structure appears
> in the dumpfile and -fopt-info logs (and in the remarks and
> optimization records introduced later in this patch kit).
> 
> The message is usually the function name, but not always.
> Should I split this out into two macros? e.g. a VECT_FUNCTION_SCOPE
> that uses __FUNCTION__?
> 
> Would DUMP_VECT_SCOPE be a better name, perhaps?
> 
> gcc/ChangeLog:
> 	* tree-vect-data-refs.c (vect_analyze_data_ref_dependences):
> 	Replace dump_printf_loc call with VECT_SCOPE.
> 	(vect_slp_analyze_instance_dependence): Likewise.
> 	(vect_enhance_data_refs_alignment): Likewise.
> 	(vect_analyze_data_refs_alignment): Likewise.
> 	(vect_slp_analyze_and_verify_instance_alignment
> 	(vect_analyze_data_ref_accesses): Likewise.
> 	(vect_prune_runtime_alias_test_list): Likewise.
> 	(vect_analyze_data_refs): Likewise.
> 	* tree-vect-loop-manip.c (vect_update_inits_of_drs): Likewise.
> 	* tree-vect-loop.c (vect_determine_vectorization_factor): Likewise.
> 	(vect_analyze_scalar_cycles_1): Likewise.
> 	(vect_get_loop_niters): Likewise.
> 	(vect_analyze_loop_form_1): Likewise.
> 	(vect_update_vf_for_slp): Likewise.
> 	(vect_analyze_loop_operations): Likewise.
> 	(vect_analyze_loop): Likewise.
> 	(vectorizable_induction): Likewise.
> 	(vect_transform_loop): Likewise.
> 	* tree-vect-patterns.c (vect_pattern_recog): Likewise.
> 	* tree-vect-slp.c (vect_analyze_slp): Likewise.
> 	(vect_make_slp_decision): Likewise.
> 	(vect_detect_hybrid_slp): Likewise.
> 	(vect_slp_analyze_operations): Likewise.
> 	(vect_slp_bb): Likewise.
> 	* tree-vect-stmts.c (vect_mark_stmts_to_be_vectorized): Likewise.
> 	(vectorizable_bswap): Likewise.
> 	(vectorizable_call): Likewise.
> 	(vectorizable_simd_clone_call): Likewise.
> 	(vectorizable_conversion): Likewise.
> 	(vectorizable_assignment): Likewise.
> 	(vectorizable_shift): Likewise.
> 	(vectorizable_operation): Likewise.
> 	* tree-vectorizer.h (VECT_SCOPE): New macro.
OK.  But rather than using a macro, *consider* just using a normal
function.  I'm less and less inclined to use macros as I get older :-)

If there's a solid reason to use a macro, then that's fine.

DUMP_VECT_SCOPE seems better than VEC_SCOPE.
Jeff

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/8] Introduce VECT_SCOPE macro
  2018-06-15 20:11                 ` Jeff Law
@ 2018-06-18  9:26                   ` Richard Biener
  2018-06-18 19:07                   ` David Malcolm
  1 sibling, 0 replies; 80+ messages in thread
From: Richard Biener @ 2018-06-18  9:26 UTC (permalink / raw)
  To: Jeff Law; +Cc: David Malcolm, GCC Patches

On Fri, Jun 15, 2018 at 10:11 PM Jeff Law <law@redhat.com> wrote:
>
> On 06/14/2018 02:32 PM, David Malcolm wrote:
> > The vectorizer code has numerous instances of:
> >
> >   if (dump_enabled_p ())
> >     dump_printf_loc (MSG_NOTE, vect_location,
> >                      "=== some message ===\n");
> >
> > In each case, the dump_printf_loc is a MSG_NODE at vect_location.
> >
> > In almost all cases the message is of the form
> >   "=== foo ===\n"
> >
> > The exceptions are:
> >   "===== analyze_loop_nest =====\n"
> > which uses 4 equal signs rather than 3, and
> >   "===vect_slp_analyze_bb===\n"
> > which is missing the spaces.
> >
> > In most cases (but not always) the message matches the function name.
> >
> > This patch replaces all of these with a macro, taking the message
> > as an argument (and forcing the use of three dashes and a space).
> >
> > The idea is to later convert this macro to use an RAII type
> > that pushes and pops scope, so that the nesting structure appears
> > in the dumpfile and -fopt-info logs (and in the remarks and
> > optimization records introduced later in this patch kit).
> >
> > The message is usually the function name, but not always.
> > Should I split this out into two macros? e.g. a VECT_FUNCTION_SCOPE
> > that uses __FUNCTION__?
> >
> > Would DUMP_VECT_SCOPE be a better name, perhaps?
> >
> > gcc/ChangeLog:
> >       * tree-vect-data-refs.c (vect_analyze_data_ref_dependences):
> >       Replace dump_printf_loc call with VECT_SCOPE.
> >       (vect_slp_analyze_instance_dependence): Likewise.
> >       (vect_enhance_data_refs_alignment): Likewise.
> >       (vect_analyze_data_refs_alignment): Likewise.
> >       (vect_slp_analyze_and_verify_instance_alignment
> >       (vect_analyze_data_ref_accesses): Likewise.
> >       (vect_prune_runtime_alias_test_list): Likewise.
> >       (vect_analyze_data_refs): Likewise.
> >       * tree-vect-loop-manip.c (vect_update_inits_of_drs): Likewise.
> >       * tree-vect-loop.c (vect_determine_vectorization_factor): Likewise.
> >       (vect_analyze_scalar_cycles_1): Likewise.
> >       (vect_get_loop_niters): Likewise.
> >       (vect_analyze_loop_form_1): Likewise.
> >       (vect_update_vf_for_slp): Likewise.
> >       (vect_analyze_loop_operations): Likewise.
> >       (vect_analyze_loop): Likewise.
> >       (vectorizable_induction): Likewise.
> >       (vect_transform_loop): Likewise.
> >       * tree-vect-patterns.c (vect_pattern_recog): Likewise.
> >       * tree-vect-slp.c (vect_analyze_slp): Likewise.
> >       (vect_make_slp_decision): Likewise.
> >       (vect_detect_hybrid_slp): Likewise.
> >       (vect_slp_analyze_operations): Likewise.
> >       (vect_slp_bb): Likewise.
> >       * tree-vect-stmts.c (vect_mark_stmts_to_be_vectorized): Likewise.
> >       (vectorizable_bswap): Likewise.
> >       (vectorizable_call): Likewise.
> >       (vectorizable_simd_clone_call): Likewise.
> >       (vectorizable_conversion): Likewise.
> >       (vectorizable_assignment): Likewise.
> >       (vectorizable_shift): Likewise.
> >       (vectorizable_operation): Likewise.
> >       * tree-vectorizer.h (VECT_SCOPE): New macro.
> OK.  But rather than using a macro, *consider* just using a normal
> function.  I'm less and less inclined to use macros as I get older :-)
>
> If there's a solid reason to use a macro, then that's fine.
>
> DUMP_VECT_SCOPE seems better than VEC_SCOPE.

Agreed on DUMP_VECT_SCOPE, using a function would somewhat
defeat the purpose of the dump_enabled_p () check.

Richard.

> Jeff

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/8] Introduce VECT_SCOPE macro
  2018-06-15 20:11                 ` Jeff Law
  2018-06-18  9:26                   ` Richard Biener
@ 2018-06-18 19:07                   ` David Malcolm
  1 sibling, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-06-18 19:07 UTC (permalink / raw)
  To: Jeff Law, Richard Biener; +Cc: GCC Patches

On Fri, 2018-06-15 at 14:11 -0600, Jeff Law wrote:
> On 06/14/2018 02:32 PM, David Malcolm wrote:
> > The vectorizer code has numerous instances of:
> > 
> >   if (dump_enabled_p ())
> >     dump_printf_loc (MSG_NOTE, vect_location,
> >                      "=== some message ===\n");
> > 
> > In each case, the dump_printf_loc is a MSG_NODE at vect_location.
> > 
> > In almost all cases the message is of the form
> >   "=== foo ===\n"
> > 
> > The exceptions are:
> >   "===== analyze_loop_nest =====\n"
> > which uses 4 equal signs rather than 3, and
> >   "===vect_slp_analyze_bb===\n"
> > which is missing the spaces.
> > 
> > In most cases (but not always) the message matches the function
> > name.
> > 
> > This patch replaces all of these with a macro, taking the message
> > as an argument (and forcing the use of three dashes and a space).
> > 
> > The idea is to later convert this macro to use an RAII type
> > that pushes and pops scope, so that the nesting structure appears
> > in the dumpfile and -fopt-info logs (and in the remarks and
> > optimization records introduced later in this patch kit).
> > 
> > The message is usually the function name, but not always.
> > Should I split this out into two macros? e.g. a VECT_FUNCTION_SCOPE
> > that uses __FUNCTION__?
> > 
> > Would DUMP_VECT_SCOPE be a better name, perhaps?
> > 
> > gcc/ChangeLog:
> > 	* tree-vect-data-refs.c (vect_analyze_data_ref_dependences):
> > 	Replace dump_printf_loc call with VECT_SCOPE.
> > 	(vect_slp_analyze_instance_dependence): Likewise.
> > 	(vect_enhance_data_refs_alignment): Likewise.
> > 	(vect_analyze_data_refs_alignment): Likewise.
> > 	(vect_slp_analyze_and_verify_instance_alignment
> > 	(vect_analyze_data_ref_accesses): Likewise.
> > 	(vect_prune_runtime_alias_test_list): Likewise.
> > 	(vect_analyze_data_refs): Likewise.
> > 	* tree-vect-loop-manip.c (vect_update_inits_of_drs): Likewise.
> > 	* tree-vect-loop.c (vect_determine_vectorization_factor):
> > Likewise.
> > 	(vect_analyze_scalar_cycles_1): Likewise.
> > 	(vect_get_loop_niters): Likewise.
> > 	(vect_analyze_loop_form_1): Likewise.
> > 	(vect_update_vf_for_slp): Likewise.
> > 	(vect_analyze_loop_operations): Likewise.
> > 	(vect_analyze_loop): Likewise.
> > 	(vectorizable_induction): Likewise.
> > 	(vect_transform_loop): Likewise.
> > 	* tree-vect-patterns.c (vect_pattern_recog): Likewise.
> > 	* tree-vect-slp.c (vect_analyze_slp): Likewise.
> > 	(vect_make_slp_decision): Likewise.
> > 	(vect_detect_hybrid_slp): Likewise.
> > 	(vect_slp_analyze_operations): Likewise.
> > 	(vect_slp_bb): Likewise.
> > 	* tree-vect-stmts.c (vect_mark_stmts_to_be_vectorized):
> > Likewise.
> > 	(vectorizable_bswap): Likewise.
> > 	(vectorizable_call): Likewise.
> > 	(vectorizable_simd_clone_call): Likewise.
> > 	(vectorizable_conversion): Likewise.
> > 	(vectorizable_assignment): Likewise.
> > 	(vectorizable_shift): Likewise.
> > 	(vectorizable_operation): Likewise.
> > 	* tree-vectorizer.h (VECT_SCOPE): New macro.
> 
> OK.  But rather than using a macro, *consider* just using a normal
> function.  I'm less and less inclined to use macros as I get older :-
> )
> 
> If there's a solid reason to use a macro, then that's fine.

The reason for doing it as a macro is that I'm planning to turn this
into an RAII-style class for pushing and popping scopes, capturing the
nesting.  That wouldn't work if it was a function.

> DUMP_VECT_SCOPE seems better than VEC_SCOPE.

Thanks.  I've committed the DUMP_VECT_SCOPE version of the patch as
r261710 (after usual testing)

Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH] v3 of optinfo, remarks and optimization records
  2018-06-04 13:20           ` Richard Biener
  2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
@ 2018-06-20 16:35             ` David Malcolm
  2018-06-25 13:35               ` Richard Biener
  1 sibling, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-20 16:35 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

Here's v3 of the patch (one big patch this time, rather than a kit).

Like the v2 patch kit, this patch reuses the existing dump API,
rather than inventing its own.

Specifically, it uses the dump_* functions in dumpfile.h that don't
take a FILE *, the ones that implicitly write to dump_file and/or
alt_dump_file.  I needed a name for them, so I've taken to calling
them the "structured dump API" (better name ideas welcome).

v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
calls as delimiters when consolidating "dump_*" calls.  There's a
new dump_context class which has responsibility for consolidating
them into optimization records.

The dump_*_loc calls now capture more than just a location_t: they
capture the profile_count and the location in GCC's own sources where
the dump is being emitted from.

This works by introducing a new "dump_location_t" class as the
argument of those dump_*_loc calls.  The dump_location_t can
be constructed from a gimple * or from an rtx_insn *, so that
rather than writing:

  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
                   "some message: %i", 42);

you can write:

  dump_printf_loc (MSG_NOTE, stmt,
                   "some message: %i", 42);

and the dump_location_t constructor will grab the location_t and
profile_count of stmt, and the location of the "dump_printf_loc"
callsite (and gracefully handle "stmt" being NULL).

Earlier versions of the patch captured the location of the
dump_*_loc call via preprocessor hacks, or didn't work properly;
this version of the patch works more cleanly: internally,
dump_location_t is split into two new classes:
  * dump_user_location_t: the location_t and profile_count within
    the *user's code*, and
  * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
    the *implementation* code (i.e. GCC or a plugin), captured
    "automagically" via default params

These classes are sometimes used elsewhere in the code.  For
example, "vect_location" becomes a dump_user_location_t
(location_t and profile_count), so that in e.g:

  vect_location = find_loop_location (loop);

it's capturing the location_t and profile_count, and then when
it's used here:

  dump_printf_loc (MSG_NOTE, vect_location, "foo");

the dump_location_t is constructed from the vect_location
plus the dump_impl_location_t at that callsite.

In contrast, loop-unroll.c's report_unroll's "locus" param
becomes a dump_location_t: we're interested in where it was
called from, not in the locations of the various dump_*_loc calls
within it.

Previous versions of the patch captured a gimple *, and needed
GTY markers; in this patch, the dump_user_location_t is now just a
location_t and a profile_count.

The v2 patch added an overload for dump_printf_loc so that you
could pass in either a location_t, or the new type; this version
of the patch eliminates that: they all now take dump_location_t.

Doing so required adding support for rtx_insn *, so that one can
write this kind of thing in RTL passes:

  dump_printf_loc (MSG_NOTE, insn, "foo");

One knock-on effect is that get_loop_location now returns a
dump_user_location_t rather than a location_t, so that it has
hotness information.

Richi: would you like me to split out this location-handling
code into a separate patch?  (It's kind of redundant without
adding the remarks and optimization records work, but if that's
easier I can do it)

The v3 patch adds more detail to remarks: they now show the gcc
source location that emitted them e.g.:

test.c:8:3: remark:   Symbolic number of iterations is '(unsigned int)
n_9(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]

and I added new command-line options for controlling the above:
* -fno-diagnostics-show-remark-hotness
* -fno-diagnostics-show-remark-origin
* -fno-diagnostics-show-remark-pass

An example of remark output (showing colors) can be seen here:

  https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html

I haven't yet implemented options for filtering remarks (I'm thinking
of using the optgroups from -fopt-info, a hotness threshold, and pragmas
for narrowing them to a specific region of the user's code); though
the HTML visualization from the JSON output is likely to provide more
flexibility.

I got rid of GCC_UNLIKELY in favor of a __builtin_expect in
dump_enabled_p, but I haven't measured the impact yet.

Other changes relative to v2:
* added class dump_context; eliminate class pending_optinfo; moved optinfo
  emission from optinfo dtor and into dump_context
* added selftests for dump_* consolidation, for JSON emission, and for
  remarks
* minimized global state in JSON emission
* renamed OPTINFO_SCOPE to AUTO_DUMP_SCOPE and moved from optinfo.h to
  dumpfile.h (and similar changes to the support class)
* added "m_pass" field to optinfo, reading current_pass at time of
  creation, rather than as needed later on

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
(relative to r261555).

How is this looking?

As before, this patch requires the JSON support patch from v1 of the kit:
  "[PATCH 02/10] Add JSON implementation"
    https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01676.html

This approach means we can use all of the existing dump_* calls without
having to rewrite anything, gaining the ability to prioritize dump messages
by code hotness and to track where in GCC any dump message came from
without needing to resort to "grep".

But in terms of actually helping users figure out "how do I get gcc to
vectorize this loop?", it's only a start.  Maybe followup activity would be
to go through the existing dump messages and to make them more user-friendly.
Or to add new dump API entrypoints?
I find the MSG_* a bit verbose, so maybe instead of e.g.:

  dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                   "can't inline function call: ");
  dump_symtab_node (callee);
  dump_printf (" into ");
  dump_symtab_node (caller);
  dump_printf (" as body is not available\n");

there could be, say:

 missed_opt_at (call_stmt,
                ("can't inline function call: %N into %N"
                 " as body is not available\n"),
                callee, caller);

(plus, say "optimized_at", "opt_note_at"), introducing a new family
of format codes (so that the optimization records "know" how to
embed them).

This would also allow for i18n; such API calls would be more explicitly
aimed at tech-savvy end-users.

Anyway, that last bit is more me just thinking aloud about possible future
work here, so here's the v3 patch as it is:

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
	optinfo-emit-json.o.
	(CFLAGS-optinfo-emit-json.o): Add -DTARGET_NAME as per toplev.o.
	* cfgloop.c (get_loop_location): Convert return type from
	location_t to dump_user_location_t, replacing INSN_LOCATION lookups
	by implicit construction from rtx_insn *, and using
	dump_user_location_t::from_function_decl for the fallback case.
	* cfgloop.h (get_loop_location): Convert return type from
	location_t to dump_user_location_t.
	* cgraph.c (cgraph_node::get_body): Replace assignment to
	"dump_file" with call to set_dump_file.
	* cgraphunit.c (walk_polymorphic_call_targets): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	* common.opt (fremarks): New option.
	(fdiagnostics-show-remark-hotness): New option.
	(fdiagnostics-show-remark-origin): New option.
	(fdiagnostics-show-remark-pass): New option.
	(fsave-optimization-record): New option.
	* coretypes.h  (class symtab_node): New forward declaration.
	(struct cgraph_node): Likewise.
	(class varpool_node): Likewise.
	(struct kv_pair): Move here from dumpfile.c.
	* coverage.c (get_coverage_counts): Update calls to
	dump_printf_loc to pass in dump_location_t rather than a
	location_t.
	* diagnostic-color.c (color_dict): Add "remark", as bold green.
	* diagnostic-core.h (remark): New decl.
	* diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
	(remark): New function.
	* diagnostic.def (DK_REMARK): New diagnostic kind.
	* doc/invoke.texi (Remarks): New section.
	(-fsave-optimization-record): New option.
	(-fremarks): New option.
	(-fno-diagnostics-show-remark-hotness): New option.
	(-fno-diagnostics-show-remark-origin): New option.
	(-fno-diagnostics-show-remark-pass): New option.
	* dump-context.h: New file.
	* dumpfile.c: Include "optinfo.h", "cgraph.h",
	"optinfo-emit-json.h", "optinfo-internal.h", "backend.h",
	"gimple.h", "rtl.h", "dump-context.h", "tree-pass.h", "selftest.h"
	(alt_dump_file): Make static, and group with...
	(alt_flags): ...this definition.
	(dumps_are_enabled): New variable.
	(refresh_dumps_are_enabled): New function.
	(set_dump_file): New function.
	(set_alt_dump_file): New function.
	(struct kv_pair): Move from here to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_user_location_t::dump_user_location_t): New ctors
	(dump_user_location_t::from_function_decl): New function.
	(dump_loc): Make static.  Add indentation based on scope depth.
	(dump_context::~dump_context): New dtor.
	(dump_gimple_stmt): Move implementation to...
	(dump_context::dump_gimple_stmt): ...this new method.  Add the stmt
	to any pending optinfo, creating one if need be.
	(dump_gimple_stmt_loc): Move implementation to...
	(dump_context::dump_gimple_stmt_loc): ...this new method.  Convert
	param "loc" from location_t to const dump_location_t &.  Start a
	new optinfo and add the stmt to it.
	(dump_generic_expr): Move implementation to...
	(dump_context::dump_generic_expr): ...this new method.  Add the
	tree to any pending optinfo, creating one if need be.
	(dump_generic_expr_loc): Delete.
	(dump_printf): Move implementation to...
	(dump_context::dump_printf_va): ...this new method.  Add the
	text to any pending optinfo, creating one if need be.
	(dump_printf_loc): Move implementation to...
	(dump_context::dump_printf_loc_va): ...this new method.  Convert
	param "loc" from location_t to const dump_location_t &.  Start a
	new optinfo and add the stmt to it.
	(dump_dec): Move implementation to...
	(dump_context::dump_dec): ...this new method.  Add the value to
	any pending optinfo, creating one if need be.
	(dump_context::dump_symtab_node): New method.
	(dump_context::get_scope_depth): New method.
	(dump_context::begin_scope): New method.
	(dump_context::end_scope): New method.
	(dump_context::ensure_pending_optinfo): New method.
	(dump_context::begin_next_optinfo): New method.
	(dump_context::end_any_optinfo): New method.
	(dump_context::s_current): New global.
	(dump_context::s_default): New global.
	(dump_symtab_node): New function.
	(get_dump_scope_depth): New function.
	(dump_begin_scope): New function.
	(dump_end_scope): New function.
	(gcc::dump_manager::dump_start): Replace assignments to
	"dump_file" and "alt_dump_file" with call to set_dump_file and
	set_alt_dump_file.
	(gcc::dump_manager::dump_finish): Likewise.
	(selftest::temp_dump_context::temp_dump_context): New ctor.
	(selftest::temp_dump_context::~temp_dump_context): New dtor.
	(selftest::test_impl_location): New test.
	(selftest::assert_is_text): New support function.
	(selftest::assert_is_tree): New support function.
	(selftest::assert_is_gimple): New support function.
	(selftest::test_capture_of_dump_calls): New test.
	(selftest::dumpfile_c_tests): New function.
	* dumpfile.h: Include "profile-count.h".
	(dump_file, dump_flags, dump_file_name): Move decls to top of
	file, to split them out from the "Structured dumping" API.
	(set_dump_file): New function decl.
	(dumps_are_enabled): New variable decl.
	(dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
	global.
	(class dump_user_location_t): New class.
	(struct dump_impl_location_t): New struct.
	(class dump_location_t): New class.
	(dump_printf_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	(dump_generic_expr_loc): Delete.
	(dump_gimple_stmt_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	(dump_symtab_node): New decl.
	(get_dump_scope_depth): New decl.
	(dump_begin_scope): New decl.
	(dump_end_scope): New decl.
	(class auto_dump_scope): New class.
	(AUTO_DUMP_SCOPE): New macro.
	(dump_function, print_combine_total_stats, enable_rtl_dump_file):
	Move these decls, to split them out from the "Structured dumping"
	API.
	(alt_dump_file): Delete decl.
	(optgroup_options): New decl.
	(class dump_manager): Add leading comment.
	* gimple-fold.c (fold_gimple_assign): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(gimple_fold_call): Likewise.
	* gimple-loop-interchange.cc
	(loop_cand::analyze_iloop_reduction_var): Update for change to
	check_reduction_path.
	(tree_loop_interchange::interchange): Update for change to
	find_loop_location.  Add a usage of AUTO_DUMP_SCOPE.
	* gimple-pretty-print.c (gimple_dump_bb_buff): Make non-static.
	* gimple-pretty-print.h (gimple_dump_bb_buff): New decl.
	* graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
	change in return-type of find_loop_location.
	(graphite_regenerate_ast_isl): Likewise.
	* graphite-optimize-isl.c (optimize_isl): Likewise.
	* graphite.c (graphite_transform_loops): Update for change in
	return-type of find_loop_location.
	* ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
	pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
	(ipa_make_edge_direct_to_target): Likewise.
	* ipa.c (walk_polymorphic_call_targets): Likewise.
	* loop-unroll.c (report_unroll): Convert "locus" param from
	location_t to dump_location_t.
	(decide_unrolling): Update for change to get_loop_location's
	return type.
	* omp-grid.c (struct grid_prop): Convert field "target_loc" from
	location_t to dump_user_location_t.
	(grid_find_single_omp_among_assignments_1): Updates calls to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(grid_parallel_clauses_gridifiable): Convert "tloc" from
	location_t to dump_location_t.  Updates calls to dump_printf_loc
	to pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	(grid_inner_loop_gridifiable_p): Likewise.
	(grid_dist_follows_simple_pattern): Likewise.
	(grid_gfor_follows_tiling_pattern): Likewise.
	(grid_target_follows_gridifiable_pattern): Likewise.
	(grid_attempt_target_gridification): Convert initialization
	of local "grid" from memset to zero-initialization; FIXME: does
	this require C++11?  Update call to dump_printf_loc to pass in a
	optinfo_location rather than a location_t, via the gimple stmt.
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* opts.c (print_specific_help): Handle CL_REMARK.
	(common_handle_option): Likewise.
	* opts.h (CL_REMARK): New macro.
	(CL_MAX_OPTION_CLASS): Update for CL_REMARK.
	(CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD)
	(CL_PCH_IGNORE): Likewise.
	* passes.c: Include "optinfo.h" and "optinfo-emit-json.h".
	(execute_optinfo_function_dump): New function.
	(execute_one_ipa_transform_pass): Add per-function call to
	execute_optinfo_function_dump.
	(execute_one_pass): Likewise.
	* pretty-print.c (test_pp_format): Move save and restore of quotes
	to class auto_fix_quotes, and add an instance.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* profile.c (read_profile_edge_counts): Updates call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t
	(compute_branch_probabilities): Likewise.
	* selftest-run-tests.c (selftest::run_tests): Call
	dumpfile_c_tests, optinfo_cc_tests,
	optinfo_emit_diagnostics_cc_tests, and optinfo_emit_json_cc_tests.
	* selftest.c: Include "intl.h".
	(selftest::auto_fix_quotes::auto_fix_quotes): New ctor.
	(selftest::auto_fix_quotes::~auto_fix_quotes): New dtor.
	* selftest.h (selftest::auto_fix_quotes): New class.
	(dumpfile_c_tests): New decl.
	(optinfo_cc_tests): New decl.
	(optinfo_emit_diagnostics_cc_tests): New decl.
	(optinfo_emit_json_cc_tests): New decl.
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_start and
	optimization_records_finish.
	* tree-loop-distribution.c (pass_loop_distribution::execute):
	Update for change in return type of find_loop_location.
	* tree-nested.c (lower_nested_functions): Replace assignments to
	"dump_file" with calls to set_dump_file.
	* tree-parloops.c (parallelize_loops): Update for change in return
	type of find_loop_location.
	* tree-ssa-live.c: Include "optinfo.h".
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
	* tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
	"locus" from location_t to dump_user_location_t.
	(canonicalize_loop_induction_variables): Likewise.
	* tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
	for change in return type of find_loop_location.
	* tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
	to dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the stmt.
	* tree-vect-loop-manip.c (find_loop_location): Convert return
	type from source_location to dump_user_location_t.
	(vect_do_peeling): Update for above change.
	(vect_loop_versioning): Update for change in type of
	vect_location.
	* tree-vect-loop.c (check_reduction_path): Convert "loc" param
	from location_t to dump_user_location_t.
	(vect_estimate_min_profitable_iters): Update for change in type
	of vect_location.
	* tree-vect-slp.c (vect_print_slp_tree): Convert param "loc" from
	location_t to dump_location_t.
	(vect_slp_bb): Update for change in type of vect_location.
	* tree-vectorizer.c (vect_location): Convert from source_location
	to dump_user_location_t.
	(vectorize_loops): Update for change in vect_location's type.  Add
	top-level DUMP_VECT_SCOPE.
	(increase_alignment): Update for change in vect_location's type.
	* tree-vectorizer.h: Include "optinfo.h".
	(vect_location): Convert from source_location to
	dump_user_location_t.
	(DUMP_VECT_SCOPE): Convert to usage of AUTO_DUMP_SCOPE.
	(find_loop_location): Convert return type from source_location to
	dump_user_location_t.
	(check_reduction_path): Convert 1st param from location_t to
	dump_user_location_t.
	* value-prof.c (check_counter): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t; update
	call to error_at for change in type of "locus".
	(check_ic_target): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t, via the
	call_stmt.

gcc/fortran/ChangeLog:
	* gfc-diagnostic.def (DK_REMARK): New diagnostic kind.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
	remarks_plugin.c.
	* gcc.dg/plugin/remarks-1.c: New test.
	* gcc.dg/plugin/remarks_plugin.c: New test plugin.
	* lib/gcc-dg.exp (dg-remark): New function.
---
 gcc/Makefile.in                              |   4 +
 gcc/cfgloop.c                                |  12 +-
 gcc/cfgloop.h                                |   2 +-
 gcc/cgraph.c                                 |   4 +-
 gcc/cgraphunit.c                             |   3 +-
 gcc/common.opt                               |  21 +
 gcc/coretypes.h                              |  15 +
 gcc/coverage.c                               |  22 +-
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 +
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  83 ++-
 gcc/dump-context.h                           | 112 ++++
 gcc/dumpfile.c                               | 648 +++++++++++++++++++---
 gcc/dumpfile.h                               | 258 ++++++++-
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/gimple-fold.c                            |   6 +-
 gcc/gimple-loop-interchange.cc               |   7 +-
 gcc/gimple-pretty-print.c                    |   2 +-
 gcc/gimple-pretty-print.h                    |   2 +
 gcc/graphite-isl-ast-to-gimple.c             |   4 +-
 gcc/graphite-optimize-isl.c                  |   4 +-
 gcc/graphite.c                               |   2 +-
 gcc/ipa-devirt.c                             |   3 +-
 gcc/ipa-prop.c                               |  10 +-
 gcc/ipa.c                                    |   9 +-
 gcc/loop-unroll.c                            |   4 +-
 gcc/omp-grid.c                               |  47 +-
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 +
 gcc/optinfo-emit-json.cc                     | 773 +++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h                      |  39 ++
 gcc/optinfo-internal.h                       | 145 +++++
 gcc/optinfo.cc                               | 254 +++++++++
 gcc/optinfo.h                                | 147 +++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/passes.c                                 |  17 +
 gcc/pretty-print.c                           |   9 +-
 gcc/profile-count.c                          |  28 +
 gcc/profile-count.h                          |   5 +
 gcc/profile.c                                |  14 +-
 gcc/selftest-run-tests.c                     |   4 +
 gcc/selftest.c                               |  20 +
 gcc/selftest.h                               |  24 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 ++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 ++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 gcc/toplev.c                                 |   5 +
 gcc/tree-loop-distribution.c                 |   2 +-
 gcc/tree-nested.c                            |   4 +-
 gcc/tree-parloops.c                          |   3 +-
 gcc/tree-ssa-live.c                          |   4 +-
 gcc/tree-ssa-loop-ivcanon.c                  |   8 +-
 gcc/tree-ssa-loop-ivopts.c                   |   2 +-
 gcc/tree-ssa-loop-niter.c                    |   2 +-
 gcc/tree-ssa-sccvn.c                         |   3 +-
 gcc/tree-vect-loop-manip.c                   |  16 +-
 gcc/tree-vect-loop.c                         |   8 +-
 gcc/tree-vect-slp.c                          |   5 +-
 gcc/tree-vectorizer.c                        |  17 +-
 gcc/tree-vectorizer.h                        |  25 +-
 gcc/value-prof.c                             |  15 +-
 66 files changed, 3229 insertions(+), 230 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 9b85787..23060a3 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1421,6 +1421,9 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
+	optinfo-emit-diagnostics.o \
+	optinfo-emit-json.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
@@ -2248,6 +2251,7 @@ s-bversion: BASE-VER
 	$(STAMP) s-bversion
 
 CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
 
 pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
 		    $(srcdir)/gen-pass-instances.awk
diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c
index 8af793c..e27cd39 100644
--- a/gcc/cfgloop.c
+++ b/gcc/cfgloop.c
@@ -1800,7 +1800,7 @@ loop_exits_from_bb_p (struct loop *loop, basic_block bb)
 
 /* Return location corresponding to the loop control condition if possible.  */
 
-location_t
+dump_user_location_t
 get_loop_location (struct loop *loop)
 {
   rtx_insn *insn = NULL;
@@ -1819,7 +1819,7 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* If loop has a single exit, then the loop control branch
@@ -1829,24 +1829,24 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (exit->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* Next check the latch, to see if it is non-empty.  */
   FOR_BB_INSNS_REVERSE (loop->latch, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* Finally, if none of the above identifies the loop control branch,
      return the first location in the loop header.  */
   FOR_BB_INSNS (loop->header, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* If all else fails, simply return the current function location.  */
-  return DECL_SOURCE_LOCATION (current_function_decl);
+  return dump_user_location_t::from_function_decl (current_function_decl);
 }
 
 /* Records that every statement in LOOP is executed I_BOUND times.
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index af9bfab..80a31c4 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -357,7 +357,7 @@ extern bool loop_exit_edge_p (const struct loop *, const_edge);
 extern bool loop_exits_to_bb_p (struct loop *, basic_block);
 extern bool loop_exits_from_bb_p (struct loop *, basic_block);
 extern void mark_loop_exit_edges (void);
-extern location_t get_loop_location (struct loop *loop);
+extern dump_user_location_t get_loop_location (struct loop *loop);
 
 /* Loops & cfg manipulation.  */
 extern basic_block *get_loop_body (const struct loop *);
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 3899467..d19f1aa 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
       const char *saved_dump_file_name = dump_file_name;
       dump_flags_t saved_dump_flags = dump_flags;
       dump_file_name = NULL;
-      dump_file = NULL;
+      set_dump_file (NULL);
 
       push_cfun (DECL_STRUCT_FUNCTION (decl));
       execute_all_ipa_transforms ();
@@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
       updated = true;
 
       current_pass = saved_current_pass;
-      dump_file = saved_dump_file;
+      set_dump_file (saved_dump_file);
       dump_file_name = saved_dump_file_name;
       dump_flags = saved_dump_flags;
     }
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 04b6919..7cfb8a0 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 	    }
           if (dump_enabled_p ())
             {
-	      location_t locus = gimple_location_safe (edge->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->name (), target->name ());
 	    }
diff --git a/gcc/common.opt b/gcc/common.opt
index 4aebcaf..141f5dd 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -506,6 +506,11 @@ Driver Negative(Qn)
 R
 Driver Joined Separate
 
+fremarks
+Common Remark Var(flag_remarks)
+Emit diagnostic remarks about optimizations
+FIXME: should this be -fdiagnostics-foo or somesuch?
+
 S
 Driver
 
@@ -1273,6 +1278,18 @@ fdiagnostics-show-option
 Common Var(flag_diagnostics_show_option) Init(1)
 Amend appropriate diagnostic messages with the command line option that controls them.
 
+fdiagnostics-show-remark-hotness
+Common Var(flag_diagnostics_show_remark_hotness) Init(1)
+When emitting optimization remarks, show the execution count of the code being optimized.
+
+fdiagnostics-show-remark-origin
+Common Var(flag_diagnostics_show_remark_origin) Init(1)
+When emitting optimization remarks, show which line of GCC code produced the remark.
+
+fdiagnostics-show-remark-pass
+Common Var(flag_diagnostics_show_remark_pass) Init(1)
+When emitting optimization remarks, show which optimization pass produced the remark.
+
 fdisable-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 disables an optimization pass.
@@ -1942,6 +1959,10 @@ fopt-info-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fopt-info[-<type>=filename]	Dump compiler optimization details.
 
+fsave-optimization-record
+Common Report Var(flag_save_optimization_record) Optimization
+Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..33f3d21 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@ struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node_def, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
@@ -325,6 +332,14 @@ namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 84fff13..350cc45 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -342,12 +342,16 @@ get_coverage_counts (unsigned counter, unsigned expected,
       static int warned = 0;
 
       if (!warned++ && dump_enabled_p ())
-	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
-                         (flag_guess_branch_prob
-                          ? "file %s not found, execution counts estimated\n"
-                          : "file %s not found, execution counts assumed to "
-                            "be zero\n"),
-                         da_file_name);
+	{
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			   (flag_guess_branch_prob
+			    ? "file %s not found, execution counts estimated\n"
+			    : "file %s not found, execution counts assumed to "
+			    "be zero\n"),
+			   da_file_name);
+	}
       return NULL;
     }
   if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
@@ -378,7 +382,9 @@ get_coverage_counts (unsigned counter, unsigned expected,
 		    "its profile data (counter %qs)", id, ctr_names[counter]);
       if (warning_printed && dump_enabled_p ())
 	{
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                            "use -Wno-error=coverage-mismatch to tolerate "
                            "the mismatch but performance may drop if the "
                            "function is hot\n");
@@ -386,7 +392,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
 	  if (!seen_error ()
 	      && !warned++)
 	    {
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                                "coverage mismatch ignored\n");
 	      dump_printf (MSG_OPTIMIZED_LOCATIONS,
                            flag_guess_branch_prob
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 3ee21bc..bcc3767 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
+  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
+	       6, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index aa5807e..63166b8 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool remark (location_t, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index e22c17b..97ed88b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
     {
     case DK_DEBUG:
     case DK_NOTE:
+    case DK_REMARK:
     case DK_ANACHRONISM:
     case DK_WARNING:
       break;
@@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   return ret;
 }
 
+/* Emit an optimization remark at LOCATION.  This is used by the optinfo
+   framework.
+   Return true if the remark was printed, false if it was inhibited.  */
+// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
+
+bool
+remark (location_t location, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, location);
+  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
index ce3dc56..b58095d 100644
--- a/gcc/diagnostic.def
+++ b/gcc/diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b06ea6e..7454ae5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
 * Debugging Options::   Producing debuggable code.
 * Optimize Options::    How much optimization?
 * Instrumentation Options:: Enabling profiling and extra run-time error checking.
+* Remarks::             Details on how your code is being optimized.
 * Preprocessor Options:: Controlling header files and macro definitions.
                          Also, getting dependency information for Make.
 * Assembler Options::   Passing options to the assembler.
@@ -416,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
 -freorder-blocks-algorithm=@var{algorithm} @gol
 -freorder-blocks-and-partition  -freorder-functions @gol
 -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
--frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
+-frounding-math  -fsave-optimization-record @gol
+-fsched2-use-superblocks  -fsched-pressure @gol
 -fsched-spec-load  -fsched-spec-load-dangerous @gol
 -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
 -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
@@ -470,6 +472,11 @@ Objective-C and Objective-C++ Dialects}.
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
 
+@item Remarks
+@xref{Remarks,,Options to Control Remarks from the Compiler}.
+@gccoptlist{-fremarks -fno-diagnostics-show-remark-hotness @gol
+-fno-diagnostics-show-remark-origin -fno-diagnostics-show-remark-pass}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -9904,6 +9911,15 @@ Future versions of GCC may provide finer control of this setting
 using C99's @code{FENV_ACCESS} pragma.  This command-line option
 will be used to specify the default state for @code{FENV_ACCESS}.
 
+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+FIXME: The precise format is not yet set in stone, but it ought
+to be stabilized and then documented somewhere.
+FIXME: should this be described here within the optimization options,
+or within the developer options?
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
@@ -12037,6 +12053,71 @@ The NOP instructions are inserted at---and maybe before, depending on
 @end table
 
 
+@node Remarks
+@section Options to Control Remarks from the Compiler
+@cindex remarks
+@cindex options, remarks
+
+These options are aimed at advanced users who may be interested
+in seeing additional diagnostics from the compiler, giving information
+on the decisions it is making on the code.
+
+The precise messages and their format are subject to change.
+
+Rather than attempting to parse the textual remark format, consider
+using @option{-fsave-optimization-record}, which works from the same
+data internally.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+
+Enabling this option leads to GCC emitting diagnostics detailing the
+optimization decisions it is making.
+
+For example, this message:
+
+@smallexample
+test.c:13:3: remark:   Symbolic number of iterations is '(unsigned int) n_8(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
+@end smallexample
+
+describes an internal detail of the ``vect'' optimization pass, acting at
+the given source location within ``test.c'', where the remark was emitted
+by the function ``vect_analyze_loop_form'' at line 1387 of GCC's source
+file ``tree-vect-loop.c''.
+
+@item -fno-diagnostics-show-remark-hotness
+@opindex fno-diagnostics-show-remark-hotness
+@opindex fdiagnostics-show-remark-hotness
+By default, if diagnostic remarks are enabled, they include information
+on the execution count, or ``hotness'', of the code being optimized:
+the ``[count(precise)=76800000]'' in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-origin
+@opindex fno-diagnostics-show-remark-origin
+@opindex fdiagnostics-show-remark-origin
+By default, if diagnostic remarks are enabled, they include information
+on where in GCC's own source code (or a plugin's source code) the remark
+is being emitted from; the
+``[../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-pass
+@opindex fno-diagnostics-show-remark-pass
+@opindex fdiagnostics-show-remark-pass
+By default, if diagnostic remarks are enabled, they include information
+on which optimization pass emitted the remark: the ``[pass=vect]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
new file mode 100644
index 0000000..2986317
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,112 @@
+/* Support code for handling the various dump_* calls in dumpfile.h
+   Copyright (C) 2018 Free Software Foundation, Inc.
+
+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_DUMP_CONTEXT_H
+#define GCC_DUMP_CONTEXT_H 1
+
+/* A class for handling the various dump_* calls.
+
+   In particular, this class has responsibility for consolidating
+   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
+   calls), and emitting them.
+
+   Putting this in a class (rather than as global state) allows
+   for selftesting of this code.  */
+
+class dump_context
+{
+  friend class temp_dump_context;
+ public:
+  static dump_context &get () { return *s_current; }
+
+  ~dump_context ();
+
+  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
+			     const dump_location_t &loc,
+			     dump_flags_t extra_dump_flags,
+			     gimple *gs, int spc);
+
+  void dump_generic_expr (dump_flags_t dump_kind,
+			  dump_flags_t extra_dump_flags,
+			  tree t);
+
+  void dump_printf_va (dump_flags_t dump_kind, const char *format,
+		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
+
+  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+			   const char *format, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0);
+
+  template<unsigned int N, typename C>
+  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
+
+  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
+
+  /* Managing nested scopes.  */
+  unsigned int get_scope_depth () const;
+  void begin_scope (const char *name, const dump_location_t &loc);
+  void end_scope ();
+
+ private:
+  optinfo &ensure_pending_optinfo ();
+  optinfo &begin_next_optinfo (const dump_location_t &loc);
+  void end_any_optinfo ();
+
+  /* The current nesting depth of dump scopes, for showing nesting
+     via indentation).  */
+  unsigned int m_scope_depth;
+
+  /* The optinfo currently being accumulated since the last dump_*_loc call,
+     if any.  */
+  optinfo *m_pending;
+
+  /* The currently active dump_context, for use by the dump_* API calls.  */
+  static dump_context *s_current;
+
+  /* The default active context.  */
+  static dump_context s_default;
+};
+
+#if CHECKING_P
+
+/* An RAII class for use in selftests for temporarily using a different
+   dump_context.  */
+
+class temp_dump_context
+{
+ public:
+  temp_dump_context (bool new_flag_remarks);
+  ~temp_dump_context ();
+
+  /* Support for selftests.  */
+  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+
+ private:
+  dump_context m_context;
+  dump_context *m_saved;
+  bool m_saved_flag_remarks;
+};
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 2f11284..3be4af5 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -29,6 +29,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "tree-cfg.h"
 #include "langhooks.h"
+#include "optinfo.h"
+#include "cgraph.h" /* for selftests.  */
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h" /* for selftests.  */
+#include "backend.h"
+#include "gimple.h" /* for dump_user_location_t ctor.  */
+#include "rtl.h" /* for dump_user_location_t ctor.  */
+#include "dump-context.h"
+#include "tree-pass.h" /* for "current_pass".  */
+#include "selftest.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -36,18 +46,52 @@ along with GCC; see the file COPYING3.  If not see
    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
 
 static dump_flags_t pflags;		      /* current dump_flags */
-static dump_flags_t alt_flags;		      /* current opt_info flags */
 
 static void dump_loc (dump_flags_t, FILE *, source_location);
+
+/* Current -fopt-info output stream, if any, and flags.  */
+static FILE *alt_dump_file = NULL;
+static dump_flags_t alt_flags;
+
 static FILE *dump_open_alternate_stream (struct dump_file_info *);
 
 /* These are currently used for communicating between passes.
    However, instead of accessing them directly, the passes can use
    dump_printf () for dumps.  */
 FILE *dump_file = NULL;
-FILE *alt_dump_file = NULL;
 const char *dump_file_name;
 dump_flags_t dump_flags;
+bool dumps_are_enabled = false;
+
+
+/* Update the "dumps_are_enabled" global; to be called whenever dump_file
+   or alt_dump_file change.  */
+
+static void
+refresh_dumps_are_enabled ()
+{
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
+}
+
+/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+void
+set_dump_file (FILE *new_dump_file)
+{
+  dump_file = new_dump_file;
+  refresh_dumps_are_enabled ();
+}
+
+/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+static void
+set_alt_dump_file (FILE *new_alt_dump_file)
+{
+  alt_dump_file = new_alt_dump_file;
+  refresh_dumps_are_enabled ();
+}
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
   {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
@@ -74,14 +118,6 @@ static struct dump_file_info dump_files[TDI_end] =
   DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
 };
 
-/* Define a name->number mapping for a dump flag value.  */
-template <typename ValueType>
-struct kv_pair
-{
-  const char *const name;	/* the name of the value */
-  const ValueType value;	/* the value of the name */
-};
-
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
 static const kv_pair<dump_flags_t> dump_options[] =
@@ -132,7 +168,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 };
 
 /* Flags used for -fopt-info groups.  */
-static const kv_pair<optgroup_flags_t> optgroup_options[] =
+const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -358,9 +394,53 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
   return stream;
 }
 
+/* Implementation of "structured dumping API".  */
+
+/* Construct a dump_user_location_t from STMT (using its location and
+   hotness).  */
+
+dump_user_location_t::dump_user_location_t (gimple *stmt)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (stmt)
+    {
+      if (stmt->bb)
+	m_count = stmt->bb->count;
+      m_loc = gimple_location (stmt);
+    }
+}
+
+/* Construct a dump_user_location_t from an RTL instruction (using its
+   location and hotness).  */
+dump_user_location_t::dump_user_location_t (rtx_insn *insn)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (insn)
+    {
+      basic_block bb = BLOCK_FOR_INSN (insn);
+      if (bb)
+	m_count = bb->count;
+      m_loc = INSN_LOCATION (insn);
+    }
+}
+
+/* Construct from a function declaration.  This one requires spelling out
+   to avoid accidentally constructing from other kinds of tree.  */
+
+dump_user_location_t
+dump_user_location_t::from_function_decl (tree fndecl)
+{
+  gcc_assert (fndecl);
+
+  // FIXME: profile count for function?
+  return dump_user_location_t (profile_count (),
+			       DECL_SOURCE_LOCATION (fndecl));
+}
+
+
 /* Print source location on DFILE if enabled.  */
 
-void
+static void
 dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
 {
   if (dump_kind)
@@ -373,137 +453,363 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
                  DECL_SOURCE_FILE (current_function_decl),
                  DECL_SOURCE_LINE (current_function_decl),
                  DECL_SOURCE_COLUMN (current_function_decl));
+      /* Indentation based on scope depth.  */
+      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
     }
 }
 
+/* Implementation of dump_context methods.  */
+
+/* dump_context's dtor.  */
+
+dump_context::~dump_context ()
+{
+  delete m_pending;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
 void
-dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
+				dump_flags_t extra_dump_flags,
+				gimple *gs, int spc)
 {
   if (dump_file && (dump_kind & pflags))
     print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs, int spc)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
    DUMP_KIND is enabled.  */
 
 void
-dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		   tree t)
+dump_context::dump_generic_expr (dump_flags_t dump_kind,
+				 dump_flags_t extra_dump_flags,
+				 tree t)
 {
   if (dump_file && (dump_kind & pflags))
       print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
-}
 
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, extra_dump_flags);
+    }
+}
 
-/* Similar to dump_generic_expr, except additionally print the source
-   location.  */
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
+			      va_list ap)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Output a formatted message using FORMAT on appropriate dump streams.  */
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+				  const char *format, va_list ap)
 {
+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      dump_loc (dump_kind, dump_file, srcloc);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* Output VALUE in decimal to appropriate dump streams.  */
 
+template<unsigned int N, typename C>
 void
-dump_printf_loc (dump_flags_t dump_kind, source_location loc,
-		 const char *format, ...)
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
+  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
   if (dump_file && (dump_kind & pflags))
+    print_dec (value, dump_file, sgn);
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
     {
-      va_list ap;
-      dump_loc (dump_kind, dump_file, loc);
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
     }
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", node->dump_name ());
 
   if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", node->dump_name ());
+
+  if (optinfo_enabled_p ())
     {
-      va_list ap;
-      dump_loc (dump_kind, alt_dump_file, loc);
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
     }
 }
 
+/* Get the current dump scope-nesting depth.
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+dump_context::get_scope_depth () const
+{
+  return m_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Push a "scope" opinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_context::begin_scope (const char *name, const dump_location_t &loc)
+{
+  /* Specialcase, to avoid going through dump_printf_loc,
+     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
+
+  if (dump_file)
+    {
+      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      fprintf (dump_file, "=== %s ===\n", name);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      end_any_optinfo ();
+      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
+      info.add_printf ("=== %s ===", name);
+      info.emit ();
+    }
+
+  m_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_context::end_scope ()
+{
+  end_any_optinfo ();
+  m_scope_depth--;
+  optimization_records_maybe_pop_dump_scope ();
+}
+
+/* Return the optinfo currently being accumulated, creating one if
+   necessary.  */
+
+optinfo &
+dump_context::ensure_pending_optinfo ()
+{
+  if (!m_pending)
+    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
+  return *m_pending;
+}
+
+/* Start a new optinfo and return it, ending any optinfo that was already
+   accumulated.  */
+
+optinfo &
+dump_context::begin_next_optinfo (const dump_location_t &loc)
+{
+  end_any_optinfo ();
+  gcc_assert (m_pending == NULL);
+  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
+  return *m_pending;
+}
+
+/* End any optinfo that has been accumulated within this context, emitting
+   it as a diagnostic remark or to optimization records as appropriate.  */
+
+void
+dump_context::end_any_optinfo ()
+{
+  if (m_pending)
+    m_pending->emit ();
+  delete m_pending;
+  m_pending = NULL;
+}
+
+/* The current singleton dump_context, and its default.  */
+
+dump_context *dump_context::s_current = &dump_context::s_default;
+dump_context dump_context::s_default;
+
+/* Implementation of dump_* API calls, calling into dump_context
+   methods.  */
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_stmt, except additionally print source location.  */
+
+void
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
+   DUMP_KIND is enabled.  */
+
+void
+dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		   tree t)
+{
+  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
+}
+
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
+void
+dump_printf (dump_flags_t dump_kind, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  va_end (ap);
+}
+
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
+
+void
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		 const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  va_end (ap);
+}
+
 /* Output VALUE in decimal to appropriate dump streams.  */
 
 template<unsigned int N, typename C>
 void
 dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
-  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
-  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
-  if (dump_file && (dump_kind & pflags))
-    print_dec (value, dump_file, sgn);
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    print_dec (value, alt_dump_file, sgn);
+  dump_context::get ().dump_dec (dump_kind, value);
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -512,6 +818,45 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  dump_context::get ().dump_symtab_node (dump_kind, node);
+}
+
+/* Get the current dump scope-nesting depth.
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+get_dump_scope_depth ()
+{
+  return dump_context::get ().get_scope_depth ();
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Push a "scope" opinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_begin_scope (const char *name, const dump_location_t &loc)
+{
+  dump_context::get ().begin_scope (name, loc);
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_end_scope ()
+{
+  dump_context::get ().end_scope ();
+}
+
+/* End of implementation of "structured dumping API".  */
+
 /* Start a dump for PHASE. Store user-supplied dump flags in
    *FLAG_PTR.  Return the number of streams opened.  Set globals
    DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
@@ -541,7 +886,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
         }
       free (name);
       dfi->pstream = stream;
-      dump_file = dfi->pstream;
+      set_dump_file (dfi->pstream);
       /* Initialize current dump flags. */
       pflags = dfi->pflags;
     }
@@ -551,7 +896,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
     {
       dfi->alt_stream = stream;
       count++;
-      alt_dump_file = dfi->alt_stream;
+      set_alt_dump_file (dfi->alt_stream);
       /* Initialize current -fopt-info flags. */
       alt_flags = dfi->alt_flags;
     }
@@ -582,8 +927,8 @@ dump_finish (int phase)
 
   dfi->alt_stream = NULL;
   dfi->pstream = NULL;
-  dump_file = NULL;
-  alt_dump_file = NULL;
+  set_dump_file (NULL);
+  set_alt_dump_file (NULL);
   dump_flags = TDF_NONE;
   alt_flags = TDF_NONE;
   pflags = TDF_NONE;
@@ -1021,6 +1366,7 @@ dump_basic_block (dump_flags_t dump_kind, basic_block bb, int indent)
     dump_bb (dump_file, bb, indent, TDF_DETAILS);
   if (alt_dump_file && (dump_kind & alt_flags))
     dump_bb (alt_dump_file, bb, indent, TDF_DETAILS);
+  // TODO: should this also write to optinfo?
 }
 
 /* Dump FUNCTION_DECL FN as tree dump PHASE.  */
@@ -1059,3 +1405,179 @@ enable_rtl_dump_file (void)
 			    NULL);
   return num_enabled > 0;
 }
+
+#if CHECKING_P
+
+/* temp_dump_context's ctor.  Temporarily override the dump_context,
+   and the value of "flag_remarks" (to forcibly enable optinfo-generation).  */
+
+temp_dump_context::temp_dump_context (bool new_flag_remarks)
+: m_context (),
+  m_saved (&dump_context ().get ()),
+  m_saved_flag_remarks (flag_remarks)
+{
+  dump_context::s_current = &m_context;
+  flag_remarks = new_flag_remarks;
+}
+
+/* temp_dump_context's dtor.  Restore the saved values of dump_context and
+   "flag_remarks".  */
+
+temp_dump_context::~temp_dump_context ()
+{
+  dump_context::s_current = m_saved;
+  flag_remarks = m_saved_flag_remarks;
+}
+
+namespace selftest {
+
+/* Verify that the dump_location_t constructor captures the source location
+   at which it was called (provided that the build compiler is sufficiently
+   recent).  */
+
+static void
+test_impl_location ()
+{
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+  /* Default ctor.  */
+  {
+    dump_location_t loc;
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from a gimple.  */
+  {
+    dump_location_t loc ((gimple *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from an rtx_insn.  */
+  {
+    dump_location_t loc ((rtx_insn *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_TRUE (strstr (loc.get_impl_location ().m_file, "dumpfile.c"));
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+#endif
+}
+
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+static void
+assert_is_text (const optinfo_item *item, const char *expected_text)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
+  const optinfo_item_text * text_item
+    = static_cast <const optinfo_item_text *> (item);
+  ASSERT_STREQ (text_item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+static void
+assert_is_tree (const optinfo_item *item, tree expected_tree,
+		dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
+  const optinfo_item_tree * tree_item
+    = static_cast <const optinfo_item_tree *> (item);
+  ASSERT_EQ (tree_item->get_node (), expected_tree);
+  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+static void
+assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
+		  dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
+  const optinfo_item_gimple * gimple_item
+    = static_cast <const optinfo_item_gimple *> (item);
+  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
+  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls ()
+{
+  dump_location_t loc;
+
+  /* Tree.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    assert_is_text (info->get_item (0), "test of tree: ");
+    assert_is_tree (info->get_item (1), integer_zero_node, TDF_SLIM);
+  }
+
+  /* Gimple.  */
+  {
+    greturn *stmt = gimple_build_return (NULL);
+
+    temp_dump_context tmp (true);
+    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
+  }
+
+  /* poly_int.  */
+  {
+    temp_dump_context tmp (true);
+    dump_dec (MSG_NOTE, poly_int64 (42));
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_text (info->get_item (0), "42");
+  }
+
+  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
+     above.  */
+  {
+    /* MSG_OPTIMIZED_LOCATIONS.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_SUCCESS);
+    }
+
+    /* MSG_MISSED_OPTIMIZATION.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_FAILURE);
+    }
+  }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+dumpfile_c_tests ()
+{
+  test_impl_location ();
+  test_capture_of_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index f6ad670..96bdb30 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DUMPFILE_H
 #define GCC_DUMPFILE_H 1
 
+#include "profile-count.h"
 
 /* Different tree dump places.  When you add new tree dump places,
    extend the DUMP_FILES array in dumpfile.c.  */
@@ -273,22 +274,247 @@ extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
+
+/* Global variables used to communicate with passes.  */
+extern FILE *dump_file;
+extern dump_flags_t dump_flags;
+extern const char *dump_file_name;
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
+
+/* Return true if any of the dumps are enabled, false otherwise. */
+static inline bool
+dump_enabled_p (void)
+{
+  return __builtin_expect (dumps_are_enabled, false);
+}
+
+/* "Structured dumping" API.
+
+   The following API calls (which *don't* take a "FILE *")
+   write the output to zero or more locations:
+   (a) the active dump_file, if any
+   (b) the -fopt-info destination, if any
+   (c) to the "optinfo" destinations, if any:
+     (c.1) as diagnostic "remarks"
+     (c.2) as optimization records
+   For optinfos, the dump_*_loc mark the beginning of an optinfo
+   instance: all subsequent dump_* calls are consolidated into
+   that optinfo, until the next dump_*_loc call (or a change in
+   dump scope).
+
+   A group of dump_* calls should be guarded by:
+
+     if (dump_enabled_p ())
+
+   to minimize the work done for the common case where dumps
+   are disabled.  */
+
+/* A class for describing where in the user's source that a dump message
+   relates to, with various constructors for convenience.
+   In particular, this lets us associate dump messages
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.  */
+
+class dump_user_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_user_location_t () : m_count (), m_loc (UNKNOWN_LOCATION) {}
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_user_location_t (gimple *stmt);
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_user_location_t (rtx_insn *insn);
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information); it thus needs to be spelled out.  */
+  static dump_user_location_t
+  from_location_t (location_t loc)
+  {
+    return dump_user_location_t (profile_count (), loc);
+  }
+
+  /* Construct from a function declaration.  This one requires spelling out
+     to avoid accidentally constructing from other kinds of tree.  */
+  static dump_user_location_t
+  from_function_decl (tree fndecl);
+
+  profile_count get_count () const { return m_count; }
+  location_t get_location_t () const { return m_loc; }
+
+ private:
+  /* Private ctor from count and location, for use by from_location_t.  */
+  dump_user_location_t (profile_count count, location_t loc)
+    : m_count (count), m_loc (loc)
+  {}
+
+  profile_count m_count;
+  location_t m_loc;
+};
+
+/* A class for identifying where in the compiler's own source
+   (or a plugin) that a dump message is being emitted from.  */
+
+struct dump_impl_location_t
+{
+  dump_impl_location_t (
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+			const char *file = __builtin_FILE (),
+			int line = __builtin_LINE (),
+			const char *function = __builtin_FUNCTION ()
+#else
+			const char *file = __FILE__,
+			int line = __LINE__,
+			const char *function = NULL
+#endif
+  )
+  : m_file (file), m_line (line), m_function (function)
+  {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* A bundle of information for describing the location of a dump message:
+   (a) the source location and hotness within the user's code, together with
+   (b) the source location within the compiler/plugin.
+
+   The constructors use default parameters so that (b) gets sets up
+   automatically.
+
+   The upshot is that you can pass in e.g. a gimple * to dump_printf_loc,
+   and the dump call will automatically record where in GCC's source
+   code the dump was emitted from.  */
+
+class dump_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_location_t (const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t ()),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_location_t (gimple *stmt,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (stmt)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_location_t (rtx_insn *insn,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (insn)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a dump_user_location_t.  */
+  dump_location_t (const dump_user_location_t &user_location,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (user_location),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information), and thus requires spelling out.  */
+  static dump_location_t
+  from_location_t (location_t loc,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  {
+    return dump_location_t (dump_user_location_t::from_location_t (loc),
+			    impl_location);
+  }
+
+  const dump_user_location_t &
+  get_user_location () const { return m_user_location; }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_impl_location; }
+
+  location_t get_location_t () const
+  {
+    return m_user_location.get_location_t ();
+  }
+
+  profile_count get_count () const { return m_user_location.get_count (); }
+
+ private:
+  dump_user_location_t m_user_location;
+  dump_impl_location_t m_impl_location;
+};
+
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
-extern void dump_printf_loc (dump_flags_t, source_location,
-                             const char *, ...) ATTRIBUTE_PRINTF_3;
-extern void dump_function (int phase, tree fn);
+extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
+			     const char *, ...) ATTRIBUTE_PRINTF_3;
 extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
 extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
-				  gimple *, int);
+extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
+				  dump_flags_t, gimple *, int);
 extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
 
 template<unsigned int N, typename C>
 void dump_dec (dump_flags_t, const poly_int<N, C> &);
 
+/* Managing nested scopes, so that dumps can express the call chain
+   leading to a dump message.  */
+
+extern unsigned int get_dump_scope_depth ();
+extern void dump_begin_scope (const char *name, const dump_location_t &loc);
+extern void dump_end_scope ();
+
+/* Implementation detail of the AUTO_DUMP_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit dump
+   information about entering and exiting a collection of nested
+   function calls.  */
+
+class auto_dump_scope
+{
+ public:
+  auto_dump_scope (const char *name, dump_location_t loc)
+  {
+    if (dump_enabled_p ())
+      dump_begin_scope (name, loc);
+  }
+  ~auto_dump_scope ()
+  {
+    if (dump_enabled_p ())
+      dump_end_scope ();
+  }
+};
+
+/* A macro for calling:
+     dump_begin_scope (NAME, LOC);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
+
+#define AUTO_DUMP_SCOPE(NAME, LOC) \
+  auto_dump_scope scope (NAME, LOC)
+
+/* End of "structured dumping" API.  */
+
+extern void dump_function (int phase, tree fn);
+extern void print_combine_total_stats (void);
+extern bool enable_rtl_dump_file (void);
+
 /* In tree-dump.c  */
 extern void dump_node (const_tree, dump_flags_t, FILE *);
 
@@ -297,21 +523,13 @@ extern void dump_combine_total_stats (FILE *);
 /* In cfghooks.c  */
 extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
 
-/* Global variables used to communicate with passes.  */
-extern FILE *dump_file;
-extern FILE *alt_dump_file;
-extern dump_flags_t dump_flags;
-extern const char *dump_file_name;
-
-/* Return true if any of the dumps is enabled, false otherwise. */
-static inline bool
-dump_enabled_p (void)
-{
-  return (dump_file || alt_dump_file);
-}
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
 
 namespace gcc {
 
+/* A class for managing all of the various dump files used by the
+   optimization passes.  */
+
 class dump_manager
 {
 public:
diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
index 565fa83..a10b6aa 100644
--- a/gcc/fortran/gfc-diagnostic.def
+++ b/gcc/fortran/gfc-diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index c1d8442..c023274 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -347,8 +347,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 		  {
 		    if (dump_enabled_p ())
 		      {
-			location_t loc = gimple_location_safe (stmt);
-			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 					 "resolving virtual function address "
 					 "reference to function %s\n",
 					 targets.length () == 1
@@ -4060,8 +4059,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 	      tree lhs = gimple_call_lhs (stmt);
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				   "folding virtual function call to %s\n",
 		 		   targets.length () == 1
 		  		   ? targets[0]->name ()
diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index eb35263..2230af7 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -523,7 +523,7 @@ loop_cand::analyze_iloop_reduction_var (tree var)
 
   /* Handle and verify a series of stmts feeding the reduction op.  */
   if (single_use != next_def
-      && !check_reduction_path (UNKNOWN_LOCATION, m_loop, phi, next,
+      && !check_reduction_path (dump_user_location_t (), m_loop, phi, next,
 				gimple_assign_rhs_code (single_use)))
     return false;
 
@@ -1578,7 +1578,10 @@ bool
 tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    vec<ddr_p> ddrs)
 {
-  location_t loc = find_loop_location (m_loop_nest[0]);
+  dump_user_location_t loc = find_loop_location (m_loop_nest[0]);
+
+  AUTO_DUMP_SCOPE ("tree_loop_interchange::interchange", loc);
+
   bool changed_p = false;
   /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
      The overall effect is to push inner loop to outermost level in whole
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 405d9e3..c1f28cd 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2796,7 +2796,7 @@ dump_implicit_edges (pretty_printer *buffer, basic_block bb, int indent,
 /* Dumps basic block BB to buffer BUFFER with details described by FLAGS and
    indented by INDENT spaces.  */
 
-static void
+void
 gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
 		     dump_flags_t flags)
 {
diff --git a/gcc/gimple-pretty-print.h b/gcc/gimple-pretty-print.h
index 6ae6a3b..e7a5607 100644
--- a/gcc/gimple-pretty-print.h
+++ b/gcc/gimple-pretty-print.h
@@ -34,6 +34,8 @@ extern void print_gimple_expr (FILE *, gimple *, int, dump_flags_t = TDF_NONE);
 extern void pp_gimple_stmt_1 (pretty_printer *, gimple *, int, dump_flags_t);
 extern void gimple_dump_bb (FILE *, basic_block, int, dump_flags_t);
 extern void gimple_dump_bb_for_graph (pretty_printer *, basic_block);
+extern void gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
+				 dump_flags_t flags);
 extern void dump_ssaname_info_to_file (FILE *, tree, int);
 extern void percent_G_format (text_info *);
 
diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c
index b607b12..9e78465 100644
--- a/gcc/graphite-isl-ast-to-gimple.c
+++ b/gcc/graphite-isl-ast-to-gimple.c
@@ -1409,7 +1409,7 @@ scop_to_isl_ast (scop_p scop)
   isl_ctx_set_max_operations (scop->isl_context, old_max_operations);
   if (isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -1518,7 +1518,7 @@ graphite_regenerate_ast_isl (scop_p scop)
 
   if (t.codegen_error_p ())
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
 		       "loop nest not optimized, code generation error\n");
diff --git a/gcc/graphite-optimize-isl.c b/gcc/graphite-optimize-isl.c
index 456a797..35e9ac0 100644
--- a/gcc/graphite-optimize-isl.c
+++ b/gcc/graphite-optimize-isl.c
@@ -160,7 +160,7 @@ optimize_isl (scop_p scop)
   if (!scop->transformed_schedule
       || isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -182,7 +182,7 @@ optimize_isl (scop_p scop)
 
   if (same_schedule)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_NOTE, loc,
 		       "loop nest not optimized, optimized schedule is "
diff --git a/gcc/graphite.c b/gcc/graphite.c
index bcf4828..ddf16a8 100644
--- a/gcc/graphite.c
+++ b/gcc/graphite.c
@@ -412,7 +412,7 @@ graphite_transform_loops (void)
 	changed = true;
 	if (graphite_regenerate_ast_isl (scop))
 	  {
-	    location_t loc = find_loop_location
+	    dump_user_location_t loc = find_loop_location
 	      (scops[i]->scop_info->region.entry->dest->loop_father);
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
 			     "loop nest optimized\n");
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 308b6e6..e99d8cc 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -3755,8 +3755,7 @@ ipa_devirt (void)
 	      {
 		if (dump_enabled_p ())
                   {
-                    location_t locus = gimple_location_safe (e->call_stmt);
-                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
 				     "speculatively devirtualizing call "
 				     "in %s to %s\n",
 				     n->dump_name (),
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index daada4d..bc02fd0 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -2843,8 +2843,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 	    {
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (ie->call_stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 				   "discovered direct call non-invariant %s\n",
 				   ie->caller->dump_name ());
 		}
@@ -2854,8 +2853,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 
           if (dump_enabled_p ())
 	    {
-	      location_t loc = gimple_location_safe (ie->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 			       "discovered direct call to non-function in %s, "
 			       "making it __builtin_unreachable\n",
 			       ie->caller->dump_name ());
@@ -2943,9 +2941,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
      }
   if (dump_enabled_p ())
     {
-      location_t loc = gimple_location_safe (ie->call_stmt);
-
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 		       "converting indirect call in %s to direct call to %s\n",
 		       ie->caller->name (), callee->name ());
     }
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 82fc334..3b6b5e5 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -225,13 +225,8 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
 	  if (dump_enabled_p ())
-            {
-	      location_t locus;
-	      if (edge->call_stmt)
-		locus = gimple_location (edge->call_stmt);
-	      else
-		locus = UNKNOWN_LOCATION;
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	    {
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->dump_name (),
 			       target->dump_name ());
diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c
index 5a03932..48bbda0 100644
--- a/gcc/loop-unroll.c
+++ b/gcc/loop-unroll.c
@@ -189,7 +189,7 @@ static rtx get_expansion (struct var_to_expand *);
    appropriate given the dump or -fopt-info settings.  */
 
 static void
-report_unroll (struct loop *loop, location_t locus)
+report_unroll (struct loop *loop, dump_location_t locus)
 {
   dump_flags_t report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS;
 
@@ -220,7 +220,7 @@ decide_unrolling (int flags)
   FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
     {
       loop->lpt_decision.decision = LPT_NONE;
-      location_t locus = get_loop_location (loop);
+      dump_user_location_t locus = get_loop_location (loop);
 
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, locus,
diff --git a/gcc/omp-grid.c b/gcc/omp-grid.c
index ffa301e..6edc92f 100644
--- a/gcc/omp-grid.c
+++ b/gcc/omp-grid.c
@@ -91,7 +91,7 @@ struct grid_prop
   bool tiling;
   /* Location of the target construct for optimization information
      messages.  */
-  location_t target_loc;
+  dump_user_location_t target_loc;
   /* The collapse clause of the involved loops.  Collapse value of all of them
      must be the same for gridification to take place.  */
   size_t collapse;
@@ -177,10 +177,10 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "%s construct "
 				   "contains multiple OpenMP constructs\n",
 				   name);
-		  dump_printf_loc (MSG_NOTE, gimple_location (*ret),
+		  dump_printf_loc (MSG_NOTE, *ret,
 				   "The first OpenMP construct within "
 				   "a parallel\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "The second OpenMP construct within "
 				   "a parallel\n");
 		}
@@ -195,7 +195,7 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "%s construct contains "
 			       "a complex statement\n", name);
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "gridification\n");
 	    }
@@ -286,7 +286,7 @@ grid_find_ungridifiable_statement (gimple_stmt_iterator *gsi,
    loop that is evaluated for possible gridification.  */
 
 static bool
-grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
+grid_parallel_clauses_gridifiable (gomp_parallel *par, dump_user_location_t tloc)
 {
   tree clauses = gimple_omp_parallel_clauses (par);
   while (clauses)
@@ -300,7 +300,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 			       GRID_MISSED_MSG_PREFIX "because there is "
 			       "a num_threads clause of the parallel "
 			       "construct\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a num_threads clause\n");
 	    }
 	  return false;
@@ -311,7 +311,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, tloc,
 			       GRID_MISSED_MSG_PREFIX "a reduction clause "
 			       "is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a reduction clause\n");
 	    }
 	  return false;
@@ -341,7 +341,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			   GRID_MISSED_MSG_PREFIX "the inner loop "
 			   "loop bounds computation contains a complex "
 			   "statement\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -361,7 +361,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the inner loop "
 				   "has a non-automatic schedule clause\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+		  dump_printf_loc (MSG_NOTE, gfor,
 				   "Loop construct has a non automatic "
 				   "schedule clause\n");
 		}
@@ -375,7 +375,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "a reduction "
 			       "clause is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct has a reduction schedule "
 			       "clause\n");
 	    }
@@ -404,7 +404,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			     GRID_MISSED_MSG_PREFIX "the inner loop contains "
 			     "statement %s which cannot be transformed\n",
 			     gimple_code_name[(int) gimple_code (bad)]);
-	  dump_printf_loc (MSG_NOTE, gimple_location (bad),
+	  dump_printf_loc (MSG_NOTE, bad,
 			   "This statement cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -422,7 +422,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 static bool
 grid_dist_follows_simple_pattern (gomp_for *dist, grid_prop *grid)
 {
-  location_t tloc = grid->target_loc;
+  dump_user_location_t tloc = grid->target_loc;
   gimple *stmt = grid_find_single_omp_among_assignments (gimple_omp_body (dist),
 							 grid, "distribute");
   gomp_parallel *par;
@@ -468,7 +468,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop is not "
 			   "a simple for loop\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "This statement is not a simple for loop\n");
 	}
       return false;
@@ -484,7 +484,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop does not "
 			   "have use the same collapse clause\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct uses a different collapse clause\n");
 	}
       return false;
@@ -524,7 +524,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute and "
 			       "an internal loop do not agree on tile size\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct does not seem to loop over "
 			       "a tile size\n");
 	    }
@@ -636,7 +636,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the distribute "
 				   "construct contains a try..catch region\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (try_stmt),
+		  dump_printf_loc (MSG_NOTE, try_stmt,
 				   "This statement cannot be analyzed for "
 				   "tiled gridification\n");
 		}
@@ -661,7 +661,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a call\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -677,7 +677,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a parallel "
 				   "construct contains another parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This parallel construct is nested in "
 				   "another one\n");
 		}
@@ -698,7 +698,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a loop "
 				   "construct is not nested within a parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This loop construct is not nested in "
 				   "a parallel construct\n");
 		}
@@ -714,7 +714,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a complex statement\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -734,7 +734,7 @@ grid_target_follows_gridifiable_pattern (gomp_target *target, grid_prop *grid)
   if (gimple_omp_target_kind (target) != GF_OMP_TARGET_KIND_REGION)
     return false;
 
-  location_t tloc = gimple_location (target);
+  dump_user_location_t tloc = target;
   grid->target_loc = tloc;
   gimple *stmt
     = grid_find_single_omp_among_assignments (gimple_omp_body (target),
@@ -1257,14 +1257,13 @@ grid_attempt_target_gridification (gomp_target *target,
 				   gbind *tgt_bind)
 {
   /* removed group_size */
-  grid_prop grid;
-  memset (&grid, 0, sizeof (grid));
+  grid_prop grid = {};
   if (!target || !grid_target_follows_gridifiable_pattern (target, &grid))
     return;
 
   location_t loc = gimple_location (target);
   if (dump_enabled_p ())
-    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, target,
 		     "Target construct will be turned into a gridified HSA "
 		     "kernel\n");
 
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 2c371e5..48ecac5 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,6 +105,7 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
+	  test_flag("Remark", flags,  " | CL_REMARK") \
 	  test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
 	sub( "^0 \\| ", "", result )
 	return result
diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
new file mode 100644
index 0000000..5320379
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,317 @@
+/* Emit optimization information as "remark" diagnostics.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "gimple.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-internal.h"
+#include "dump-context.h"
+#include "selftest.h"
+#include "context.h"
+#include "pass_manager.h"
+
+/* Print the items within OPTINFO to PP (as part of a remark).  */
+
+static void
+print_optinfo_items (pretty_printer *pp, const optinfo *optinfo)
+{
+  bool show_color = pp_show_color (pp);
+
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text
+	      = (const optinfo_item_text *)item;
+	    pp_string (pp, as_text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree
+	      = (const optinfo_item_tree *)item;
+	    pp_begin_quote (pp, show_color);
+	    dump_generic_node (pp, as_tree->get_node (), 0, TDF_DETAILS,
+			       false);
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    pp_begin_quote (pp, show_color);
+	    pp_gimple_stmt_1 (pp, stmt, 0, TDF_SLIM);
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    pp_begin_quote (pp, show_color);
+	    pp_string (pp, node->dump_name ());
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	}
+    }
+}
+
+/* Print PASS to PP (as part of a remark).  */
+
+static void
+print_pass (pretty_printer *pp, opt_pass *pass)
+{
+  if (pass == NULL)
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp, colorize_start (show_color,
+				 diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_string (pp, "pass=");
+  pp_string (pp, pass->name);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print COUNT to PP (as part of a remark).  */
+
+static void
+print_count (pretty_printer *pp, profile_count count)
+{
+  if (!count.initialized_p ())
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp,
+	     colorize_start (show_color,
+			     diagnostic_get_color_for_kind (DK_NOTE)));
+  pp_string (pp, "count(");
+  pp_string (pp, profile_quality_as_string (count.quality ()));
+  pp_string (pp, ")=");
+  pp_scalar (pp, "%li", count.to_gcov_type ());
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print IMPL_LOC to PP (as part of a remark).  */
+
+static void
+print_impl_location (pretty_printer *pp, const dump_impl_location_t &impl_loc)
+{
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp,
+	     colorize_start (show_color,
+			     diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_printf (pp, "%s:%i", impl_loc.m_file, impl_loc.m_line);
+  if (impl_loc.m_function)
+    pp_printf (pp, ":%s", impl_loc.m_function);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print OPTINFO to PP.  */
+
+static void
+print_optinfo_as_remark (pretty_printer *pp, const optinfo *optinfo)
+{
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_dump_scope_depth (); i > 0; i--)
+    pp_space (pp);
+
+  /* Print the items into PP.  */
+  print_optinfo_items (pp, optinfo);
+
+  /* Add metadata: which pass?  */
+  if (flag_diagnostics_show_remark_pass)
+    print_pass (pp, optinfo->get_pass ());
+
+  /* Add metadata: hotness.  */
+  if (flag_diagnostics_show_remark_hotness)
+    print_count (pp, optinfo->get_count ());
+
+  /* Add metadata: where was this emitted from.  */
+  if (flag_diagnostics_show_remark_origin)
+    print_impl_location (pp, optinfo->get_impl_location ());
+}
+
+/* Subclass of pretty_printer for building up the text of a remark.  */
+
+class remark_printer : public pretty_printer
+{
+public:
+  remark_printer (bool show_color_);
+};
+
+/* remark_printer's ctor.  */
+
+remark_printer::remark_printer (bool show_color_)
+{
+  pp_needs_newline (this) = true;
+  pp_translate_identifiers (this) = false;
+  pp_show_color (this) = show_color_;
+}
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  remark_printer pp (pp_show_color (global_dc->printer));
+
+  print_optinfo_as_remark (&pp, optinfo);
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that print_optinfo_items works.  */
+
+static void
+test_print_optinfo_items ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+  optinfo *info = tmp.get_pending_optinfo ();
+  ASSERT_TRUE (info != NULL);
+
+  /* Avoid introducing locale-specific differences in the results
+     by hardcoding open_quote and close_quote.  */
+  auto_fix_quotes fix_quotes;
+
+  remark_printer pp (false);
+  print_optinfo_items (&pp, info);
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, "test of tree: `0'");
+}
+
+/* Verify that print_pass works.  */
+
+static void
+test_print_pass ()
+{
+  /* NULL pass.  */
+  {
+    remark_printer pp (false);
+    print_pass (&pp, NULL);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, "");
+  }
+
+  /* Non-NULL pass.  */
+  {
+    remark_printer pp (false);
+    opt_pass *pass = make_pass_ipa_increase_alignment (NULL);
+    print_pass (&pp, pass);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [pass=increase_alignment]");
+    delete pass;
+  }
+}
+
+/* Verify that print_count works.  */
+
+static void
+test_print_count ()
+{
+  remark_printer pp (false);
+  print_count (&pp, profile_count ());
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, " [count(uninitialized)=0]");
+}
+
+/* Verify that print_impl_location works.  */
+
+static void
+test_print_impl_location ()
+{
+  /* Non-NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, "funcname");
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42:funcname]");
+  }
+
+  /* NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, NULL);
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42]");
+  }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_diagnostics_cc_tests ()
+{
+  test_print_optinfo_items ();
+  test_print_pass ();
+  test_print_count ();
+  test_print_impl_location ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
new file mode 100644
index 0000000..820cefd
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.h
@@ -0,0 +1,26 @@
+/* Emit optimization information as "remark" diagnostics.
+   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_OPTINFO_EMIT_DIAGNOSTICS_H
+#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
+
+extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..93de887
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,773 @@
+/* Emit optimization information as JSON files.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+#include "version.h"
+#include "context.h"
+#include "pass_manager.h"
+#include "selftest.h"
+#include "dump-context.h"
+
+/* A class for writing out optimization records in JSON format.  */
+
+class optrecord_json_writer
+{
+public:
+  optrecord_json_writer ();
+  ~optrecord_json_writer ();
+  void write () const;
+  void add_record (const optinfo *optinfo);
+  void pop_scope ();
+  void record_function_state (function *fn, opt_pass *pass);
+
+  void add_record (json::object *obj);
+  json::object *impl_location_to_json (dump_impl_location_t loc);
+  json::object *location_to_json (location_t loc);
+  json::object *profile_count_to_json (profile_count count);
+  json::string *get_id_value_for_pass (opt_pass *pass);
+  json::object *pass_to_json (opt_pass *pass);
+  json::value *inlining_chain_to_json (location_t loc);
+  json::object *optinfo_to_json (const optinfo *optinfo);
+  void add_pass_list (json::array *arr, opt_pass *pass);
+  void add_bb_to_json (basic_block bb, json::array *cfg_obj);
+  void add_function_state_to_json (function *fun, json::object *fn_obj);
+  json::object *function_state_to_json (function *fn, opt_pass *pass);
+
+private:
+  /* The root value for the JSON file.
+     Currently the JSON values are stored in memory, and flushed when the
+     compiler exits.  It would probably be better to simply write out
+     the JSON as we go.  */
+  json::array *m_root_tuple;
+
+  /* The currently open scopes, for expressing nested optimization records.  */
+  vec<json::array *> m_scopes;
+};
+
+/* optrecord_json_writer's ctor.  Populate the top-level parts of the
+   in-memory JSON representation.  */
+
+optrecord_json_writer::optrecord_json_writer ()
+  : m_root_tuple (NULL), m_scopes ()
+{
+  m_root_tuple = new json::array ();
+
+  /* Populate with metadata; compare with toplev.c: print_version.  */
+  json::object *metadata = new json::object ();
+  m_root_tuple->append (metadata);
+  metadata->set ("format", new json::string ("1"));
+  json::object *generator = new json::object ();
+  metadata->set ("generator", generator);
+  generator->set ("name", new json::string (lang_hooks.name));
+  generator->set ("pkgversion", new json::string (pkgversion_string));
+  generator->set ("version", new json::string (version_string));
+  /* TARGET_NAME is passed in by the Makefile.  */
+  generator->set ("target", new json::string (TARGET_NAME));
+
+  /* TODO: capture command-line?
+     see gen_producer_string in dwarf2out.c (currently static).  */
+
+  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
+
+  json::array *passes = new json::array ();
+  m_root_tuple->append (passes);
+
+  /* Call add_pass_list for all of the pass lists.  */
+  {
+#define DEF_PASS_LIST(LIST) \
+    add_pass_list (passes, g->get_passes ()->LIST);
+    GCC_PASS_LISTS
+#undef DEF_PASS_LIST
+  }
+
+  json::array *records = new json::array ();
+  m_root_tuple->append (records);
+
+  m_scopes.safe_push (records);
+}
+
+/* optrecord_json_writer's ctor.
+   Delete the in-memory JSON representation.  */
+
+optrecord_json_writer::~optrecord_json_writer ()
+{
+  delete m_root_tuple;
+}
+
+/* Choose an appropriate filename, and write the saved records to it.  */
+
+void
+optrecord_json_writer::write () const
+{
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      m_root_tuple->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+}
+
+/* Add a record for OPTINFO to the queue of records to be written.  */
+
+void
+optrecord_json_writer::add_record (const optinfo *optinfo)
+{
+  json::object *obj = optinfo_to_json (optinfo);
+
+  add_record (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      m_scopes.safe_push (children);
+    }
+}
+
+/* Private methods of optrecord_json_writer.  */
+
+/* Add record OBJ to the the innermost scope.  */
+
+void
+optrecord_json_writer::add_record (json::object *obj)
+{
+  /* Add to innermost scope.  */
+  gcc_assert (m_scopes.length () > 0);
+  m_scopes[m_scopes.length () - 1]->append (obj);
+}
+
+/* Pop the innermost scope.  */
+
+void
+optrecord_json_writer::pop_scope ()
+{
+  m_scopes.pop ();
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  if (loc.m_function)
+    obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+json::object *
+optrecord_json_writer::profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Get a string for use when referring to PASS in the saved optimization
+   records.  */
+
+json::string *
+optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
+{
+  pretty_printer pp;
+  /* this is host-dependent, but will be consistent for a given host.  */
+  pp_pointer (&pp, static_cast<void *> (pass));
+  return new json::string (pp_formatted_text (&pp));
+}
+
+/* Create a JSON object representing PASS.  */
+
+json::object *
+optrecord_json_writer::pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("id", get_id_value_for_pass (pass));
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+json::value *
+optrecord_json_writer::inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  const char *printable_name
+	    = lang_hooks.decl_printable_name (fndecl, 2);
+	  obj->set ("fndecl", new json::string (printable_name));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* Create a JSON object representing OPTINFO.  */
+
+json::object *
+optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
+{
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text = (const optinfo_item_text *)item;
+	    message->append (new json::string (as_text->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree = (const optinfo_item_tree *)item;
+	    tree node = as_tree->get_node ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+	         dump_generic_expr (MSG_NOTE, flags, node);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+	         print_generic_expr (alt_dump_file, node, dump_flags | extra_dump_flags);
+	       which is:
+		 maybe_init_pretty_print (file);
+		 dump_generic_node (tree_pp, node, 0, flags, false);
+		 pp_flush (tree_pp);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_translate_identifiers (&pp) = false;
+
+	    dump_generic_node (&pp, node, 0, as_tree->get_flags (), false);
+
+	    item->set ("expr", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the node.  */
+	    if (EXPR_HAS_LOCATION (node))
+	      item->set ("location", location_to_json (EXPR_LOCATION (node)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    json::object *item = new json::object ();
+	    /* Capture output of:
+		 dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	       into a text buffer, and add it as a attribute of ITEM.
+	       This is:
+		 print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
+	       which is:
+		 pp_needs_newline (&buffer) = true;
+		 buffer.buffer->stream = file;
+		 pp_gimple_stmt_1 (&buffer, g, spc, flags);
+		 pp_newline_and_flush (&buffer);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_gimple_stmt_1 (&pp, stmt, 0, TDF_SLIM);
+	    item->set ("stmt", new json::string (pp_formatted_text (&pp)));
+
+	    /* Capture any location for the stmt.  */
+	    if (gimple_location (stmt))
+	      item->set ("location", location_to_json (gimple_location (stmt)));
+
+	    message->append (item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    json::object *item = new json::object ();
+	    item->set ("name", new json::string (node->name ()));
+	    item->set ("order", new json::number (node->order));
+	    if (DECL_SOURCE_LOCATION (node->decl) != UNKNOWN_LOCATION)
+	      item->set ("location",
+			 location_to_json (DECL_SOURCE_LOCATION (node->decl)));
+	    message->append (item);
+	  }
+	  break;
+	}
+   }
+
+  if (optinfo->get_pass ())
+    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
+
+  profile_count count = optinfo->get_count ();
+  if (count.initialized_p ())
+    obj->set ("count", profile_count_to_json (count));
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  location_t loc = optinfo->get_location_t ();
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  return obj;
+}
+
+/* Add a json description of PASS and its siblings to ARR, recursing into
+   child passes (adding their descriptions within a "children" array).  */
+
+void
+optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
+{
+  do
+    {
+      json::object *pass_obj = pass_to_json (pass);
+      arr->append (pass_obj);
+      if (pass->sub)
+	{
+	  json::array *sub = new json::array ();
+	  pass_obj->set ("children", sub);
+	  add_pass_list (sub, pass->sub);
+	}
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* Generate a JSON object describing BB, adding it to CFG_OBJ.
+   Compare with dump_bb, dump_bb_info etc.  */
+
+void
+optrecord_json_writer::add_bb_to_json (basic_block bb, json::array *cfg_obj)
+{
+  json::object *bb_obj = new json::object ();
+  cfg_obj->append (bb_obj);
+
+  /* Basic metadata.  Compare with dump_bb_info.  */
+
+  bb_obj->set ("index", new json::number (bb->index));
+  if (bb->count.initialized_p ())
+    bb_obj->set ("count", profile_count_to_json (bb->count));
+
+  /* Flags.  */
+  static const char * const bb_bitnames[] =
+    {
+#define DEF_BASIC_BLOCK_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+      NULL
+#undef DEF_BASIC_BLOCK_FLAG
+    };
+  const unsigned n_bitnames = sizeof (bb_bitnames) / sizeof (char *);
+  json::array *bb_flags = new json::array ();
+  bb_obj->set ("flags", bb_flags);
+  for (unsigned i = 0; i < n_bitnames; i++)
+    if (bb->flags & (1 << i))
+      bb_flags->append (new json::string (bb_bitnames[i]));
+
+  bb_obj->set ("discriminator", new json::number (bb->discriminator));
+
+  if (bb->index >= NUM_FIXED_BLOCKS)
+    {
+      /* For now, just capture all of the statements as one string.  */
+      pretty_printer pp;
+      pp.buffer->stream = NULL;
+      pp.buffer->flush_p = false;
+      pp_needs_newline (&pp) = true;
+      gimple_dump_bb_buff (&pp, bb, 0, TDF_NONE);
+      bb_obj->set ("stmts", new json::string (pp_formatted_text (&pp)));
+    }
+
+  json::array *succs = new json::array ();
+  bb_obj->set ("succs", succs);
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      /* compare with dump_edge_info.  */
+      json::object *edge_obj = new json::object ();
+      succs->append (edge_obj);
+      edge_obj->set ("dest", new json::number (e->dest->index));
+
+      /* Edge flags.  */
+      static const char * const bitnames[] =
+	{
+#define DEF_EDGE_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+	  NULL
+#undef DEF_EDGE_FLAG
+	};
+
+      json::array *edge_flags = new json::array ();
+      edge_obj->set ("flags", edge_flags);
+
+      gcc_assert (e->flags <= EDGE_ALL_FLAGS);
+      int flags = e->flags;
+      for (int i = 0; flags; i++)
+	if (flags & (1 << i))
+	  {
+	    flags &= ~(1 << i);
+	    edge_flags->append (new json::string (bitnames[i]));
+	  }
+    }
+}
+
+/* Populate FN_OBJ based on FUN.
+   Compare with dump_function_to_file.  */
+
+void
+optrecord_json_writer::add_function_state_to_json (function *fun,
+						   json::object *fn_obj)
+{
+  tree old_current_fndecl = current_function_decl;
+  basic_block bb;
+  tree fndecl = fun->decl;
+
+  current_function_decl = fndecl;
+
+  if (fun && fun->decl == fndecl
+      && fun->cfg
+      && basic_block_info_for_fn (fun))
+    {
+      json::array *cfg_obj = new json::array ();
+      fn_obj->set ("cfg", cfg_obj);
+
+      FOR_ALL_BB_FN (bb, fun)
+	add_bb_to_json (bb, cfg_obj);
+    }
+
+  current_function_decl = old_current_fndecl;
+}
+
+/* Add a record describing the state of FN after PASS to the queue of
+   records to be written.  */
+
+
+void
+optrecord_json_writer::record_function_state (function *fn,
+					      opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  json::object *obj = function_state_to_json (fn, pass);
+  add_record (obj);
+}
+
+/* Create a JSON object representing the state of FN after PASS.  */
+
+json::object *
+optrecord_json_writer::function_state_to_json (function *fn,
+					       opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  tree fndecl = fn->decl;
+
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("state"));
+  obj->set ("pass", get_id_value_for_pass (pass));
+  const char *fnname = get_fnname_from_decl (fndecl);
+  obj->set ("function", new json::string (fnname));
+
+  if (fn->curr_properties & PROP_trees)
+    {
+      add_function_state_to_json (fn, obj);
+    }
+  else
+    {
+      // TODO?  RTL equivalent when dumping is:
+      //print_rtl_with_bb (dump_file, get_insns (), dump_flags);
+    }
+  return obj;
+}
+
+/* File-level interface to rest of compiler (to avoid exposing
+   class optrecord_json_writer outside of this file).  */
+
+static optrecord_json_writer *the_json_writer;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  the_json_writer = new optrecord_json_writer ();
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go, before cleaning
+   up.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->write ();
+
+  delete the_json_writer;
+  the_json_writer = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return the_json_writer != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->add_record (optinfo);
+}
+
+/* Handling for the end of a dump scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_dump_scope ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->pop_scope ();
+}
+
+/* If optimization records were requested, then  add a record describing the
+   state of FN after PASS to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_function (function *fn,
+					    opt_pass *pass)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->record_function_state (fn, pass);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that we can build a JSON optimization record from dump_*
+   calls.  */
+
+static void
+test_building_json_from_dump_calls ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+  optinfo *info = tmp.get_pending_optinfo ();
+  ASSERT_TRUE (info != NULL);
+  ASSERT_EQ (info->num_items (), 2);
+
+  optrecord_json_writer writer;
+  json::object *json_obj = writer.optinfo_to_json (info);
+  ASSERT_TRUE (json_obj != NULL);
+
+  /* Verify that the json is sane.  */
+  char *json_str = json_obj->to_str ();
+  ASSERT_STR_CONTAINS (json_str, "impl_location");
+  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
+  ASSERT_STR_CONTAINS (json_str,
+		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
+
+  free (json_str);
+  delete json_obj;
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_json_cc_tests ()
+{
+  test_building_json_from_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..9f2ceaa
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,39 @@
+/* Emit optimization information as JSON files.
+   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_OPTINFO_EMIT_JSON_H
+#define GCC_OPTINFO_EMIT_JSON_H
+
+class optinfo;
+struct opt_pass;
+
+extern void optimization_records_start ();
+extern void optimization_records_finish ();
+
+// FIXME: maybe make this inline?
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_dump_scope ();
+extern void optimization_records_maybe_record_function (function *fun,
+							opt_pass *pass);
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..3f267ee
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,145 @@
+/* Implementation details of optinfo.
+   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_OPTINFO_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+  void trim_trailing_whitespace ();
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node, dump_flags_t dump_flags)
+    : m_node (node), m_dump_flags (dump_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  tree m_node;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
+    : m_stmt (stmt), m_dump_flags (dump_flags) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  gimple *m_stmt;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked, as it's assumed that instances
+   are short-lived.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..015248b
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,254 @@
+/* Optimization information.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-emit-json.h"
+#include "optinfo-internal.h"
+#include "selftest.h"
+
+/* Remove any trailing whitespace characters from this text item.
+   Primarily for use in stripping trailing newline characters when
+   emitting remarks (since the diagnostic subsystem doesn't expect
+   trailing newlines in messages).  */
+
+void
+optinfo_item_text::trim_trailing_whitespace ()
+{
+  size_t len = strlen (m_text);
+  if (len == 0)
+    return;
+
+  size_t new_len = len;
+  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
+    new_len--;
+
+  if (new_len == len)
+    return;
+
+  if (m_owned)
+    m_text[new_len] = '\0';
+  else
+    {
+      m_text = xstrndup (m_text, new_len);
+      m_owned = true;
+    }
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit ()
+{
+  /* Eliminate any trailing whitespace.  */
+  while (m_items.length () > 0)
+    {
+      optinfo_item *last_item = m_items[m_items.length () - 1];
+      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
+	break;
+
+      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
+      last_text->trim_trailing_whitespace ();
+
+      if (strlen (last_text->get_text ()) > 0)
+	break;
+
+      m_items.pop ();
+      delete last_item;
+    }
+
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
+
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
+}
+
+/* Update the optinfo's kind based on DUMP_KIND.  */
+
+void
+optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    m_kind = OPTINFO_KIND_NOTE;
+}
+
+/* Append a string literal to this optinfo.  */
+
+void
+optinfo::add_string (const char *str)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (str), false);
+  m_items.safe_push (item);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  add_printf_va (format, ap);
+  va_end (ap);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf_va (const char *format, va_list ap)
+{
+  char *formatted_text = xvasprintf (format, ap);
+  optinfo_item *item
+    = new optinfo_item_text (formatted_text, true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo.  */
+
+void
+optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
+}
+
+/* Append a tree node to this optinfo.  */
+
+void
+optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
+}
+
+/* Append a symbol table node to this optinfo.  */
+
+void
+optinfo::add_symtab_node (symtab_node *node)
+{
+  m_items.safe_push (new optinfo_item_symtab_node (node));
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   optinfo.  */
+
+void
+optinfo::add_dec (const wide_int_ref &wi, signop sgn)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (wi, buf, sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_items.safe_push (item);
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  return optimization_records_enabled_p () || flag_remarks;
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return optimization_records_enabled_p ();
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that optinfo_item_text::trim_trailing_whitespace turns
+   INPUT into EXPECTED.  */
+
+static void
+test_trim_trailing_whitespace (const char *input, const char *expected)
+{
+  optinfo_item_text item (const_cast <char *> (input), false);
+  item.trim_trailing_whitespace ();
+  ASSERT_STREQ (item.get_text (), expected);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_cc_tests ()
+{
+  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
+  test_trim_trailing_whitespace ("", "");
+  test_trim_trailing_whitespace ("\n", "");
+  test_trim_trailing_whitespace ("foo", "foo");
+  test_trim_trailing_whitespace ("foo\n", "foo");
+  test_trim_trailing_whitespace ("foo\n\n", "foo");
+  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..ed7abbd
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,147 @@
+/* Optimization information.
+   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_OPTINFO_H
+#define GCC_OPTINFO_H
+
+#include "unique-ptr.h"
+#include "dumpfile.h"
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file via "-fsave-optimization-record"
+
+   They are generated in response to calls to calls to the "dump_*" API
+   in dumpfile.h
+
+   Building an optinfo instance is non-trivial, so all usage should be
+   guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.
+
+   They're intended to be be short-lived; in particular, there's no
+   interaction with GTY: it's assumed that no GC happens during the
+   lifetime of an optinfo.  */
+
+/* Forward decls.  */
+struct opt_pass;
+class optinfo_item; /* optinfo-internal.h.  */
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.  */
+
+class optinfo
+{
+  friend class dump_context;
+
+ public:
+  optinfo (const dump_location_t &loc,
+	   enum optinfo_kind kind,
+	   opt_pass *pass)
+  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
+  {}
+  ~optinfo ();
+
+  const dump_user_location_t &
+  get_user_location () const { return m_loc.get_user_location (); }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_loc.get_impl_location (); }
+
+  enum optinfo_kind get_kind () const { return m_kind; }
+  opt_pass *get_pass () const { return m_pass; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const { return m_loc.get_location_t (); }
+  profile_count get_count () const { return m_loc.get_count (); }
+
+ private:
+  void emit ();
+
+  /* Pre-canned ways of manipulating the optinfo, for use by friend class
+     dump_context.  */
+  void handle_dump_file_kind (dump_flags_t);
+  void add_string (const char *str);
+  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
+  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
+  void add_tree (tree node, dump_flags_t dump_flags);
+  void add_symtab_node (symtab_node *node);
+  void add_dec (const wide_int_ref &wi, signop sgn);
+
+  template<unsigned int N, typename C>
+  void add_poly_int (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      add_dec (value.coeffs[0], sgn);
+    else
+      {
+	add_string ("[");
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    add_dec (value.coeffs[i], sgn);
+	    add_string (i == N - 1 ? "]" : ",");
+	  }
+      }
+  }
+
+ private:
+  dump_location_t m_loc;
+  enum optinfo_kind m_kind;
+  opt_pass *m_pass;
+  auto_vec <optinfo_item *> m_items;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/opts.c b/gcc/opts.c
index 33efcc0..8029c08 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
 	case CL_WARNING:
 	  description = _("The following options control compiler warning messages");
 	  break;
+	case CL_REMARK:
+	  description = _("The following options control compiler remarks");
+	  break;
 	case CL_OPTIMIZATION:
 	  description = _("The following options control optimizations");
 	  break;
@@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
 	      { "optimizers", CL_OPTIMIZATION },
 	      { "target", CL_TARGET },
 	      { "warnings", CL_WARNING },
+	      { "remarks", CL_REMARK },
 	      { "undocumented", CL_UNDOCUMENTED },
 	      { "params", CL_PARAMS },
 	      { "joined", CL_JOINED },
diff --git a/gcc/opts.h b/gcc/opts.h
index 3c4065ea..d9df788 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -137,20 +137,21 @@ extern const unsigned int cl_lang_count;
 #define CL_DRIVER		(1U << 19) /* Driver option.  */
 #define CL_TARGET		(1U << 20) /* Target-specific option.  */
 #define CL_COMMON		(1U << 21) /* Language-independent.  */
+#define CL_REMARK		(1U << 22) /* Enables an (optional) remark.  */
 
 #define CL_MIN_OPTION_CLASS	CL_PARAMS
-#define CL_MAX_OPTION_CLASS	CL_COMMON
+#define CL_MAX_OPTION_CLASS	CL_REMARK
 
 /* From here on the bits describe attributes of the options.
    Before this point the bits have described the class of the option.
    This distinction is important because --help will not list options
    which only have these higher bits set.  */
 
-#define CL_JOINED		(1U << 22) /* If takes joined argument.  */
-#define CL_SEPARATE		(1U << 23) /* If takes a separate argument.  */
-#define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
-#define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
-#define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_JOINED		(1U << 23) /* If takes joined argument.  */
+#define CL_SEPARATE		(1U << 24) /* If takes a separate argument.  */
+#define CL_UNDOCUMENTED		(1U << 25) /* Do not output with --help.  */
+#define CL_NO_DWARF_RECORD	(1U << 26) /* Do not add to producer string.  */
+#define CL_PCH_IGNORE		(1U << 27) /* Do compare state for pch.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
diff --git a/gcc/passes.c b/gcc/passes.c
index 832f0b3..6cbc5f8 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h" /* for fnotice */
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
 
 using namespace gcc;
 
@@ -1734,6 +1736,16 @@ execute_function_dump (function *fn, void *data)
     }
 }
 
+/* Helper function to perform function body dump.  */
+
+static void
+execute_optinfo_function_dump (function *fn, void *data)
+{
+  opt_pass *pass = (opt_pass *)data;
+
+  optimization_records_maybe_record_function (fn, pass);
+}
+
 /* This function is called when an internal compiler error is encountered.
    Ensure that function dump is made available before compiler is aborted.  */
 
@@ -2202,6 +2214,8 @@ execute_one_ipa_transform_pass (struct cgraph_node *node,
 
   if (dump_file)
     do_per_function (execute_function_dump, pass);
+  if (optinfo_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
   pass_fini_dump_file (pass);
 
   current_pass = NULL;
@@ -2503,6 +2517,9 @@ execute_one_pass (opt_pass *pass)
   else if (dump_file)
     do_per_function (execute_function_dump, pass);
 
+  if (optimization_records_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
+
   if (!current_function_decl)
     symtab->process_new_functions ();
 
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index 8babbffd..df3ee81 100644
--- a/gcc/pretty-print.c
+++ b/gcc/pretty-print.c
@@ -2102,10 +2102,7 @@ test_pp_format ()
 {
   /* Avoid introducing locale-specific differences in the results
      by hardcoding open_quote and close_quote.  */
-  const char *old_open_quote = open_quote;
-  const char *old_close_quote = close_quote;
-  open_quote = "`";
-  close_quote = "'";
+  auto_fix_quotes fix_quotes;
 
   /* Verify that plain text is passed through unchanged.  */
   assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted");
@@ -2187,10 +2184,6 @@ test_pp_format ()
   assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7);
   assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10",
 		    "problem with %qs at line %i", "bar", 10);
-
-  /* Restore old values of open_quote and close_quote.  */
-  open_quote = old_open_quote;
-  close_quote = old_close_quote;
 }
 
 /* Run all of the selftests within this file.  */
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@ enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@ public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/profile.c b/gcc/profile.c
index 8ba6dc7..0cd0270 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -447,9 +447,14 @@ read_profile_edge_counts (gcov_type *exec_counts)
 		      {
 			static bool informed = 0;
 			if (dump_enabled_p () && !informed)
-		          dump_printf_loc (MSG_NOTE, input_location,
-                                           "corrupted profile info: edge count"
-                                           " exceeds maximal count\n");
+			  {
+			    dump_location_t loc
+			      = dump_location_t::from_location_t
+			        (input_location);
+			    dump_printf_loc (MSG_NOTE, loc,
+					     "corrupted profile info: edge count"
+					     " exceeds maximal count\n");
+			  }
 			informed = 1;
 		      }
 		    else
@@ -672,7 +677,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum)
          if (dump_enabled_p () && informed == 0)
            {
              informed = 1;
-             dump_printf_loc (MSG_NOTE, input_location,
+             dump_printf_loc (MSG_NOTE,
+			      dump_location_t::from_location_t (input_location),
                               "correcting inconsistent profile data\n");
            }
          correct_negative_edge_counts ();
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 36879cf..9d588fa 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -59,6 +59,7 @@ selftest::run_tests ()
   /* Low-level data structures.  */
   bitmap_c_tests ();
   sbitmap_c_tests ();
+  dumpfile_c_tests ();
   et_forest_c_tests ();
   hash_map_tests_c_tests ();
   hash_set_tests_c_tests ();
@@ -71,6 +72,9 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   json_cc_tests ();
+  optinfo_cc_tests ();
+  optinfo_emit_diagnostics_cc_tests ();
+  optinfo_emit_json_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.c b/gcc/selftest.c
index 74adc63..b333ca8 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "selftest.h"
+#include "intl.h"
 
 #if CHECKING_P
 
@@ -158,6 +159,25 @@ temp_source_file::temp_source_file (const location &loc,
   fclose (out);
 }
 
+/* Avoid introducing locale-specific differences in the results
+   by hardcoding open_quote and close_quote.  */
+
+auto_fix_quotes::auto_fix_quotes ()
+{
+  m_saved_open_quote = open_quote;
+  m_saved_close_quote = close_quote;
+  open_quote = "`";
+  close_quote = "'";
+}
+
+/* Restore old values of open_quote and close_quote.  */
+
+auto_fix_quotes::~auto_fix_quotes ()
+{
+  open_quote = m_saved_open_quote;
+  close_quote = m_saved_close_quote;
+}
+
 /* Read the contents of PATH into memory, returning a 0-terminated buffer
    that must be freed by the caller.
    Fail (and abort) if there are any problems, with LOC as the reported
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 173700b..1434329 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -104,6 +104,26 @@ class temp_source_file : public named_temp_file
 		    const char *content);
 };
 
+/* RAII class for avoiding introducing locale-specific differences
+   in strings containing localized quote marks, by temporarily overriding
+   the "open_quote" and "close_quote" globals to something hardcoded.
+
+   Specifically, the C locale's values are used:
+   - open_quote becomes "`"
+   - close_quote becomes "'"
+   for the lifetime of the object.  */
+
+class auto_fix_quotes
+{
+ public:
+  auto_fix_quotes ();
+  ~auto_fix_quotes ();
+
+ private:
+  const char *m_saved_open_quote;
+  const char *m_saved_close_quote;
+};
+
 /* Various selftests involving location-handling require constructing a
    line table and one or more line maps within it.
 
@@ -188,6 +208,7 @@ extern void attribute_c_tests ();
 extern void bitmap_c_tests ();
 extern void diagnostic_c_tests ();
 extern void diagnostic_show_locus_c_tests ();
+extern void dumpfile_c_tests ();
 extern void edit_context_c_tests ();
 extern void et_forest_c_tests ();
 extern void fibonacci_heap_c_tests ();
@@ -199,6 +220,9 @@ extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void json_cc_tests ();
+extern void optinfo_cc_tests ();
+extern void optinfo_emit_diagnostics_cc_tests ();
+extern void optinfo_emit_json_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5a19fc9..1f0a079 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -96,6 +96,8 @@ set plugin_test_list [list \
 	  must-tail-call-2.c } \
     { expensive_selftests_plugin.c \
 	  expensive-selftests-1.c } \
+    { remarks_plugin.c \
+	  remarks-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
new file mode 100644
index 0000000..9139b9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-fremarks" } */
+
+extern void test_string_literal (void);
+extern void test_tree (void);
+extern void test_gimple (int);
+extern void test_cgraph_node (void);
+extern void test_printf (void);
+extern void test_wide_int (void);
+extern void test_poly_int (void);
+extern void test_scopes (void);
+
+void test_remarks (void)
+{
+  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
+  test_tree (); /* { dg-remark "test of tree: '0'" } */
+  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
+  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
+  test_printf (); /* { dg-remark "test of optinfo printf: 42" } */
+  test_wide_int (); /* { dg-remark "test of wide int: 0" } */
+  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
+
+  test_scopes (); /* { dg-line test_scopes_line } */
+  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
new file mode 100644
index 0000000..332bba6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,152 @@
+/* Test of remark-emission by optinfo.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "optinfo.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cgraph.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_remarks =
+{
+  GIMPLE_PASS, /* type */
+  "test_remarks", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_remarks : public gimple_opt_pass
+{
+public:
+  pass_test_remarks(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_remarks, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_remarks
+
+unsigned int
+pass_test_remarks::execute (function *fun)
+{
+  basic_block bb;
+
+  if (!dump_enabled_p ())
+    return 0;
+  
+  FOR_ALL_BB_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+	 !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+	gimple *stmt = gsi_stmt (gsi);
+	gcall *call = dyn_cast <gcall *> (stmt);
+	if (!call)
+	  continue;
+	tree callee_decl = gimple_call_fndecl (call);
+	if (!callee_decl)
+	  continue;
+	tree callee_name = DECL_NAME (callee_decl);
+	if (!callee_name)
+	  continue;
+	const char *callee = IDENTIFIER_POINTER (callee_name);
+
+	/* Various optinfo tests, done at callsites,
+	   controlled by the callee name.  */
+	if (strcmp (callee, "test_string_literal") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of remark for ");
+	    dump_printf (MSG_NOTE, callee);
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of tree: ");
+	    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of gimple: ");
+	    dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of callgraph node: ");
+	    dump_symtab_node (MSG_NOTE, cgraph_node::get (callee_decl));
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of optinfo printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int") == 0)
+	  {
+	    HOST_WIDE_INT val = 0;
+	    dump_printf_loc (MSG_NOTE, stmt,
+			     "test of wide int: " HOST_WIDE_INT_PRINT_DEC,
+			     val);
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of poly int: ");
+	    dump_dec (MSG_NOTE, poly_int64 (42));
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    AUTO_DUMP_SCOPE ("outer scope", stmt);
+	    {
+	      dump_printf_loc (MSG_NOTE, stmt, "at outer scope");
+	      AUTO_DUMP_SCOPE ("middle scope", stmt);
+	      {
+		dump_printf_loc (MSG_NOTE, stmt, "at middle scope");
+		AUTO_DUMP_SCOPE ("innermost scope", stmt);
+		dump_printf_loc (MSG_NOTE, stmt, "at innermost scope");
+	      }
+	    }
+	  }
+      }
+
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_remarks (gcc::context *ctxt)
+{
+  return new pass_test_remarks (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  pass_info.pass = make_pass_test_remarks (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index a15c5d5..906ee3b 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1154,6 +1154,15 @@ proc dg-locus { args } {
     verbose "process-message:\n${dg-messages}" 2
 }
 
+# Handle remarks.
+
+proc dg-remark { args } {
+    # Make this variable available here and to the saved proc.
+    upvar dg-messages dg-messages
+
+    process-message saved-dg-error "remark: " "$args"
+}
+
 # Check the existence of a gdb in the path, and return true if there
 # is one.
 #
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d108096..a047390 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -487,6 +488,8 @@ compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2048,6 +2051,8 @@ do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
index c6e0a60..1206614 100644
--- a/gcc/tree-loop-distribution.c
+++ b/gcc/tree-loop-distribution.c
@@ -3117,7 +3117,7 @@ pass_loop_distribution::execute (function *fun)
 	    break;
 
 	  const char *str = loop->inner ? " nest" : "";
-	  location_t loc = find_loop_location (loop);
+	  dump_user_location_t loc = find_loop_location (loop);
 	  if (!cd)
 	    {
 	      calculate_dominance_info (CDI_DOMINATORS);
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index f1557c9..2a12eef 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -3334,7 +3334,7 @@ lower_nested_functions (tree fndecl)
 
   gimplify_all_functions (cgn);
 
-  dump_file = dump_begin (TDI_nested, &dump_flags);
+  set_dump_file (dump_begin (TDI_nested, &dump_flags));
   if (dump_file)
     fprintf (dump_file, "\n;; Function %s\n\n",
 	     lang_hooks.decl_printable_name (fndecl, 2));
@@ -3361,7 +3361,7 @@ lower_nested_functions (tree fndecl)
   if (dump_file)
     {
       dump_end (TDI_nested, dump_file);
-      dump_file = NULL;
+      set_dump_file (NULL);
     }
 }
 
diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
index aa74427..64712e4 100644
--- a/gcc/tree-parloops.c
+++ b/gcc/tree-parloops.c
@@ -3284,7 +3284,6 @@ parallelize_loops (bool oacc_kernels_p)
   struct tree_niter_desc niter_desc;
   struct obstack parloop_obstack;
   HOST_WIDE_INT estimated;
-  source_location loop_loc;
 
   /* Do not parallelize loops in the functions created by parallelization.  */
   if (!oacc_kernels_p
@@ -3409,7 +3408,7 @@ parallelize_loops (bool oacc_kernels_p)
       changed = true;
       skip_loop = loop->inner;
 
-      loop_loc = find_loop_location (loop);
+      dump_user_location_t loop_loc = find_loop_location (loop);
       if (loop->inner)
 	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loop_loc,
 			 "parallelizing outer loop %d\n", loop->num);
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 7447f7a..2623d9b 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 24bf60e..5f741c3 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -691,7 +691,7 @@ try_unroll_loop_completely (struct loop *loop,
 			    edge exit, tree niter, bool may_be_zero,
 			    enum unroll_level ul,
 			    HOST_WIDE_INT maxiter,
-			    location_t locus, bool allow_peel)
+			    dump_user_location_t locus, bool allow_peel)
 {
   unsigned HOST_WIDE_INT n_unroll = 0;
   bool n_unroll_found = false;
@@ -1162,7 +1162,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
   tree niter;
   HOST_WIDE_INT maxiter;
   bool modified = false;
-  location_t locus = UNKNOWN_LOCATION;
+  dump_user_location_t locus;
   struct tree_niter_desc niter_desc;
   bool may_be_zero = false;
 
@@ -1177,7 +1177,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	= niter_desc.may_be_zero && !integer_zerop (niter_desc.may_be_zero);
     }
   if (TREE_CODE (niter) == INTEGER_CST)
-    locus = gimple_location (last_stmt (exit->src));
+    locus = last_stmt (exit->src);
   else
     {
       /* For non-constant niter fold may_be_zero into niter again.  */
@@ -1204,7 +1204,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	niter = find_loop_niter_by_eval (loop, &exit);
 
       if (exit)
-        locus = gimple_location (last_stmt (exit->src));
+        locus = last_stmt (exit->src);
 
       if (TREE_CODE (niter) != INTEGER_CST)
 	exit = NULL;
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index b313571..d80541c 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -7525,7 +7525,7 @@ tree_ssa_iv_optimize_loop (struct ivopts_data *data, struct loop *loop)
 
   gcc_assert (!data->niters);
   data->current_loop = loop;
-  data->loop_loc = find_loop_location (loop);
+  data->loop_loc = find_loop_location (loop).get_location_t ();
   data->speed = optimize_loop_for_speed_p (loop);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 7a54c5f..3aee0f6 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -2447,7 +2447,7 @@ number_of_iterations_exit (struct loop *loop, edge exit,
     return true;
 
   if (warn)
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
+    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
 		     "missed loop optimization: niters analysis ends up "
 		     "with assumptions.\n");
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 3d025c2..e5eddf9 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5866,8 +5866,7 @@ eliminate_dom_walker::before_dom_children (basic_block b)
 		    fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
 		  if (dump_enabled_p ())
 		    {
-		      location_t loc = gimple_location (stmt);
-		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				       "converting indirect call to "
 				       "function %s\n",
 				       lang_hooks.decl_printable_name (fn, 2));
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index 3eab650..227f993 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -1301,7 +1301,7 @@ create_lcssa_for_virtual_phi (struct loop *loop)
    location is calculated.
    Return the loop location if succeed and NULL if not.  */
 
-source_location
+dump_user_location_t
 find_loop_location (struct loop *loop)
 {
   gimple *stmt = NULL;
@@ -1309,19 +1309,19 @@ find_loop_location (struct loop *loop)
   gimple_stmt_iterator si;
 
   if (!loop)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   stmt = get_loop_exit_condition (loop);
 
   if (stmt
       && LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-    return gimple_location (stmt);
+    return stmt;
 
   /* If we got here the loop is probably not "well formed",
      try to estimate the loop location */
 
   if (!loop->header)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   bb = loop->header;
 
@@ -1329,10 +1329,10 @@ find_loop_location (struct loop *loop)
     {
       stmt = gsi_stmt (si);
       if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-        return gimple_location (stmt);
+        return stmt;
     }
 
-  return UNKNOWN_LOCATION;
+  return dump_user_location_t ();
 }
 
 /* Return true if PHI defines an IV of the loop to be vectorized.  */
@@ -2494,7 +2494,7 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1,
 	}
     }
 
-  source_location loop_loc = find_loop_location (loop);
+  dump_user_location_t loop_loc = find_loop_location (loop);
   struct loop *scalar_loop = LOOP_VINFO_SCALAR_LOOP (loop_vinfo);
   if (prolog_peeling)
     {
@@ -3068,7 +3068,7 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       loop_constraint_set (loop, LOOP_C_INFINITE);
     }
 
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
       && dump_enabled_p ())
     {
       if (version_alias)
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 8e45aec..7198826 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -2723,8 +2723,8 @@ needs_fold_left_reduction_p (tree type, tree_code code,
    reduction operation CODE has a handled computation expression.  */
 
 bool
-check_reduction_path (location_t loc, loop_p loop, gphi *phi, tree loop_arg,
-		      enum tree_code code)
+check_reduction_path (dump_user_location_t loc, loop_p loop, gphi *phi,
+		      tree loop_arg, enum tree_code code)
 {
   auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
   auto_bitmap visited;
@@ -3742,8 +3742,8 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
   else
     {
       if (LOOP_VINFO_LOOP (loop_vinfo)->force_vectorize)
-	warning_at (vect_location, OPT_Wopenmp_simd, "vectorization "
-		    "did not happen for a simd loop");
+	warning_at (vect_location.get_location_t (), OPT_Wopenmp_simd,
+		    "vectorization did not happen for a simd loop");
 
       if (dump_enabled_p ())
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 1bbe468..562f835 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -1510,7 +1510,8 @@ fail:
 /* Dump a slp tree NODE using flags specified in DUMP_KIND.  */
 
 static void
-vect_print_slp_tree (dump_flags_t dump_kind, location_t loc, slp_tree node)
+vect_print_slp_tree (dump_flags_t dump_kind, dump_location_t loc,
+		     slp_tree node)
 {
   int i;
   gimple *stmt;
@@ -3001,7 +3002,7 @@ vect_slp_bb (basic_block bb)
 	  insns++;
 
 	  if (gimple_location (stmt) != UNKNOWN_LOCATION)
-	    vect_location = gimple_location (stmt);
+	    vect_location = stmt;
 
 	  if (!vect_find_stmt_data_reference (NULL, stmt, &datarefs))
 	    break;
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 8ff90b3..fa4909e 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -81,8 +81,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 
 
-/* Loop or bb location.  */
-source_location vect_location;
+/* Loop or bb location, with hotness information.  */
+dump_user_location_t vect_location;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> *stmt_vec_info_vec;
@@ -749,11 +749,12 @@ vectorize_loops (void)
 	loop_dist_alias_call = vect_loop_dist_alias_call (loop);
        vectorize_epilogue:
 	vect_location = find_loop_location (loop);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+	DUMP_VECT_SCOPE ("analyzing loop");
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
 	    && dump_enabled_p ())
 	  dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
-                       LOCATION_FILE (vect_location),
-		       LOCATION_LINE (vect_location));
+                       LOCATION_FILE (vect_location.get_location_t ()),
+		       LOCATION_LINE (vect_location.get_location_t ()));
 
 	loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo);
 	loop->aux = loop_vinfo;
@@ -827,7 +828,7 @@ vectorize_loops (void)
 
 	if (loop_vectorized_call)
 	  set_uid_loop_bbs (loop_vinfo, loop_vectorized_call);
-        if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+        if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
 	    && dump_enabled_p ())
           dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
                            "loop vectorized\n");
@@ -872,7 +873,7 @@ vectorize_loops (void)
 	  }
       }
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
   if (dump_enabled_p ()
@@ -1207,7 +1208,7 @@ increase_alignment (void)
 {
   varpool_node *vnode;
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
   type_align_map = new hash_map<tree, unsigned>;
 
   /* Increase the alignment of all global arrays for vectorization.  */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 8bb9e3e..5d8ba94 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-data-ref.h"
 #include "tree-hash-traits.h"
 #include "target.h"
+#include "optinfo.h"
 
 /* Used for naming of new temporaries.  */
 enum vect_var_kind {
@@ -1422,18 +1423,20 @@ vect_get_scalar_dr_size (struct data_reference *dr)
   return tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr))));
 }
 
-/* Source location */
-extern source_location vect_location;
+/* Source location + hotness information. */
 
-/* If dumping is enabled, emit a MSG_NOTE at vect_location about
-   entering MSG within the vectorizer.  MSG should be a string literal. */
+extern dump_user_location_t vect_location;
+
+/* A macro for calling:
+     dump_begin_scope (MSG, vect_location);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
 
 #define DUMP_VECT_SCOPE(MSG) \
-  do {						\
-    if (dump_enabled_p ())			\
-      dump_printf_loc (MSG_NOTE, vect_location, \
-		       "=== " MSG " ===\n");	\
-  } while (0)
+  AUTO_DUMP_SCOPE (MSG, vect_location)
 
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
@@ -1451,7 +1454,7 @@ extern void vect_loop_versioning (loop_vec_info, unsigned int, bool,
 extern struct loop *vect_do_peeling (loop_vec_info, tree, tree,
 				     tree *, tree *, tree *, int, bool, bool);
 extern void vect_prepare_for_masked_peels (loop_vec_info);
-extern source_location find_loop_location (struct loop *);
+extern dump_user_location_t find_loop_location (struct loop *);
 extern bool vect_can_advance_ivs_p (loop_vec_info);
 
 /* In tree-vect-stmts.c.  */
@@ -1567,7 +1570,7 @@ extern tree vect_create_addr_base_for_vector_ref (gimple *, gimple_seq *,
 extern gimple *vect_force_simple_reduction (loop_vec_info, gimple *,
 					    bool *, bool);
 /* Used in gimple-loop-interchange.c.  */
-extern bool check_reduction_path (location_t, loop_p, gphi *, tree,
+extern bool check_reduction_path (dump_user_location_t, loop_p, gphi *, tree,
 				  enum tree_code);
 /* Drive for loop analysis stage.  */
 extern loop_vec_info vect_analyze_loop (struct loop *, loop_vec_info);
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index d50a179..77d4849 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -585,10 +585,11 @@ check_counter (gimple *stmt, const char * name,
   gcov_type bb_count = bb_count_d.ipa ().to_gcov_type ();
   if (*all != bb_count || *count > *all)
     {
-      location_t locus;
-      locus = (stmt != NULL)
-              ? gimple_location (stmt)
-              : DECL_SOURCE_LOCATION (current_function_decl);
+      dump_user_location_t locus;
+      locus = ((stmt != NULL)
+	       ? dump_user_location_t (stmt)
+	       : dump_user_location_t::from_function_decl
+		   (current_function_decl));
       if (flag_profile_correction)
         {
           if (dump_enabled_p ())
@@ -603,7 +604,7 @@ check_counter (gimple *stmt, const char * name,
 	}
       else
 	{
-	  error_at (locus, "corrupted value profile: %s "
+	  error_at (locus.get_location_t (), "corrupted value profile: %s "
 		    "profile counter (%d out of %d) inconsistent with "
 		    "basic-block count (%d)",
 		    name,
@@ -1271,13 +1272,11 @@ find_func_by_profile_id (int profile_id)
 bool
 check_ic_target (gcall *call_stmt, struct cgraph_node *target)
 {
-   location_t locus;
    if (gimple_check_call_matching_types (call_stmt, target->decl, true))
      return true;
 
-   locus =  gimple_location (call_stmt);
    if (dump_enabled_p ())
-     dump_printf_loc (MSG_MISSED_OPTIMIZATION, locus,
+     dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                       "Skipping target %s with mismatching types for icall\n",
                       target->name ());
    return false;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] v3 of optinfo, remarks and optimization records
  2018-06-20 16:35             ` [PATCH] v3 " David Malcolm
@ 2018-06-25 13:35               ` Richard Biener
  2018-06-26 13:54                 ` [committed] Introduce dump_location_t David Malcolm
                                   ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: Richard Biener @ 2018-06-25 13:35 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> Here's v3 of the patch (one big patch this time, rather than a kit).
>
> Like the v2 patch kit, this patch reuses the existing dump API,
> rather than inventing its own.
>
> Specifically, it uses the dump_* functions in dumpfile.h that don't
> take a FILE *, the ones that implicitly write to dump_file and/or
> alt_dump_file.  I needed a name for them, so I've taken to calling
> them the "structured dump API" (better name ideas welcome).
>
> v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
> calls as delimiters when consolidating "dump_*" calls.  There's a
> new dump_context class which has responsibility for consolidating
> them into optimization records.
>
> The dump_*_loc calls now capture more than just a location_t: they
> capture the profile_count and the location in GCC's own sources where
> the dump is being emitted from.
>
> This works by introducing a new "dump_location_t" class as the
> argument of those dump_*_loc calls.  The dump_location_t can
> be constructed from a gimple * or from an rtx_insn *, so that
> rather than writing:
>
>   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
>                    "some message: %i", 42);
>
> you can write:
>
>   dump_printf_loc (MSG_NOTE, stmt,
>                    "some message: %i", 42);
>
> and the dump_location_t constructor will grab the location_t and
> profile_count of stmt, and the location of the "dump_printf_loc"
> callsite (and gracefully handle "stmt" being NULL).
>
> Earlier versions of the patch captured the location of the
> dump_*_loc call via preprocessor hacks, or didn't work properly;
> this version of the patch works more cleanly: internally,
> dump_location_t is split into two new classes:
>   * dump_user_location_t: the location_t and profile_count within
>     the *user's code*, and
>   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
>     the *implementation* code (i.e. GCC or a plugin), captured
>     "automagically" via default params
>
> These classes are sometimes used elsewhere in the code.  For
> example, "vect_location" becomes a dump_user_location_t
> (location_t and profile_count), so that in e.g:
>
>   vect_location = find_loop_location (loop);
>
> it's capturing the location_t and profile_count, and then when
> it's used here:
>
>   dump_printf_loc (MSG_NOTE, vect_location, "foo");
>
> the dump_location_t is constructed from the vect_location
> plus the dump_impl_location_t at that callsite.
>
> In contrast, loop-unroll.c's report_unroll's "locus" param
> becomes a dump_location_t: we're interested in where it was
> called from, not in the locations of the various dump_*_loc calls
> within it.
>
> Previous versions of the patch captured a gimple *, and needed
> GTY markers; in this patch, the dump_user_location_t is now just a
> location_t and a profile_count.
>
> The v2 patch added an overload for dump_printf_loc so that you
> could pass in either a location_t, or the new type; this version
> of the patch eliminates that: they all now take dump_location_t.
>
> Doing so required adding support for rtx_insn *, so that one can
> write this kind of thing in RTL passes:
>
>   dump_printf_loc (MSG_NOTE, insn, "foo");
>
> One knock-on effect is that get_loop_location now returns a
> dump_user_location_t rather than a location_t, so that it has
> hotness information.
>
> Richi: would you like me to split out this location-handling
> code into a separate patch?  (It's kind of redundant without
> adding the remarks and optimization records work, but if that's
> easier I can do it)

I think that would be easier because it doesn't require the JSON
stuff and so I'll happily approve it.

Thus - trying to review that bits (and sorry for the delay).

+  location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
     }

   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }

seeing this in multiple places.  I seem to remember that
dump_file / alt_dump_file was suposed to handle dumping
into two locations - a dump file and optinfo (or stdout).  This looks
like the optinfo "stream" is even more separate.  Could that
obsolete the alt_dump_file stream?  I'd need to review existing stuff
in more detail to answer but maybe you already know from recently
digging into this.

Oh, and all the if (optinfo_enable_p ()) stuff is for the followup then, right?

I like the boiler-plate changes to dump_* using stuff a lot, so the
infrastructure to do that (the location wrapping) and these boiler-plate
changes are pre-approved if split out.

I think the *_REMARK stuff should get attention of the respective
maintainers - not sure what the difference between NOTE and REMARK
is ;)

Thanks and sorry again for the repeated delays...
Richard.

>
> The v3 patch adds more detail to remarks: they now show the gcc
> source location that emitted them e.g.:
>
> test.c:8:3: remark:   Symbolic number of iterations is '(unsigned int)
> n_9(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
>
> and I added new command-line options for controlling the above:
> * -fno-diagnostics-show-remark-hotness
> * -fno-diagnostics-show-remark-origin
> * -fno-diagnostics-show-remark-pass
>
> An example of remark output (showing colors) can be seen here:
>
>   https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html
>
> I haven't yet implemented options for filtering remarks (I'm thinking
> of using the optgroups from -fopt-info, a hotness threshold, and pragmas
> for narrowing them to a specific region of the user's code); though
> the HTML visualization from the JSON output is likely to provide more
> flexibility.
>
> I got rid of GCC_UNLIKELY in favor of a __builtin_expect in
> dump_enabled_p, but I haven't measured the impact yet.
>
> Other changes relative to v2:
> * added class dump_context; eliminate class pending_optinfo; moved optinfo
>   emission from optinfo dtor and into dump_context
> * added selftests for dump_* consolidation, for JSON emission, and for
>   remarks
> * minimized global state in JSON emission
> * renamed OPTINFO_SCOPE to AUTO_DUMP_SCOPE and moved from optinfo.h to
>   dumpfile.h (and similar changes to the support class)
> * added "m_pass" field to optinfo, reading current_pass at time of
>   creation, rather than as needed later on
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
> (relative to r261555).
>
> How is this looking?
>
> As before, this patch requires the JSON support patch from v1 of the kit:
>   "[PATCH 02/10] Add JSON implementation"
>     https://gcc.gnu.org/ml/gcc-patches/2018-05/msg01676.html
>
> This approach means we can use all of the existing dump_* calls without
> having to rewrite anything, gaining the ability to prioritize dump messages
> by code hotness and to track where in GCC any dump message came from
> without needing to resort to "grep".
>
> But in terms of actually helping users figure out "how do I get gcc to
> vectorize this loop?", it's only a start.  Maybe followup activity would be
> to go through the existing dump messages and to make them more user-friendly.
> Or to add new dump API entrypoints?
> I find the MSG_* a bit verbose, so maybe instead of e.g.:
>
>   dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
>                    "can't inline function call: ");
>   dump_symtab_node (callee);
>   dump_printf (" into ");
>   dump_symtab_node (caller);
>   dump_printf (" as body is not available\n");
>
> there could be, say:
>
>  missed_opt_at (call_stmt,
>                 ("can't inline function call: %N into %N"
>                  " as body is not available\n"),
>                 callee, caller);
>
> (plus, say "optimized_at", "opt_note_at"), introducing a new family
> of format codes (so that the optimization records "know" how to
> embed them).
>
> This would also allow for i18n; such API calls would be more explicitly
> aimed at tech-savvy end-users.
>
> Anyway, that last bit is more me just thinking aloud about possible future
> work here, so here's the v3 patch as it is:
>
> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add optinfo.o, optinfo-emit-diagnostics.o,
>         optinfo-emit-json.o.
>         (CFLAGS-optinfo-emit-json.o): Add -DTARGET_NAME as per toplev.o.
>         * cfgloop.c (get_loop_location): Convert return type from
>         location_t to dump_user_location_t, replacing INSN_LOCATION lookups
>         by implicit construction from rtx_insn *, and using
>         dump_user_location_t::from_function_decl for the fallback case.
>         * cfgloop.h (get_loop_location): Convert return type from
>         location_t to dump_user_location_t.
>         * cgraph.c (cgraph_node::get_body): Replace assignment to
>         "dump_file" with call to set_dump_file.
>         * cgraphunit.c (walk_polymorphic_call_targets): Update call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         * common.opt (fremarks): New option.
>         (fdiagnostics-show-remark-hotness): New option.
>         (fdiagnostics-show-remark-origin): New option.
>         (fdiagnostics-show-remark-pass): New option.
>         (fsave-optimization-record): New option.
>         * coretypes.h  (class symtab_node): New forward declaration.
>         (struct cgraph_node): Likewise.
>         (class varpool_node): Likewise.
>         (struct kv_pair): Move here from dumpfile.c.
>         * coverage.c (get_coverage_counts): Update calls to
>         dump_printf_loc to pass in dump_location_t rather than a
>         location_t.
>         * diagnostic-color.c (color_dict): Add "remark", as bold green.
>         * diagnostic-core.h (remark): New decl.
>         * diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
>         (remark): New function.
>         * diagnostic.def (DK_REMARK): New diagnostic kind.
>         * doc/invoke.texi (Remarks): New section.
>         (-fsave-optimization-record): New option.
>         (-fremarks): New option.
>         (-fno-diagnostics-show-remark-hotness): New option.
>         (-fno-diagnostics-show-remark-origin): New option.
>         (-fno-diagnostics-show-remark-pass): New option.
>         * dump-context.h: New file.
>         * dumpfile.c: Include "optinfo.h", "cgraph.h",
>         "optinfo-emit-json.h", "optinfo-internal.h", "backend.h",
>         "gimple.h", "rtl.h", "dump-context.h", "tree-pass.h", "selftest.h"
>         (alt_dump_file): Make static, and group with...
>         (alt_flags): ...this definition.
>         (dumps_are_enabled): New variable.
>         (refresh_dumps_are_enabled): New function.
>         (set_dump_file): New function.
>         (set_alt_dump_file): New function.
>         (struct kv_pair): Move from here to coretypes.h.
>         (optgroup_options): Make non-static.
>         (dump_user_location_t::dump_user_location_t): New ctors
>         (dump_user_location_t::from_function_decl): New function.
>         (dump_loc): Make static.  Add indentation based on scope depth.
>         (dump_context::~dump_context): New dtor.
>         (dump_gimple_stmt): Move implementation to...
>         (dump_context::dump_gimple_stmt): ...this new method.  Add the stmt
>         to any pending optinfo, creating one if need be.
>         (dump_gimple_stmt_loc): Move implementation to...
>         (dump_context::dump_gimple_stmt_loc): ...this new method.  Convert
>         param "loc" from location_t to const dump_location_t &.  Start a
>         new optinfo and add the stmt to it.
>         (dump_generic_expr): Move implementation to...
>         (dump_context::dump_generic_expr): ...this new method.  Add the
>         tree to any pending optinfo, creating one if need be.
>         (dump_generic_expr_loc): Delete.
>         (dump_printf): Move implementation to...
>         (dump_context::dump_printf_va): ...this new method.  Add the
>         text to any pending optinfo, creating one if need be.
>         (dump_printf_loc): Move implementation to...
>         (dump_context::dump_printf_loc_va): ...this new method.  Convert
>         param "loc" from location_t to const dump_location_t &.  Start a
>         new optinfo and add the stmt to it.
>         (dump_dec): Move implementation to...
>         (dump_context::dump_dec): ...this new method.  Add the value to
>         any pending optinfo, creating one if need be.
>         (dump_context::dump_symtab_node): New method.
>         (dump_context::get_scope_depth): New method.
>         (dump_context::begin_scope): New method.
>         (dump_context::end_scope): New method.
>         (dump_context::ensure_pending_optinfo): New method.
>         (dump_context::begin_next_optinfo): New method.
>         (dump_context::end_any_optinfo): New method.
>         (dump_context::s_current): New global.
>         (dump_context::s_default): New global.
>         (dump_symtab_node): New function.
>         (get_dump_scope_depth): New function.
>         (dump_begin_scope): New function.
>         (dump_end_scope): New function.
>         (gcc::dump_manager::dump_start): Replace assignments to
>         "dump_file" and "alt_dump_file" with call to set_dump_file and
>         set_alt_dump_file.
>         (gcc::dump_manager::dump_finish): Likewise.
>         (selftest::temp_dump_context::temp_dump_context): New ctor.
>         (selftest::temp_dump_context::~temp_dump_context): New dtor.
>         (selftest::test_impl_location): New test.
>         (selftest::assert_is_text): New support function.
>         (selftest::assert_is_tree): New support function.
>         (selftest::assert_is_gimple): New support function.
>         (selftest::test_capture_of_dump_calls): New test.
>         (selftest::dumpfile_c_tests): New function.
>         * dumpfile.h: Include "profile-count.h".
>         (dump_file, dump_flags, dump_file_name): Move decls to top of
>         file, to split them out from the "Structured dumping" API.
>         (set_dump_file): New function decl.
>         (dumps_are_enabled): New variable decl.
>         (dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
>         global.
>         (class dump_user_location_t): New class.
>         (struct dump_impl_location_t): New struct.
>         (class dump_location_t): New class.
>         (dump_printf_loc): Convert 2nd param from source_location to
>         const dump_location_t &.
>         (dump_generic_expr_loc): Delete.
>         (dump_gimple_stmt_loc): Convert 2nd param from source_location to
>         const dump_location_t &.
>         (dump_symtab_node): New decl.
>         (get_dump_scope_depth): New decl.
>         (dump_begin_scope): New decl.
>         (dump_end_scope): New decl.
>         (class auto_dump_scope): New class.
>         (AUTO_DUMP_SCOPE): New macro.
>         (dump_function, print_combine_total_stats, enable_rtl_dump_file):
>         Move these decls, to split them out from the "Structured dumping"
>         API.
>         (alt_dump_file): Delete decl.
>         (optgroup_options): New decl.
>         (class dump_manager): Add leading comment.
>         * gimple-fold.c (fold_gimple_assign): Update call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         (gimple_fold_call): Likewise.
>         * gimple-loop-interchange.cc
>         (loop_cand::analyze_iloop_reduction_var): Update for change to
>         check_reduction_path.
>         (tree_loop_interchange::interchange): Update for change to
>         find_loop_location.  Add a usage of AUTO_DUMP_SCOPE.
>         * gimple-pretty-print.c (gimple_dump_bb_buff): Make non-static.
>         * gimple-pretty-print.h (gimple_dump_bb_buff): New decl.
>         * graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
>         change in return-type of find_loop_location.
>         (graphite_regenerate_ast_isl): Likewise.
>         * graphite-optimize-isl.c (optimize_isl): Likewise.
>         * graphite.c (graphite_transform_loops): Update for change in
>         return-type of find_loop_location.
>         * ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
>         pass in a dump_location_t rather than a location_t, via the
>         gimple stmt.
>         * ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
>         (ipa_make_edge_direct_to_target): Likewise.
>         * ipa.c (walk_polymorphic_call_targets): Likewise.
>         * loop-unroll.c (report_unroll): Convert "locus" param from
>         location_t to dump_location_t.
>         (decide_unrolling): Update for change to get_loop_location's
>         return type.
>         * omp-grid.c (struct grid_prop): Convert field "target_loc" from
>         location_t to dump_user_location_t.
>         (grid_find_single_omp_among_assignments_1): Updates calls to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         (grid_parallel_clauses_gridifiable): Convert "tloc" from
>         location_t to dump_location_t.  Updates calls to dump_printf_loc
>         to pass in a dump_location_t rather than a location_t, via the
>         gimple stmt.
>         (grid_inner_loop_gridifiable_p): Likewise.
>         (grid_dist_follows_simple_pattern): Likewise.
>         (grid_gfor_follows_tiling_pattern): Likewise.
>         (grid_target_follows_gridifiable_pattern): Likewise.
>         (grid_attempt_target_gridification): Convert initialization
>         of local "grid" from memset to zero-initialization; FIXME: does
>         this require C++11?  Update call to dump_printf_loc to pass in a
>         optinfo_location rather than a location_t, via the gimple stmt.
>         * opt-functions.awk (function): Handle "Remark" by adding
>         CL_REMARK.
>         * optinfo-emit-diagnostics.cc: New file.
>         * optinfo-emit-diagnostics.h: New file.
>         * optinfo-emit-json.cc: New file.
>         * optinfo-emit-json.h: New file.
>         * optinfo-internal.h: New file.
>         * optinfo.cc: New file.
>         * optinfo.h: New file.
>         * opts.c (print_specific_help): Handle CL_REMARK.
>         (common_handle_option): Likewise.
>         * opts.h (CL_REMARK): New macro.
>         (CL_MAX_OPTION_CLASS): Update for CL_REMARK.
>         (CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD)
>         (CL_PCH_IGNORE): Likewise.
>         * passes.c: Include "optinfo.h" and "optinfo-emit-json.h".
>         (execute_optinfo_function_dump): New function.
>         (execute_one_ipa_transform_pass): Add per-function call to
>         execute_optinfo_function_dump.
>         (execute_one_pass): Likewise.
>         * pretty-print.c (test_pp_format): Move save and restore of quotes
>         to class auto_fix_quotes, and add an instance.
>         * profile-count.c (profile_quality_as_string): New function.
>         * profile-count.h (profile_quality_as_string): New decl.
>         (profile_count::quality): New accessor.
>         * profile.c (read_profile_edge_counts): Updates call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t
>         (compute_branch_probabilities): Likewise.
>         * selftest-run-tests.c (selftest::run_tests): Call
>         dumpfile_c_tests, optinfo_cc_tests,
>         optinfo_emit_diagnostics_cc_tests, and optinfo_emit_json_cc_tests.
>         * selftest.c: Include "intl.h".
>         (selftest::auto_fix_quotes::auto_fix_quotes): New ctor.
>         (selftest::auto_fix_quotes::~auto_fix_quotes): New dtor.
>         * selftest.h (selftest::auto_fix_quotes): New class.
>         (dumpfile_c_tests): New decl.
>         (optinfo_cc_tests): New decl.
>         (optinfo_emit_diagnostics_cc_tests): New decl.
>         (optinfo_emit_json_cc_tests): New decl.
>         * toplev.c: Include "optinfo-emit-json.h".
>         (compile_file): Call optimization_records_start and
>         optimization_records_finish.
>         * tree-loop-distribution.c (pass_loop_distribution::execute):
>         Update for change in return type of find_loop_location.
>         * tree-nested.c (lower_nested_functions): Replace assignments to
>         "dump_file" with calls to set_dump_file.
>         * tree-parloops.c (parallelize_loops): Update for change in return
>         type of find_loop_location.
>         * tree-ssa-live.c: Include "optinfo.h".
>         (remove_unused_scope_block_p): Retain inlining information if
>         optinfo_wants_inlining_info_p returns true.
>         * tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
>         "locus" from location_t to dump_user_location_t.
>         (canonicalize_loop_induction_variables): Likewise.
>         * tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
>         for change in return type of find_loop_location.
>         * tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
>         to dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the stmt.
>         * tree-vect-loop-manip.c (find_loop_location): Convert return
>         type from source_location to dump_user_location_t.
>         (vect_do_peeling): Update for above change.
>         (vect_loop_versioning): Update for change in type of
>         vect_location.
>         * tree-vect-loop.c (check_reduction_path): Convert "loc" param
>         from location_t to dump_user_location_t.
>         (vect_estimate_min_profitable_iters): Update for change in type
>         of vect_location.
>         * tree-vect-slp.c (vect_print_slp_tree): Convert param "loc" from
>         location_t to dump_location_t.
>         (vect_slp_bb): Update for change in type of vect_location.
>         * tree-vectorizer.c (vect_location): Convert from source_location
>         to dump_user_location_t.
>         (vectorize_loops): Update for change in vect_location's type.  Add
>         top-level DUMP_VECT_SCOPE.
>         (increase_alignment): Update for change in vect_location's type.
>         * tree-vectorizer.h: Include "optinfo.h".
>         (vect_location): Convert from source_location to
>         dump_user_location_t.
>         (DUMP_VECT_SCOPE): Convert to usage of AUTO_DUMP_SCOPE.
>         (find_loop_location): Convert return type from source_location to
>         dump_user_location_t.
>         (check_reduction_path): Convert 1st param from location_t to
>         dump_user_location_t.
>         * value-prof.c (check_counter): Update call to dump_printf_loc to
>         pass in a dump_user_location_t rather than a location_t; update
>         call to error_at for change in type of "locus".
>         (check_ic_target): Update call to dump_printf_loc to
>         pass in a dump_user_location_t rather than a location_t, via the
>         call_stmt.
>
> gcc/fortran/ChangeLog:
>         * gfc-diagnostic.def (DK_REMARK): New diagnostic kind.
>
> gcc/testsuite/ChangeLog:
>         * gcc.dg/plugin/plugin.exp (plugin_test_list): Add
>         remarks_plugin.c.
>         * gcc.dg/plugin/remarks-1.c: New test.
>         * gcc.dg/plugin/remarks_plugin.c: New test plugin.
>         * lib/gcc-dg.exp (dg-remark): New function.
> ---
>  gcc/Makefile.in                              |   4 +
>  gcc/cfgloop.c                                |  12 +-
>  gcc/cfgloop.h                                |   2 +-
>  gcc/cgraph.c                                 |   4 +-
>  gcc/cgraphunit.c                             |   3 +-
>  gcc/common.opt                               |  21 +
>  gcc/coretypes.h                              |  15 +
>  gcc/coverage.c                               |  22 +-
>  gcc/diagnostic-color.c                       |   2 +
>  gcc/diagnostic-core.h                        |   2 +
>  gcc/diagnostic.c                             |  17 +
>  gcc/diagnostic.def                           |   1 +
>  gcc/doc/invoke.texi                          |  83 ++-
>  gcc/dump-context.h                           | 112 ++++
>  gcc/dumpfile.c                               | 648 +++++++++++++++++++---
>  gcc/dumpfile.h                               | 258 ++++++++-
>  gcc/fortran/gfc-diagnostic.def               |   1 +
>  gcc/gimple-fold.c                            |   6 +-
>  gcc/gimple-loop-interchange.cc               |   7 +-
>  gcc/gimple-pretty-print.c                    |   2 +-
>  gcc/gimple-pretty-print.h                    |   2 +
>  gcc/graphite-isl-ast-to-gimple.c             |   4 +-
>  gcc/graphite-optimize-isl.c                  |   4 +-
>  gcc/graphite.c                               |   2 +-
>  gcc/ipa-devirt.c                             |   3 +-
>  gcc/ipa-prop.c                               |  10 +-
>  gcc/ipa.c                                    |   9 +-
>  gcc/loop-unroll.c                            |   4 +-
>  gcc/omp-grid.c                               |  47 +-
>  gcc/opt-functions.awk                        |   1 +
>  gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++
>  gcc/optinfo-emit-diagnostics.h               |  26 +
>  gcc/optinfo-emit-json.cc                     | 773 +++++++++++++++++++++++++++
>  gcc/optinfo-emit-json.h                      |  39 ++
>  gcc/optinfo-internal.h                       | 145 +++++
>  gcc/optinfo.cc                               | 254 +++++++++
>  gcc/optinfo.h                                | 147 +++++
>  gcc/opts.c                                   |   4 +
>  gcc/opts.h                                   |  13 +-
>  gcc/passes.c                                 |  17 +
>  gcc/pretty-print.c                           |   9 +-
>  gcc/profile-count.c                          |  28 +
>  gcc/profile-count.h                          |   5 +
>  gcc/profile.c                                |  14 +-
>  gcc/selftest-run-tests.c                     |   4 +
>  gcc/selftest.c                               |  20 +
>  gcc/selftest.h                               |  24 +
>  gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
>  gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 ++
>  gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 ++++++
>  gcc/testsuite/lib/gcc-dg.exp                 |   9 +
>  gcc/toplev.c                                 |   5 +
>  gcc/tree-loop-distribution.c                 |   2 +-
>  gcc/tree-nested.c                            |   4 +-
>  gcc/tree-parloops.c                          |   3 +-
>  gcc/tree-ssa-live.c                          |   4 +-
>  gcc/tree-ssa-loop-ivcanon.c                  |   8 +-
>  gcc/tree-ssa-loop-ivopts.c                   |   2 +-
>  gcc/tree-ssa-loop-niter.c                    |   2 +-
>  gcc/tree-ssa-sccvn.c                         |   3 +-
>  gcc/tree-vect-loop-manip.c                   |  16 +-
>  gcc/tree-vect-loop.c                         |   8 +-
>  gcc/tree-vect-slp.c                          |   5 +-
>  gcc/tree-vectorizer.c                        |  17 +-
>  gcc/tree-vectorizer.h                        |  25 +-
>  gcc/value-prof.c                             |  15 +-
>  66 files changed, 3229 insertions(+), 230 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo-emit-diagnostics.cc
>  create mode 100644 gcc/optinfo-emit-diagnostics.h
>  create mode 100644 gcc/optinfo-emit-json.cc
>  create mode 100644 gcc/optinfo-emit-json.h
>  create mode 100644 gcc/optinfo-internal.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
>  create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 9b85787..23060a3 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1421,6 +1421,9 @@ OBJS = \
>         omp-grid.o \
>         omp-low.o \
>         omp-simd-clone.o \
> +       optinfo.o \
> +       optinfo-emit-diagnostics.o \
> +       optinfo-emit-json.o \
>         optabs.o \
>         optabs-libfuncs.o \
>         optabs-query.o \
> @@ -2248,6 +2251,7 @@ s-bversion: BASE-VER
>         $(STAMP) s-bversion
>
>  CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
> +CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
>
>  pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
>                     $(srcdir)/gen-pass-instances.awk
> diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c
> index 8af793c..e27cd39 100644
> --- a/gcc/cfgloop.c
> +++ b/gcc/cfgloop.c
> @@ -1800,7 +1800,7 @@ loop_exits_from_bb_p (struct loop *loop, basic_block bb)
>
>  /* Return location corresponding to the loop control condition if possible.  */
>
> -location_t
> +dump_user_location_t
>  get_loop_location (struct loop *loop)
>  {
>    rtx_insn *insn = NULL;
> @@ -1819,7 +1819,7 @@ get_loop_location (struct loop *loop)
>        FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
>          {
>            if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -            return INSN_LOCATION (insn);
> +            return insn;
>          }
>      }
>    /* If loop has a single exit, then the loop control branch
> @@ -1829,24 +1829,24 @@ get_loop_location (struct loop *loop)
>        FOR_BB_INSNS_REVERSE (exit->src, insn)
>          {
>            if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -            return INSN_LOCATION (insn);
> +            return insn;
>          }
>      }
>    /* Next check the latch, to see if it is non-empty.  */
>    FOR_BB_INSNS_REVERSE (loop->latch, insn)
>      {
>        if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -        return INSN_LOCATION (insn);
> +        return insn;
>      }
>    /* Finally, if none of the above identifies the loop control branch,
>       return the first location in the loop header.  */
>    FOR_BB_INSNS (loop->header, insn)
>      {
>        if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -        return INSN_LOCATION (insn);
> +        return insn;
>      }
>    /* If all else fails, simply return the current function location.  */
> -  return DECL_SOURCE_LOCATION (current_function_decl);
> +  return dump_user_location_t::from_function_decl (current_function_decl);
>  }
>
>  /* Records that every statement in LOOP is executed I_BOUND times.
> diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
> index af9bfab..80a31c4 100644
> --- a/gcc/cfgloop.h
> +++ b/gcc/cfgloop.h
> @@ -357,7 +357,7 @@ extern bool loop_exit_edge_p (const struct loop *, const_edge);
>  extern bool loop_exits_to_bb_p (struct loop *, basic_block);
>  extern bool loop_exits_from_bb_p (struct loop *, basic_block);
>  extern void mark_loop_exit_edges (void);
> -extern location_t get_loop_location (struct loop *loop);
> +extern dump_user_location_t get_loop_location (struct loop *loop);
>
>  /* Loops & cfg manipulation.  */
>  extern basic_block *get_loop_body (const struct loop *);
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 3899467..d19f1aa 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
>        const char *saved_dump_file_name = dump_file_name;
>        dump_flags_t saved_dump_flags = dump_flags;
>        dump_file_name = NULL;
> -      dump_file = NULL;
> +      set_dump_file (NULL);
>
>        push_cfun (DECL_STRUCT_FUNCTION (decl));
>        execute_all_ipa_transforms ();
> @@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
>        updated = true;
>
>        current_pass = saved_current_pass;
> -      dump_file = saved_dump_file;
> +      set_dump_file (saved_dump_file);
>        dump_file_name = saved_dump_file_name;
>        dump_flags = saved_dump_flags;
>      }
> diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
> index 04b6919..7cfb8a0 100644
> --- a/gcc/cgraphunit.c
> +++ b/gcc/cgraphunit.c
> @@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
>             }
>            if (dump_enabled_p ())
>              {
> -             location_t locus = gimple_location_safe (edge->call_stmt);
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
>                                "devirtualizing call in %s to %s\n",
>                                edge->caller->name (), target->name ());
>             }
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 4aebcaf..141f5dd 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -506,6 +506,11 @@ Driver Negative(Qn)
>  R
>  Driver Joined Separate
>
> +fremarks
> +Common Remark Var(flag_remarks)
> +Emit diagnostic remarks about optimizations
> +FIXME: should this be -fdiagnostics-foo or somesuch?
> +
>  S
>  Driver
>
> @@ -1273,6 +1278,18 @@ fdiagnostics-show-option
>  Common Var(flag_diagnostics_show_option) Init(1)
>  Amend appropriate diagnostic messages with the command line option that controls them.
>
> +fdiagnostics-show-remark-hotness
> +Common Var(flag_diagnostics_show_remark_hotness) Init(1)
> +When emitting optimization remarks, show the execution count of the code being optimized.
> +
> +fdiagnostics-show-remark-origin
> +Common Var(flag_diagnostics_show_remark_origin) Init(1)
> +When emitting optimization remarks, show which line of GCC code produced the remark.
> +
> +fdiagnostics-show-remark-pass
> +Common Var(flag_diagnostics_show_remark_pass) Init(1)
> +When emitting optimization remarks, show which optimization pass produced the remark.
> +
>  fdisable-
>  Common Joined RejectNegative Var(common_deferred_options) Defer
>  -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 disables an optimization pass.
> @@ -1942,6 +1959,10 @@ fopt-info-
>  Common Joined RejectNegative Var(common_deferred_options) Defer
>  -fopt-info[-<type>=filename]   Dump compiler optimization details.
>
> +fsave-optimization-record
> +Common Report Var(flag_save_optimization_record) Optimization
> +Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
> +
>  foptimize-register-move
>  Common Ignore
>  Does nothing. Preserved for backward compatibility.
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..33f3d21 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>
> +/* Subclasses of symtab_node_def, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> @@ -325,6 +332,14 @@ namespace gcc {
>
>  typedef std::pair <tree, tree> tree_pair;
>
> +/* Define a name->value mapping.  */
> +template <typename ValueType>
> +struct kv_pair
> +{
> +  const char *const name;      /* the name of the value */
> +  const ValueType value;       /* the value of the name */
> +};
> +
>  #else
>
>  struct _dont_use_rtx_here_;
> diff --git a/gcc/coverage.c b/gcc/coverage.c
> index 84fff13..350cc45 100644
> --- a/gcc/coverage.c
> +++ b/gcc/coverage.c
> @@ -342,12 +342,16 @@ get_coverage_counts (unsigned counter, unsigned expected,
>        static int warned = 0;
>
>        if (!warned++ && dump_enabled_p ())
> -       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> -                         (flag_guess_branch_prob
> -                          ? "file %s not found, execution counts estimated\n"
> -                          : "file %s not found, execution counts assumed to "
> -                            "be zero\n"),
> -                         da_file_name);
> +       {
> +         dump_user_location_t loc
> +           = dump_user_location_t::from_location_t (input_location);
> +         dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                          (flag_guess_branch_prob
> +                           ? "file %s not found, execution counts estimated\n"
> +                           : "file %s not found, execution counts assumed to "
> +                           "be zero\n"),
> +                          da_file_name);
> +       }
>        return NULL;
>      }
>    if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
> @@ -378,7 +382,9 @@ get_coverage_counts (unsigned counter, unsigned expected,
>                     "its profile data (counter %qs)", id, ctr_names[counter]);
>        if (warning_printed && dump_enabled_p ())
>         {
> -          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> +         dump_user_location_t loc
> +           = dump_user_location_t::from_location_t (input_location);
> +          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                             "use -Wno-error=coverage-mismatch to tolerate "
>                             "the mismatch but performance may drop if the "
>                             "function is hot\n");
> @@ -386,7 +392,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
>           if (!seen_error ()
>               && !warned++)
>             {
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                                 "coverage mismatch ignored\n");
>               dump_printf (MSG_OPTIMIZED_LOCATIONS,
>                             flag_guess_branch_prob
> diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
> index 3ee21bc..bcc3767 100644
> --- a/gcc/diagnostic-color.c
> +++ b/gcc/diagnostic-color.c
> @@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
>    { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
>    { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
>                7, false },
> +  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
> +              6, false },
>    { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
>    { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
>    { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
> diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
> index aa5807e..63166b8 100644
> --- a/gcc/diagnostic-core.h
> +++ b/gcc/diagnostic-core.h
> @@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
>      ATTRIBUTE_GCC_DIAG(3,4);
>  extern bool warning_at (rich_location *, int, const char *, ...)
>      ATTRIBUTE_GCC_DIAG(3,4);
> +extern bool remark (location_t, int, const char *, ...)
> +    ATTRIBUTE_GCC_DIAG(3,4);
>  extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
>  extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
>                      const char *, ...)
> diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
> index e22c17b..97ed88b 100644
> --- a/gcc/diagnostic.c
> +++ b/gcc/diagnostic.c
> @@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
>      {
>      case DK_DEBUG:
>      case DK_NOTE:
> +    case DK_REMARK:
>      case DK_ANACHRONISM:
>      case DK_WARNING:
>        break;
> @@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
>    return ret;
>  }
>
> +/* Emit an optimization remark at LOCATION.  This is used by the optinfo
> +   framework.
> +   Return true if the remark was printed, false if it was inhibited.  */
> +// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
> +
> +bool
> +remark (location_t location, int opt, const char *gmsgid, ...)
> +{
> +  va_list ap;
> +  va_start (ap, gmsgid);
> +  rich_location richloc (line_table, location);
> +  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
> +  va_end (ap);
> +  return ret;
> +}
> +
>  /* A "pedantic" warning at LOCATION: issues a warning unless
>     -pedantic-errors was given on the command line, in which case it
>     issues an error.  Use this for diagnostics required by the relevant
> diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
> index ce3dc56..b58095d 100644
> --- a/gcc/diagnostic.def
> +++ b/gcc/diagnostic.def
> @@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
>  DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
> +DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
>  DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
>  /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
>  prefix does not matter.  */
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index b06ea6e..7454ae5 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
>  * Debugging Options::   Producing debuggable code.
>  * Optimize Options::    How much optimization?
>  * Instrumentation Options:: Enabling profiling and extra run-time error checking.
> +* Remarks::             Details on how your code is being optimized.
>  * Preprocessor Options:: Controlling header files and macro definitions.
>                           Also, getting dependency information for Make.
>  * Assembler Options::   Passing options to the assembler.
> @@ -416,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
>  -freorder-blocks-algorithm=@var{algorithm} @gol
>  -freorder-blocks-and-partition  -freorder-functions @gol
>  -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
> --frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
> +-frounding-math  -fsave-optimization-record @gol
> +-fsched2-use-superblocks  -fsched-pressure @gol
>  -fsched-spec-load  -fsched-spec-load-dangerous @gol
>  -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
>  -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
> @@ -470,6 +472,11 @@ Objective-C and Objective-C++ Dialects}.
>  -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
>  -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
>
> +@item Remarks
> +@xref{Remarks,,Options to Control Remarks from the Compiler}.
> +@gccoptlist{-fremarks -fno-diagnostics-show-remark-hotness @gol
> +-fno-diagnostics-show-remark-origin -fno-diagnostics-show-remark-pass}
> +
>  @item Preprocessor Options
>  @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
>  @gccoptlist{-A@var{question}=@var{answer} @gol
> @@ -9904,6 +9911,15 @@ Future versions of GCC may provide finer control of this setting
>  using C99's @code{FENV_ACCESS} pragma.  This command-line option
>  will be used to specify the default state for @code{FENV_ACCESS}.
>
> +@item -fsave-optimization-record
> +@opindex fsave-optimization-record
> +Write a SRCFILE.opt-record.json file detailing what optimizations
> +were performed.
> +FIXME: The precise format is not yet set in stone, but it ought
> +to be stabilized and then documented somewhere.
> +FIXME: should this be described here within the optimization options,
> +or within the developer options?
> +
>  @item -fsignaling-nans
>  @opindex fsignaling-nans
>  Compile code assuming that IEEE signaling NaNs may generate user-visible
> @@ -12037,6 +12053,71 @@ The NOP instructions are inserted at---and maybe before, depending on
>  @end table
>
>
> +@node Remarks
> +@section Options to Control Remarks from the Compiler
> +@cindex remarks
> +@cindex options, remarks
> +
> +These options are aimed at advanced users who may be interested
> +in seeing additional diagnostics from the compiler, giving information
> +on the decisions it is making on the code.
> +
> +The precise messages and their format are subject to change.
> +
> +Rather than attempting to parse the textual remark format, consider
> +using @option{-fsave-optimization-record}, which works from the same
> +data internally.
> +
> +@table @gcctabopt
> +@item -fremarks
> +@opindex fremarks
> +Emit diagnostic remarks about optimizations.
> +
> +Enabling this option leads to GCC emitting diagnostics detailing the
> +optimization decisions it is making.
> +
> +For example, this message:
> +
> +@smallexample
> +test.c:13:3: remark:   Symbolic number of iterations is '(unsigned int) n_8(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
> +@end smallexample
> +
> +describes an internal detail of the ``vect'' optimization pass, acting at
> +the given source location within ``test.c'', where the remark was emitted
> +by the function ``vect_analyze_loop_form'' at line 1387 of GCC's source
> +file ``tree-vect-loop.c''.
> +
> +@item -fno-diagnostics-show-remark-hotness
> +@opindex fno-diagnostics-show-remark-hotness
> +@opindex fdiagnostics-show-remark-hotness
> +By default, if diagnostic remarks are enabled, they include information
> +on the execution count, or ``hotness'', of the code being optimized:
> +the ``[count(precise)=76800000]'' in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@item -fno-diagnostics-show-remark-origin
> +@opindex fno-diagnostics-show-remark-origin
> +@opindex fdiagnostics-show-remark-origin
> +By default, if diagnostic remarks are enabled, they include information
> +on where in GCC's own source code (or a plugin's source code) the remark
> +is being emitted from; the
> +``[../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]''
> +in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@item -fno-diagnostics-show-remark-pass
> +@opindex fno-diagnostics-show-remark-pass
> +@opindex fdiagnostics-show-remark-pass
> +By default, if diagnostic remarks are enabled, they include information
> +on which optimization pass emitted the remark: the ``[pass=vect]''
> +in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@end table
> +
>  @node Preprocessor Options
>  @section Options Controlling the Preprocessor
>  @cindex preprocessor options
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..2986317
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,112 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +
> +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_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                            const dump_location_t &loc,
> +                            dump_flags_t extra_dump_flags,
> +                            gimple *gs, int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +                         dump_flags_t extra_dump_flags,
> +                         tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                      va_list ap) ATTRIBUTE_PRINTF (3, 0);
> +
> +  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
> +                          const char *format, va_list ap)
> +    ATTRIBUTE_PRINTF (4, 0);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +  void end_any_optinfo ();
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII class for use in selftests for temporarily using a different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool new_flag_remarks);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 2f11284..3be4af5 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -29,6 +29,16 @@ along with GCC; see the file COPYING3.  If not see
>  #include "profile-count.h"
>  #include "tree-cfg.h"
>  #include "langhooks.h"
> +#include "optinfo.h"
> +#include "cgraph.h" /* for selftests.  */
> +#include "optinfo-emit-json.h"
> +#include "optinfo-internal.h" /* for selftests.  */
> +#include "backend.h"
> +#include "gimple.h" /* for dump_user_location_t ctor.  */
> +#include "rtl.h" /* for dump_user_location_t ctor.  */
> +#include "dump-context.h"
> +#include "tree-pass.h" /* for "current_pass".  */
> +#include "selftest.h"
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -36,18 +46,52 @@ along with GCC; see the file COPYING3.  If not see
>     (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
>
>  static dump_flags_t pflags;                  /* current dump_flags */
> -static dump_flags_t alt_flags;               /* current opt_info flags */
>
>  static void dump_loc (dump_flags_t, FILE *, source_location);
> +
> +/* Current -fopt-info output stream, if any, and flags.  */
> +static FILE *alt_dump_file = NULL;
> +static dump_flags_t alt_flags;
> +
>  static FILE *dump_open_alternate_stream (struct dump_file_info *);
>
>  /* These are currently used for communicating between passes.
>     However, instead of accessing them directly, the passes can use
>     dump_printf () for dumps.  */
>  FILE *dump_file = NULL;
> -FILE *alt_dump_file = NULL;
>  const char *dump_file_name;
>  dump_flags_t dump_flags;
> +bool dumps_are_enabled = false;
> +
> +
> +/* Update the "dumps_are_enabled" global; to be called whenever dump_file
> +   or alt_dump_file change.  */
> +
> +static void
> +refresh_dumps_are_enabled ()
> +{
> +  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
> +}
> +
> +/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
> +   global.  */
> +
> +void
> +set_dump_file (FILE *new_dump_file)
> +{
> +  dump_file = new_dump_file;
> +  refresh_dumps_are_enabled ();
> +}
> +
> +/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
> +   global.  */
> +
> +static void
> +set_alt_dump_file (FILE *new_alt_dump_file)
> +{
> +  alt_dump_file = new_alt_dump_file;
> +  refresh_dumps_are_enabled ();
> +}
>
>  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
>    {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
> @@ -74,14 +118,6 @@ static struct dump_file_info dump_files[TDI_end] =
>    DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
>  };
>
> -/* Define a name->number mapping for a dump flag value.  */
> -template <typename ValueType>
> -struct kv_pair
> -{
> -  const char *const name;      /* the name of the value */
> -  const ValueType value;       /* the value of the name */
> -};
> -
>  /* Table of dump options. This must be consistent with the TDF_* flags
>     in dumpfile.h and opt_info_options below. */
>  static const kv_pair<dump_flags_t> dump_options[] =
> @@ -132,7 +168,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
>  };
>
>  /* Flags used for -fopt-info groups.  */
> -static const kv_pair<optgroup_flags_t> optgroup_options[] =
> +const kv_pair<optgroup_flags_t> optgroup_options[] =
>  {
>    {"ipa", OPTGROUP_IPA},
>    {"loop", OPTGROUP_LOOP},
> @@ -358,9 +394,53 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
>    return stream;
>  }
>
> +/* Implementation of "structured dumping API".  */
> +
> +/* Construct a dump_user_location_t from STMT (using its location and
> +   hotness).  */
> +
> +dump_user_location_t::dump_user_location_t (gimple *stmt)
> +: m_count (), m_loc (UNKNOWN_LOCATION)
> +{
> +  if (stmt)
> +    {
> +      if (stmt->bb)
> +       m_count = stmt->bb->count;
> +      m_loc = gimple_location (stmt);
> +    }
> +}
> +
> +/* Construct a dump_user_location_t from an RTL instruction (using its
> +   location and hotness).  */
> +dump_user_location_t::dump_user_location_t (rtx_insn *insn)
> +: m_count (), m_loc (UNKNOWN_LOCATION)
> +{
> +  if (insn)
> +    {
> +      basic_block bb = BLOCK_FOR_INSN (insn);
> +      if (bb)
> +       m_count = bb->count;
> +      m_loc = INSN_LOCATION (insn);
> +    }
> +}
> +
> +/* Construct from a function declaration.  This one requires spelling out
> +   to avoid accidentally constructing from other kinds of tree.  */
> +
> +dump_user_location_t
> +dump_user_location_t::from_function_decl (tree fndecl)
> +{
> +  gcc_assert (fndecl);
> +
> +  // FIXME: profile count for function?
> +  return dump_user_location_t (profile_count (),
> +                              DECL_SOURCE_LOCATION (fndecl));
> +}
> +
> +
>  /* Print source location on DFILE if enabled.  */
>
> -void
> +static void
>  dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>  {
>    if (dump_kind)
> @@ -373,137 +453,363 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>                   DECL_SOURCE_FILE (current_function_decl),
>                   DECL_SOURCE_LINE (current_function_decl),
>                   DECL_SOURCE_COLUMN (current_function_decl));
> +      /* Indentation based on scope depth.  */
> +      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
>      }
>  }
>
> +/* Implementation of dump_context methods.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> +                               dump_flags_t extra_dump_flags,
> +                               gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
>  }
>
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
>
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs, int spc)
>  {
> +  location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      dump_loc (dump_kind, dump_file, loc);
> +      dump_loc (dump_kind, dump_file, srcloc);
>        print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
>  }
>
>  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
>     DUMP_KIND is enabled.  */
>
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                  tree t)
> +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> +                                dump_flags_t extra_dump_flags,
> +                                tree t)
>  {
>    if (dump_file && (dump_kind & pflags))
>        print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> -}
>
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, extra_dump_flags);
> +    }
> +}
>
> -/* Similar to dump_generic_expr, except additionally print the source
> -   location.  */
> +/* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
> -                      dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                             va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      dump_loc (dump_kind, dump_file, loc);
> -      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> -      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> -/* Output a formatted message using FORMAT on appropriate dump streams.  */
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
>
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
> +                                 const char *format, va_list ap)
>  {
> +  location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      dump_loc (dump_kind, dump_file, srcloc);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> -/* Similar to dump_printf, except source location is also printed.  */
> +/* Output VALUE in decimal to appropriate dump streams.  */
>
> +template<unsigned int N, typename C>
>  void
> -dump_printf_loc (dump_flags_t dump_kind, source_location loc,
> -                const char *format, ...)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  {
> +  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> +  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
>    if (dump_file && (dump_kind & pflags))
> +    print_dec (value, dump_file, sgn);
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
>      {
> -      va_list ap;
> -      dump_loc (dump_kind, dump_file, loc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
>      }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +void
> +dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  if (dump_file && (dump_kind & pflags))
> +    fprintf (dump_file, "%s", node->dump_name ());
>
>    if (alt_dump_file && (dump_kind & alt_flags))
> +    fprintf (alt_dump_file, "%s", node->dump_name ());
> +
> +  if (optinfo_enabled_p ())
>      {
> -      va_list ap;
> -      dump_loc (dump_kind, alt_dump_file, loc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
>      }
>  }
>
> +/* Get the current dump scope-nesting depth.
> +   For use by remarks and -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Push a "scope" opinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +void
> +dump_context::begin_scope (const char *name, const dump_location_t &loc)
> +{
> +  /* Specialcase, to avoid going through dump_printf_loc,
> +     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
> +
> +  if (dump_file)
> +    {
> +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> +      fprintf (dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +  optimization_records_maybe_pop_dump_scope ();
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context, emitting
> +   it as a diagnostic remark or to optimization records as appropriate.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   methods.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                  tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump streams.  */
> +
> +void
> +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
> +  va_end (ap);
> +}
> +
>  /* Output VALUE in decimal to appropriate dump streams.  */
>
>  template<unsigned int N, typename C>
>  void
>  dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  {
> -  STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> -  signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> -  if (dump_file && (dump_kind & pflags))
> -    print_dec (value, dump_file, sgn);
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    print_dec (value, alt_dump_file, sgn);
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -512,6 +818,45 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
>  template void dump_dec (dump_flags_t, const poly_offset_int &);
>  template void dump_dec (dump_flags_t, const poly_widest_int &);
>
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by remarks and -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +get_dump_scope_depth ()
> +{
> +  return dump_context::get ().get_scope_depth ();
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Push a "scope" opinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +void
> +dump_begin_scope (const char *name, const dump_location_t &loc)
> +{
> +  dump_context::get ().begin_scope (name, loc);
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_end_scope ()
> +{
> +  dump_context::get ().end_scope ();
> +}
> +
> +/* End of implementation of "structured dumping API".  */
> +
>  /* Start a dump for PHASE. Store user-supplied dump flags in
>     *FLAG_PTR.  Return the number of streams opened.  Set globals
>     DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
> @@ -541,7 +886,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
>          }
>        free (name);
>        dfi->pstream = stream;
> -      dump_file = dfi->pstream;
> +      set_dump_file (dfi->pstream);
>        /* Initialize current dump flags. */
>        pflags = dfi->pflags;
>      }
> @@ -551,7 +896,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
>      {
>        dfi->alt_stream = stream;
>        count++;
> -      alt_dump_file = dfi->alt_stream;
> +      set_alt_dump_file (dfi->alt_stream);
>        /* Initialize current -fopt-info flags. */
>        alt_flags = dfi->alt_flags;
>      }
> @@ -582,8 +927,8 @@ dump_finish (int phase)
>
>    dfi->alt_stream = NULL;
>    dfi->pstream = NULL;
> -  dump_file = NULL;
> -  alt_dump_file = NULL;
> +  set_dump_file (NULL);
> +  set_alt_dump_file (NULL);
>    dump_flags = TDF_NONE;
>    alt_flags = TDF_NONE;
>    pflags = TDF_NONE;
> @@ -1021,6 +1366,7 @@ dump_basic_block (dump_flags_t dump_kind, basic_block bb, int indent)
>      dump_bb (dump_file, bb, indent, TDF_DETAILS);
>    if (alt_dump_file && (dump_kind & alt_flags))
>      dump_bb (alt_dump_file, bb, indent, TDF_DETAILS);
> +  // TODO: should this also write to optinfo?
>  }
>
>  /* Dump FUNCTION_DECL FN as tree dump PHASE.  */
> @@ -1059,3 +1405,179 @@ enable_rtl_dump_file (void)
>                             NULL);
>    return num_enabled > 0;
>  }
> +
> +#if CHECKING_P
> +
> +/* temp_dump_context's ctor.  Temporarily override the dump_context,
> +   and the value of "flag_remarks" (to forcibly enable optinfo-generation).  */
> +
> +temp_dump_context::temp_dump_context (bool new_flag_remarks)
> +: m_context (),
> +  m_saved (&dump_context ().get ()),
> +  m_saved_flag_remarks (flag_remarks)
> +{
> +  dump_context::s_current = &m_context;
> +  flag_remarks = new_flag_remarks;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved values of dump_context and
> +   "flag_remarks".  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +  flag_remarks = m_saved_flag_remarks;
> +}
> +
> +namespace selftest {
> +
> +/* Verify that the dump_location_t constructor ca

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [committed] Introduce dump_location_t
  2018-06-25 13:35               ` Richard Biener
@ 2018-06-26 13:54                 ` David Malcolm
  2018-06-28 11:29                   ` Richard Biener
  2018-06-26 15:43                 ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE David Malcolm
  2018-06-26 20:27                 ` [PATCH] Hide alt_dump_file within dumpfile.c David Malcolm
  2 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-26 13:54 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > Here's v3 of the patch (one big patch this time, rather than a
> > kit).
> > 
> > Like the v2 patch kit, this patch reuses the existing dump API,
> > rather than inventing its own.
> > 
> > Specifically, it uses the dump_* functions in dumpfile.h that don't
> > take a FILE *, the ones that implicitly write to dump_file and/or
> > alt_dump_file.  I needed a name for them, so I've taken to calling
> > them the "structured dump API" (better name ideas welcome).
> > 
> > v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
> > calls as delimiters when consolidating "dump_*" calls.  There's a
> > new dump_context class which has responsibility for consolidating
> > them into optimization records.
> > 
> > The dump_*_loc calls now capture more than just a location_t: they
> > capture the profile_count and the location in GCC's own sources
> > where
> > the dump is being emitted from.
> > 
> > This works by introducing a new "dump_location_t" class as the
> > argument of those dump_*_loc calls.  The dump_location_t can
> > be constructed from a gimple * or from an rtx_insn *, so that
> > rather than writing:
> > 
> >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> >                    "some message: %i", 42);
> > 
> > you can write:
> > 
> >   dump_printf_loc (MSG_NOTE, stmt,
> >                    "some message: %i", 42);
> > 
> > and the dump_location_t constructor will grab the location_t and
> > profile_count of stmt, and the location of the "dump_printf_loc"
> > callsite (and gracefully handle "stmt" being NULL).
> > 
> > Earlier versions of the patch captured the location of the
> > dump_*_loc call via preprocessor hacks, or didn't work properly;
> > this version of the patch works more cleanly: internally,
> > dump_location_t is split into two new classes:
> >   * dump_user_location_t: the location_t and profile_count within
> >     the *user's code*, and
> >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
> >     the *implementation* code (i.e. GCC or a plugin), captured
> >     "automagically" via default params
> > 
> > These classes are sometimes used elsewhere in the code.  For
> > example, "vect_location" becomes a dump_user_location_t
> > (location_t and profile_count), so that in e.g:
> > 
> >   vect_location = find_loop_location (loop);
> > 
> > it's capturing the location_t and profile_count, and then when
> > it's used here:
> > 
> >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > 
> > the dump_location_t is constructed from the vect_location
> > plus the dump_impl_location_t at that callsite.
> > 
> > In contrast, loop-unroll.c's report_unroll's "locus" param
> > becomes a dump_location_t: we're interested in where it was
> > called from, not in the locations of the various dump_*_loc calls
> > within it.
> > 
> > Previous versions of the patch captured a gimple *, and needed
> > GTY markers; in this patch, the dump_user_location_t is now just a
> > location_t and a profile_count.
> > 
> > The v2 patch added an overload for dump_printf_loc so that you
> > could pass in either a location_t, or the new type; this version
> > of the patch eliminates that: they all now take dump_location_t.
> > 
> > Doing so required adding support for rtx_insn *, so that one can
> > write this kind of thing in RTL passes:
> > 
> >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > 
> > One knock-on effect is that get_loop_location now returns a
> > dump_user_location_t rather than a location_t, so that it has
> > hotness information.
> > 
> > Richi: would you like me to split out this location-handling
> > code into a separate patch?  (It's kind of redundant without
> > adding the remarks and optimization records work, but if that's
> > easier I can do it)
> 
> I think that would be easier because it doesn't require the JSON
> stuff and so I'll happily approve it.
> 
> Thus - trying to review that bits (and sorry for the delay).
> 
> +  location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      dump_loc (dump_kind, dump_file, loc);
> +      dump_loc (dump_kind, dump_file, srcloc);
>        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> 
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
> 
> seeing this in multiple places.  I seem to remember that
> dump_file / alt_dump_file was suposed to handle dumping
> into two locations - a dump file and optinfo (or stdout).  This looks
> like the optinfo "stream" is even more separate.  Could that
> obsolete the alt_dump_file stream?  I'd need to review existing stuff
> in more detail to answer but maybe you already know from recently
> digging into this.

Possibly.  I attempted this in v1 of the patch, but it was mixed in with
a bunch of other stuff.  I'll have another go at doing this.

> Oh, and all the if (optinfo_enable_p ()) stuff is for the followup
> then, right?

Yes.

> I like the boiler-plate changes to dump_* using stuff a lot, so the
> infrastructure to do that (the location wrapping) and these boiler-
> plate
> changes are pre-approved if split out.

Thanks.  I split out the location wrapping, and have committed it to
trunk (r262149).  It's not clear to me exactly what other parts you liked,
so I'm going to try to split out more of the non-JSON bits in the
hope that some parts are good enough as-is, and I'll post them for review
as followups.

For reference, here's what I've committed (I added some obvious changes
to doc/optinfo.texi).

> I think the *_REMARK stuff should get attention of the respective
> maintainers - not sure what the difference between NOTE and REMARK
> is ;)

Me neither :)  I think it might come down to "this is purely a debugging
message for a pass maintainer" (MSG_NOTE) vs "this is a high-level thing
that an advanced user want to see" (MSG_REMARK???).

One idea that occurred to me: are we overusing dump_flags_t?  It's a
mixture of TDF_* bitfields (used by our internal dumps) plus MSG_*
bitfields (used with -fopt-info).  IIRC the only TDF_ bitfield that's
ever used with the MSG_* bitfields is TDF_DETAILS.  Might it make sense
to split out the MSG_* bitfields into a different type (dump_level_t???)
to reinforce this split?  (and thus the dump_* entrypoints that don't take
a FILE * would take this new type).  I'm not sure if this is a good idea
or not.

> Thanks and sorry again for the repeated delays...
> Richard.

Thanks
Dave

[...snip...]

gcc/ChangeLog:
	* cfgloop.c (get_loop_location): Convert return type from
	location_t to dump_user_location_t, replacing INSN_LOCATION lookups
	by implicit construction from rtx_insn *, and using
	dump_user_location_t::from_function_decl for the fallback case.
	* cfgloop.h (get_loop_location): Convert return type from
	location_t to dump_user_location_t.
	* cgraphunit.c (walk_polymorphic_call_targets): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	* coverage.c (get_coverage_counts): Update calls to
	dump_printf_loc to pass in dump_location_t rather than a
	location_t.
	* doc/optinfo.texi (Dump types): Convert example of
	dump_printf_loc from taking "locus" to taking "insn".  Update
	description of the "_loc" calls to cover dump_location_t.
	* dumpfile.c: Include "backend.h", "gimple.h", "rtl.h", and
	"selftest.h".
	(dump_user_location_t::dump_user_location_t): New constructors,
	from gimple *stmt and rtx_insn *.
	(dump_user_location_t::from_function_decl): New function.
	(dump_loc): Make static.
	(dump_gimple_stmt_loc): Convert param "loc" from location_t to
	const dump_location_t &.
	(dump_generic_expr_loc): Delete.
	(dump_printf_loc): Convert param "loc" from location_t to
	const dump_location_t &.
	(selftest::test_impl_location): New function.
	(selftest::dumpfile_c_tests): New function.
	* dumpfile.h: Include "profile-count.h".
	(class dump_user_location_t): New class.
	(struct dump_impl_location_t): New struct.
	(class dump_location_t): New class.
	(dump_printf_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	(dump_generic_expr_loc): Delete.
	(dump_gimple_stmt_loc): Convert 2nd param from source_location to
	const dump_location_t &.
	* gimple-fold.c (fold_gimple_assign): Update call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(gimple_fold_call): Likewise.
	* gimple-loop-interchange.cc
	(loop_cand::analyze_iloop_reduction_var): Update for change to
	check_reduction_path.
	(tree_loop_interchange::interchange): Update for change to
	find_loop_location.
	* graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
	change in return-type of find_loop_location.
	(graphite_regenerate_ast_isl): Likewise.
	* graphite-optimize-isl.c (optimize_isl): Likewise.
	* graphite.c (graphite_transform_loops): Likewise.
	* ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
	pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
	* ipa.c (walk_polymorphic_call_targets): Likewise.
	* loop-unroll.c (report_unroll): Convert "locus" param from
	location_t to dump_location_t.
	(decide_unrolling): Update for change to get_loop_location's
	return type.
	* omp-grid.c (struct grid_prop): Convert field "target_loc" from
	location_t to dump_user_location_t.
	(grid_find_single_omp_among_assignments_1): Updates calls to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the gimple stmt.
	(grid_parallel_clauses_gridifiable): Convert "tloc" from
	location_t to dump_location_t.  Updates calls to dump_printf_loc
	to pass in a dump_location_t rather than a location_t, via the
	gimple stmt.
	(grid_inner_loop_gridifiable_p): Likewise.
	(grid_dist_follows_simple_pattern): Likewise.
	(grid_gfor_follows_tiling_pattern): Likewise.
	(grid_target_follows_gridifiable_pattern): Likewise.
	(grid_attempt_target_gridification): Convert initialization
	of local "grid" from memset to zero-initialization; FIXME: does
	this require C++11?  Update call to dump_printf_loc to pass in a
	optinfo_location rather than a location_t, via the gimple stmt.
	* profile.c (read_profile_edge_counts): Updates call to
	dump_printf_loc to pass in a dump_location_t rather than a
	location_t
	(compute_branch_probabilities): Likewise.
	* selftest-run-tests.c (selftest::run_tests): Call
	dumpfile_c_tests.
	* selftest.h (dumpfile_c_tests): New decl.
	* tree-loop-distribution.c (pass_loop_distribution::execute):
	Update for change in return type of find_loop_location.
	* tree-parloops.c (parallelize_loops): Likewise.
	* tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
	"locus" from location_t to dump_user_location_t.
	(canonicalize_loop_induction_variables): Likewise.
	* tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
	for change in return type of find_loop_location.
	* tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
	to dump_printf_loc to pass in a dump_location_t rather than a
	location_t, via the stmt.
	* tree-ssa-sccvn.c (eliminate_dom_walker::before_dom_children):
	Likewise.
	* tree-vect-loop-manip.c (find_loop_location): Convert return
	type from source_location to dump_user_location_t.
	(vect_do_peeling): Update for above change.
	(vect_loop_versioning): Update for change in type of
	vect_location.
	* tree-vect-loop.c (check_reduction_path): Convert "loc" param
	from location_t to dump_user_location_t.
	(vect_estimate_min_profitable_iters): Update for change in type
	of vect_location.
	* tree-vect-slp.c (vect_print_slp_tree): Convert param "loc" from
	location_t to dump_location_t.
	(vect_slp_bb): Update for change in type of vect_location.
	* tree-vectorizer.c (vect_location): Convert from source_location
	to dump_user_location_t.
	(try_vectorize_loop_1): Update for change in vect_location's type.
	(vectorize_loops): Likewise.
	(increase_alignment): Likewise.
	* tree-vectorizer.h (vect_location): Convert from source_location
	to dump_user_location_t.
	(find_loop_location): Convert return type from source_location to
	dump_user_location_t.
	(check_reduction_path): Convert 1st param from location_t to
	dump_user_location_t.
	* value-prof.c (check_counter): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t; update
	call to error_at for change in type of "locus".
	(check_ic_target): Update call to dump_printf_loc to
	pass in a dump_user_location_t rather than a location_t, via the
	call_stmt.
---
 gcc/cfgloop.c                    |  12 +--
 gcc/cfgloop.h                    |   2 +-
 gcc/cgraphunit.c                 |   3 +-
 gcc/coverage.c                   |  22 ++++--
 gcc/doc/optinfo.texi             |  16 +++-
 gcc/dumpfile.c                   | 133 ++++++++++++++++++++++++++-------
 gcc/dumpfile.h                   | 156 +++++++++++++++++++++++++++++++++++++--
 gcc/gimple-fold.c                |   6 +-
 gcc/gimple-loop-interchange.cc   |   4 +-
 gcc/graphite-isl-ast-to-gimple.c |   4 +-
 gcc/graphite-optimize-isl.c      |   4 +-
 gcc/graphite.c                   |   2 +-
 gcc/ipa-devirt.c                 |   3 +-
 gcc/ipa-prop.c                   |  10 +--
 gcc/ipa.c                        |   9 +--
 gcc/loop-unroll.c                |   4 +-
 gcc/omp-grid.c                   |  47 ++++++------
 gcc/profile.c                    |  14 +++-
 gcc/selftest-run-tests.c         |   1 +
 gcc/selftest.h                   |   1 +
 gcc/tree-loop-distribution.c     |   2 +-
 gcc/tree-parloops.c              |   3 +-
 gcc/tree-ssa-loop-ivcanon.c      |   8 +-
 gcc/tree-ssa-loop-ivopts.c       |   2 +-
 gcc/tree-ssa-loop-niter.c        |   2 +-
 gcc/tree-ssa-sccvn.c             |   3 +-
 gcc/tree-vect-loop-manip.c       |  16 ++--
 gcc/tree-vect-loop.c             |   8 +-
 gcc/tree-vect-slp.c              |   5 +-
 gcc/tree-vectorizer.c            |  14 ++--
 gcc/tree-vectorizer.h            |   8 +-
 gcc/value-prof.c                 |  15 ++--
 32 files changed, 386 insertions(+), 153 deletions(-)

diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c
index 8af793c..e27cd39 100644
--- a/gcc/cfgloop.c
+++ b/gcc/cfgloop.c
@@ -1800,7 +1800,7 @@ loop_exits_from_bb_p (struct loop *loop, basic_block bb)
 
 /* Return location corresponding to the loop control condition if possible.  */
 
-location_t
+dump_user_location_t
 get_loop_location (struct loop *loop)
 {
   rtx_insn *insn = NULL;
@@ -1819,7 +1819,7 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* If loop has a single exit, then the loop control branch
@@ -1829,24 +1829,24 @@ get_loop_location (struct loop *loop)
       FOR_BB_INSNS_REVERSE (exit->src, insn)
         {
           if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-            return INSN_LOCATION (insn);
+            return insn;
         }
     }
   /* Next check the latch, to see if it is non-empty.  */
   FOR_BB_INSNS_REVERSE (loop->latch, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* Finally, if none of the above identifies the loop control branch,
      return the first location in the loop header.  */
   FOR_BB_INSNS (loop->header, insn)
     {
       if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
-        return INSN_LOCATION (insn);
+        return insn;
     }
   /* If all else fails, simply return the current function location.  */
-  return DECL_SOURCE_LOCATION (current_function_decl);
+  return dump_user_location_t::from_function_decl (current_function_decl);
 }
 
 /* Records that every statement in LOOP is executed I_BOUND times.
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index af9bfab..80a31c4 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -357,7 +357,7 @@ extern bool loop_exit_edge_p (const struct loop *, const_edge);
 extern bool loop_exits_to_bb_p (struct loop *, basic_block);
 extern bool loop_exits_from_bb_p (struct loop *, basic_block);
 extern void mark_loop_exit_edges (void);
-extern location_t get_loop_location (struct loop *loop);
+extern dump_user_location_t get_loop_location (struct loop *loop);
 
 /* Loops & cfg manipulation.  */
 extern basic_block *get_loop_body (const struct loop *);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 04b6919..7cfb8a0 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 	    }
           if (dump_enabled_p ())
             {
-	      location_t locus = gimple_location_safe (edge->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->name (), target->name ());
 	    }
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 84fff13..350cc45 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -342,12 +342,16 @@ get_coverage_counts (unsigned counter, unsigned expected,
       static int warned = 0;
 
       if (!warned++ && dump_enabled_p ())
-	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
-                         (flag_guess_branch_prob
-                          ? "file %s not found, execution counts estimated\n"
-                          : "file %s not found, execution counts assumed to "
-                            "be zero\n"),
-                         da_file_name);
+	{
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			   (flag_guess_branch_prob
+			    ? "file %s not found, execution counts estimated\n"
+			    : "file %s not found, execution counts assumed to "
+			    "be zero\n"),
+			   da_file_name);
+	}
       return NULL;
     }
   if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
@@ -378,7 +382,9 @@ get_coverage_counts (unsigned counter, unsigned expected,
 		    "its profile data (counter %qs)", id, ctr_names[counter]);
       if (warning_printed && dump_enabled_p ())
 	{
-          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	  dump_user_location_t loc
+	    = dump_user_location_t::from_location_t (input_location);
+          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                            "use -Wno-error=coverage-mismatch to tolerate "
                            "the mismatch but performance may drop if the "
                            "function is hot\n");
@@ -386,7 +392,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
 	  if (!seen_error ()
 	      && !warned++)
 	    {
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
                                "coverage mismatch ignored\n");
 	      dump_printf (MSG_OPTIMIZED_LOCATIONS,
                            flag_guess_branch_prob
diff --git a/gcc/doc/optinfo.texi b/gcc/doc/optinfo.texi
index 8c28501..6202802 100644
--- a/gcc/doc/optinfo.texi
+++ b/gcc/doc/optinfo.texi
@@ -168,7 +168,7 @@ when any of the following flags is enabled
 
 @example
 int report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_RTL | TDF_DETAILS;
-dump_printf_loc (report_flags, locus,
+dump_printf_loc (report_flags, insn,
                  "loop turned into non-loop; it never loops.\n");
 @end example
 
@@ -181,7 +181,19 @@ Output gimple statement.
 
 Note that the above methods also have variants prefixed with
 @code{_loc}, such as @code{dump_printf_loc}, which are similar except
-they also output the source location information.
+they also output the source location information.  The @code{_loc} variants
+take a @code{const dump_location_t &}.  This class can be constructed from
+a @code{gimple *} or from a @code{rtx_insn *}, and so callers can pass
+a @code{gimple *} or a @code{rtx_insn *} as the @code{_loc} argument.
+The @code{dump_location_t} constructor will extract the source location
+from the statement or instruction, along with the profile count, and
+the location in GCC's own source code (or the plugin) from which the dump
+call was emitted.  Only the source location is currently used.
+There is also a @code{dump_user_location_t} class, capturing the
+source location and profile count, but not the dump emission location,
+so that locations in the user's code can be passed around.  This
+can also be constructed from a @code{gimple *} and from a @code{rtx_insn *},
+and it too can be passed as the @code{_loc} argument.
 
 @end ftable
 
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 2f11284..122e420 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -29,6 +29,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "tree-cfg.h"
 #include "langhooks.h"
+#include "backend.h" /* for gimple.h.  */
+#include "gimple.h" /* for dump_user_location_t ctor.  */
+#include "rtl.h" /* for dump_user_location_t ctor.  */
+#include "selftest.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -358,9 +362,51 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
   return stream;
 }
 
+/* Construct a dump_user_location_t from STMT (using its location and
+   hotness).  */
+
+dump_user_location_t::dump_user_location_t (gimple *stmt)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (stmt)
+    {
+      if (stmt->bb)
+	m_count = stmt->bb->count;
+      m_loc = gimple_location (stmt);
+    }
+}
+
+/* Construct a dump_user_location_t from an RTL instruction (using its
+   location and hotness).  */
+
+dump_user_location_t::dump_user_location_t (rtx_insn *insn)
+: m_count (), m_loc (UNKNOWN_LOCATION)
+{
+  if (insn)
+    {
+      basic_block bb = BLOCK_FOR_INSN (insn);
+      if (bb)
+	m_count = bb->count;
+      m_loc = INSN_LOCATION (insn);
+    }
+}
+
+/* Construct from a function declaration.  This one requires spelling out
+   to avoid accidentally constructing from other kinds of tree.  */
+
+dump_user_location_t
+dump_user_location_t::from_function_decl (tree fndecl)
+{
+  gcc_assert (fndecl);
+
+  // FIXME: profile count for function?
+  return dump_user_location_t (profile_count (),
+			       DECL_SOURCE_LOCATION (fndecl));
+}
+
 /* Print source location on DFILE if enabled.  */
 
-void
+static void
 dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
 {
   if (dump_kind)
@@ -393,18 +439,19 @@ dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
 {
+  location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
     {
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
 }
@@ -423,27 +470,6 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
 }
 
-
-/* Similar to dump_generic_expr, except additionally print the source
-   location.  */
-
-void
-dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
-		       dump_flags_t extra_dump_flags, tree t)
-{
-  if (dump_file && (dump_kind & pflags))
-    {
-      dump_loc (dump_kind, dump_file, loc);
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      dump_loc (dump_kind, alt_dump_file, loc);
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
-    }
-}
-
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
@@ -469,13 +495,14 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
 /* Similar to dump_printf, except source location is also printed.  */
 
 void
-dump_printf_loc (dump_flags_t dump_kind, source_location loc,
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 		 const char *format, ...)
 {
+  location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
     {
       va_list ap;
-      dump_loc (dump_kind, dump_file, loc);
+      dump_loc (dump_kind, dump_file, srcloc);
       va_start (ap, format);
       vfprintf (dump_file, format, ap);
       va_end (ap);
@@ -484,7 +511,7 @@ dump_printf_loc (dump_flags_t dump_kind, source_location loc,
   if (alt_dump_file && (dump_kind & alt_flags))
     {
       va_list ap;
-      dump_loc (dump_kind, alt_dump_file, loc);
+      dump_loc (dump_kind, alt_dump_file, srcloc);
       va_start (ap, format);
       vfprintf (alt_dump_file, format, ap);
       va_end (ap);
@@ -1059,3 +1086,53 @@ enable_rtl_dump_file (void)
 			    NULL);
   return num_enabled > 0;
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that the dump_location_t constructors capture the source location
+   at which they were called (provided that the build compiler is sufficiently
+   recent).  */
+
+static void
+test_impl_location ()
+{
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+  /* Default ctor.  */
+  {
+    dump_location_t loc;
+    const int expected_line = __LINE__ - 1;
+    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from a gimple.  */
+  {
+    dump_location_t loc ((gimple *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+
+  /* Constructing from an rtx_insn.  */
+  {
+    dump_location_t loc ((rtx_insn *)NULL);
+    const int expected_line = __LINE__ - 1;
+    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
+    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
+  }
+#endif
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+dumpfile_c_tests ()
+{
+  test_impl_location ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index f6ad670..90d8930 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DUMPFILE_H
 #define GCC_DUMPFILE_H 1
 
+#include "profile-count.h"
 
 /* Different tree dump places.  When you add new tree dump places,
    extend the DUMP_FILES array in dumpfile.c.  */
@@ -268,20 +269,165 @@ struct dump_file_info
   bool graph_dump_initialized;
 };
 
+/* A class for describing where in the user's source that a dump message
+   relates to, with various constructors for convenience.
+   In particular, this lets us associate dump messages
+   with hotness information (e.g. from PGO), allowing them to
+   be prioritized by code hotness.  */
+
+class dump_user_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_user_location_t () : m_count (), m_loc (UNKNOWN_LOCATION) {}
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_user_location_t (gimple *stmt);
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_user_location_t (rtx_insn *insn);
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information); it thus needs to be spelled out.  */
+  static dump_user_location_t
+  from_location_t (location_t loc)
+  {
+    return dump_user_location_t (profile_count (), loc);
+  }
+
+  /* Construct from a function declaration.  This one requires spelling out
+     to avoid accidentally constructing from other kinds of tree.  */
+  static dump_user_location_t
+  from_function_decl (tree fndecl);
+
+  profile_count get_count () const { return m_count; }
+  location_t get_location_t () const { return m_loc; }
+
+ private:
+  /* Private ctor from count and location, for use by from_location_t.  */
+  dump_user_location_t (profile_count count, location_t loc)
+    : m_count (count), m_loc (loc)
+  {}
+
+  profile_count m_count;
+  location_t m_loc;
+};
+
+/* A class for identifying where in the compiler's own source
+   (or a plugin) that a dump message is being emitted from.  */
+
+struct dump_impl_location_t
+{
+  dump_impl_location_t (
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+			const char *file = __builtin_FILE (),
+			int line = __builtin_LINE (),
+			const char *function = __builtin_FUNCTION ()
+#else
+			const char *file = __FILE__,
+			int line = __LINE__,
+			const char *function = NULL
+#endif
+  )
+  : m_file (file), m_line (line), m_function (function)
+  {}
+
+  const char *m_file;
+  int m_line;
+  const char *m_function;
+};
+
+/* A bundle of information for describing the location of a dump message:
+   (a) the source location and hotness within the user's code, together with
+   (b) the source location within the compiler/plugin.
+
+   The constructors use default parameters so that (b) gets sets up
+   automatically.
+
+   The upshot is that you can pass in e.g. a gimple * to dump_printf_loc,
+   and the dump call will automatically record where in GCC's source
+   code the dump was emitted from.  */
+
+class dump_location_t
+{
+ public:
+  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
+  dump_location_t (const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t ()),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a gimple statement (using its location and hotness).  */
+  dump_location_t (gimple *stmt,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (stmt)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from an RTL instruction (using its location and hotness).  */
+  dump_location_t (rtx_insn *insn,
+		   const dump_impl_location_t &impl_location
+		   = dump_impl_location_t ())
+  : m_user_location (dump_user_location_t (insn)),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a dump_user_location_t.  */
+  dump_location_t (const dump_user_location_t &user_location,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  : m_user_location (user_location),
+    m_impl_location (impl_location)
+  {
+  }
+
+  /* Construct from a location_t.  This one is deprecated (since it doesn't
+     capture hotness information), and thus requires spelling out.  */
+  static dump_location_t
+  from_location_t (location_t loc,
+		   const dump_impl_location_t &impl_location
+		     = dump_impl_location_t ())
+  {
+    return dump_location_t (dump_user_location_t::from_location_t (loc),
+			    impl_location);
+  }
+
+  const dump_user_location_t &
+  get_user_location () const { return m_user_location; }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_impl_location; }
+
+  location_t get_location_t () const
+  {
+    return m_user_location.get_location_t ();
+  }
+
+  profile_count get_count () const { return m_user_location.get_count (); }
+
+ private:
+  dump_user_location_t m_user_location;
+  dump_impl_location_t m_impl_location;
+};
+
 /* In dumpfile.c */
 extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
-extern void dump_printf_loc (dump_flags_t, source_location,
-                             const char *, ...) ATTRIBUTE_PRINTF_3;
+extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
+			     const char *, ...) ATTRIBUTE_PRINTF_3;
 extern void dump_function (int phase, tree fn);
 extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
 extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
-				  gimple *, int);
+extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
+				  dump_flags_t, gimple *, int);
 extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
 extern void print_combine_total_stats (void);
 extern bool enable_rtl_dump_file (void);
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index a01bce7..f12e4a7 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -347,8 +347,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 		  {
 		    if (dump_enabled_p ())
 		      {
-			location_t loc = gimple_location_safe (stmt);
-			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+			dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 					 "resolving virtual function address "
 					 "reference to function %s\n",
 					 targets.length () == 1
@@ -4061,8 +4060,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 	      tree lhs = gimple_call_lhs (stmt);
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				   "folding virtual function call to %s\n",
 		 		   targets.length () == 1
 		  		   ? targets[0]->name ()
diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
index eb35263..08aeb8e 100644
--- a/gcc/gimple-loop-interchange.cc
+++ b/gcc/gimple-loop-interchange.cc
@@ -523,7 +523,7 @@ loop_cand::analyze_iloop_reduction_var (tree var)
 
   /* Handle and verify a series of stmts feeding the reduction op.  */
   if (single_use != next_def
-      && !check_reduction_path (UNKNOWN_LOCATION, m_loop, phi, next,
+      && !check_reduction_path (dump_user_location_t (), m_loop, phi, next,
 				gimple_assign_rhs_code (single_use)))
     return false;
 
@@ -1578,7 +1578,7 @@ bool
 tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
 				    vec<ddr_p> ddrs)
 {
-  location_t loc = find_loop_location (m_loop_nest[0]);
+  dump_user_location_t loc = find_loop_location (m_loop_nest[0]);
   bool changed_p = false;
   /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
      The overall effect is to push inner loop to outermost level in whole
diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c
index b607b12..9e78465 100644
--- a/gcc/graphite-isl-ast-to-gimple.c
+++ b/gcc/graphite-isl-ast-to-gimple.c
@@ -1409,7 +1409,7 @@ scop_to_isl_ast (scop_p scop)
   isl_ctx_set_max_operations (scop->isl_context, old_max_operations);
   if (isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -1518,7 +1518,7 @@ graphite_regenerate_ast_isl (scop_p scop)
 
   if (t.codegen_error_p ())
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
 		       "loop nest not optimized, code generation error\n");
diff --git a/gcc/graphite-optimize-isl.c b/gcc/graphite-optimize-isl.c
index 456a797..35e9ac0 100644
--- a/gcc/graphite-optimize-isl.c
+++ b/gcc/graphite-optimize-isl.c
@@ -160,7 +160,7 @@ optimize_isl (scop_p scop)
   if (!scop->transformed_schedule
       || isl_ctx_last_error (scop->isl_context) != isl_error_none)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
 	dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
@@ -182,7 +182,7 @@ optimize_isl (scop_p scop)
 
   if (same_schedule)
     {
-      location_t loc = find_loop_location
+      dump_user_location_t loc = find_loop_location
 	(scop->scop_info->region.entry->dest->loop_father);
       dump_printf_loc (MSG_NOTE, loc,
 		       "loop nest not optimized, optimized schedule is "
diff --git a/gcc/graphite.c b/gcc/graphite.c
index bcf4828..ddf16a8 100644
--- a/gcc/graphite.c
+++ b/gcc/graphite.c
@@ -412,7 +412,7 @@ graphite_transform_loops (void)
 	changed = true;
 	if (graphite_regenerate_ast_isl (scop))
 	  {
-	    location_t loc = find_loop_location
+	    dump_user_location_t loc = find_loop_location
 	      (scops[i]->scop_info->region.entry->dest->loop_father);
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
 			     "loop nest optimized\n");
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 308b6e6..e99d8cc 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -3755,8 +3755,7 @@ ipa_devirt (void)
 	      {
 		if (dump_enabled_p ())
                   {
-                    location_t locus = gimple_location_safe (e->call_stmt);
-                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
 				     "speculatively devirtualizing call "
 				     "in %s to %s\n",
 				     n->dump_name (),
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 000c05f..8b19fe3 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -2842,8 +2842,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 	    {
 	      if (dump_enabled_p ())
 		{
-		  location_t loc = gimple_location_safe (ie->call_stmt);
-		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 				   "discovered direct call non-invariant %s\n",
 				   ie->caller->dump_name ());
 		}
@@ -2853,8 +2852,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
 
           if (dump_enabled_p ())
 	    {
-	      location_t loc = gimple_location_safe (ie->call_stmt);
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 			       "discovered direct call to non-function in %s, "
 			       "making it __builtin_unreachable\n",
 			       ie->caller->dump_name ());
@@ -2942,9 +2940,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
      }
   if (dump_enabled_p ())
     {
-      location_t loc = gimple_location_safe (ie->call_stmt);
-
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
 		       "converting indirect call in %s to direct call to %s\n",
 		       ie->caller->name (), callee->name ());
     }
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 82fc334..3b6b5e5 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -225,13 +225,8 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
 	  if (dump_enabled_p ())
-            {
-	      location_t locus;
-	      if (edge->call_stmt)
-		locus = gimple_location (edge->call_stmt);
-	      else
-		locus = UNKNOWN_LOCATION;
-	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+	    {
+	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
 			       "devirtualizing call in %s to %s\n",
 			       edge->caller->dump_name (),
 			       target->dump_name ());
diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c
index 5a03932..48bbda0 100644
--- a/gcc/loop-unroll.c
+++ b/gcc/loop-unroll.c
@@ -189,7 +189,7 @@ static rtx get_expansion (struct var_to_expand *);
    appropriate given the dump or -fopt-info settings.  */
 
 static void
-report_unroll (struct loop *loop, location_t locus)
+report_unroll (struct loop *loop, dump_location_t locus)
 {
   dump_flags_t report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS;
 
@@ -220,7 +220,7 @@ decide_unrolling (int flags)
   FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
     {
       loop->lpt_decision.decision = LPT_NONE;
-      location_t locus = get_loop_location (loop);
+      dump_user_location_t locus = get_loop_location (loop);
 
       if (dump_enabled_p ())
 	dump_printf_loc (MSG_NOTE, locus,
diff --git a/gcc/omp-grid.c b/gcc/omp-grid.c
index ffa301e..6edc92f 100644
--- a/gcc/omp-grid.c
+++ b/gcc/omp-grid.c
@@ -91,7 +91,7 @@ struct grid_prop
   bool tiling;
   /* Location of the target construct for optimization information
      messages.  */
-  location_t target_loc;
+  dump_user_location_t target_loc;
   /* The collapse clause of the involved loops.  Collapse value of all of them
      must be the same for gridification to take place.  */
   size_t collapse;
@@ -177,10 +177,10 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "%s construct "
 				   "contains multiple OpenMP constructs\n",
 				   name);
-		  dump_printf_loc (MSG_NOTE, gimple_location (*ret),
+		  dump_printf_loc (MSG_NOTE, *ret,
 				   "The first OpenMP construct within "
 				   "a parallel\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "The second OpenMP construct within "
 				   "a parallel\n");
 		}
@@ -195,7 +195,7 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "%s construct contains "
 			       "a complex statement\n", name);
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "gridification\n");
 	    }
@@ -286,7 +286,7 @@ grid_find_ungridifiable_statement (gimple_stmt_iterator *gsi,
    loop that is evaluated for possible gridification.  */
 
 static bool
-grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
+grid_parallel_clauses_gridifiable (gomp_parallel *par, dump_user_location_t tloc)
 {
   tree clauses = gimple_omp_parallel_clauses (par);
   while (clauses)
@@ -300,7 +300,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 			       GRID_MISSED_MSG_PREFIX "because there is "
 			       "a num_threads clause of the parallel "
 			       "construct\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a num_threads clause\n");
 	    }
 	  return false;
@@ -311,7 +311,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, tloc,
 			       GRID_MISSED_MSG_PREFIX "a reduction clause "
 			       "is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (par),
+	      dump_printf_loc (MSG_NOTE, par,
 			       "Parallel construct has a reduction clause\n");
 	    }
 	  return false;
@@ -341,7 +341,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			   GRID_MISSED_MSG_PREFIX "the inner loop "
 			   "loop bounds computation contains a complex "
 			   "statement\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -361,7 +361,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the inner loop "
 				   "has a non-automatic schedule clause\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+		  dump_printf_loc (MSG_NOTE, gfor,
 				   "Loop construct has a non automatic "
 				   "schedule clause\n");
 		}
@@ -375,7 +375,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "a reduction "
 			       "clause is present\n ");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct has a reduction schedule "
 			       "clause\n");
 	    }
@@ -404,7 +404,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 			     GRID_MISSED_MSG_PREFIX "the inner loop contains "
 			     "statement %s which cannot be transformed\n",
 			     gimple_code_name[(int) gimple_code (bad)]);
-	  dump_printf_loc (MSG_NOTE, gimple_location (bad),
+	  dump_printf_loc (MSG_NOTE, bad,
 			   "This statement cannot be analyzed for "
 			   "gridification\n");
 	}
@@ -422,7 +422,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
 static bool
 grid_dist_follows_simple_pattern (gomp_for *dist, grid_prop *grid)
 {
-  location_t tloc = grid->target_loc;
+  dump_user_location_t tloc = grid->target_loc;
   gimple *stmt = grid_find_single_omp_among_assignments (gimple_omp_body (dist),
 							 grid, "distribute");
   gomp_parallel *par;
@@ -468,7 +468,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop is not "
 			   "a simple for loop\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "This statement is not a simple for loop\n");
 	}
       return false;
@@ -484,7 +484,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			   GRID_MISSED_MSG_PREFIX "an inner loop does not "
 			   "have use the same collapse clause\n");
-	  dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	  dump_printf_loc (MSG_NOTE, gfor,
 			   "Loop construct uses a different collapse clause\n");
 	}
       return false;
@@ -524,7 +524,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute and "
 			       "an internal loop do not agree on tile size\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (gfor),
+	      dump_printf_loc (MSG_NOTE, gfor,
 			       "Loop construct does not seem to loop over "
 			       "a tile size\n");
 	    }
@@ -636,7 +636,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 				   GRID_MISSED_MSG_PREFIX "the distribute "
 				   "construct contains a try..catch region\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (try_stmt),
+		  dump_printf_loc (MSG_NOTE, try_stmt,
 				   "This statement cannot be analyzed for "
 				   "tiled gridification\n");
 		}
@@ -661,7 +661,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a call\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -677,7 +677,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a parallel "
 				   "construct contains another parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This parallel construct is nested in "
 				   "another one\n");
 		}
@@ -698,7 +698,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 				   GRID_MISSED_MSG_PREFIX "a loop "
 				   "construct is not nested within a parallel "
 				   "construct\n");
-		  dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+		  dump_printf_loc (MSG_NOTE, stmt,
 				   "This loop construct is not nested in "
 				   "a parallel construct\n");
 		}
@@ -714,7 +714,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
 	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
 			       GRID_MISSED_MSG_PREFIX "the distribute "
 			       "construct contains a complex statement\n");
-	      dump_printf_loc (MSG_NOTE, gimple_location (stmt),
+	      dump_printf_loc (MSG_NOTE, stmt,
 			       "This statement cannot be analyzed for "
 			       "tiled gridification\n");
 	    }
@@ -734,7 +734,7 @@ grid_target_follows_gridifiable_pattern (gomp_target *target, grid_prop *grid)
   if (gimple_omp_target_kind (target) != GF_OMP_TARGET_KIND_REGION)
     return false;
 
-  location_t tloc = gimple_location (target);
+  dump_user_location_t tloc = target;
   grid->target_loc = tloc;
   gimple *stmt
     = grid_find_single_omp_among_assignments (gimple_omp_body (target),
@@ -1257,14 +1257,13 @@ grid_attempt_target_gridification (gomp_target *target,
 				   gbind *tgt_bind)
 {
   /* removed group_size */
-  grid_prop grid;
-  memset (&grid, 0, sizeof (grid));
+  grid_prop grid = {};
   if (!target || !grid_target_follows_gridifiable_pattern (target, &grid))
     return;
 
   location_t loc = gimple_location (target);
   if (dump_enabled_p ())
-    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, target,
 		     "Target construct will be turned into a gridified HSA "
 		     "kernel\n");
 
diff --git a/gcc/profile.c b/gcc/profile.c
index 8ba6dc7..0cd0270 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -447,9 +447,14 @@ read_profile_edge_counts (gcov_type *exec_counts)
 		      {
 			static bool informed = 0;
 			if (dump_enabled_p () && !informed)
-		          dump_printf_loc (MSG_NOTE, input_location,
-                                           "corrupted profile info: edge count"
-                                           " exceeds maximal count\n");
+			  {
+			    dump_location_t loc
+			      = dump_location_t::from_location_t
+			        (input_location);
+			    dump_printf_loc (MSG_NOTE, loc,
+					     "corrupted profile info: edge count"
+					     " exceeds maximal count\n");
+			  }
 			informed = 1;
 		      }
 		    else
@@ -672,7 +677,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum)
          if (dump_enabled_p () && informed == 0)
            {
              informed = 1;
-             dump_printf_loc (MSG_NOTE, input_location,
+             dump_printf_loc (MSG_NOTE,
+			      dump_location_t::from_location_t (input_location),
                               "correcting inconsistent profile data\n");
            }
          correct_negative_edge_counts ();
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index fe221ff..a9aacc02 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -59,6 +59,7 @@ selftest::run_tests ()
   /* Low-level data structures.  */
   bitmap_c_tests ();
   sbitmap_c_tests ();
+  dumpfile_c_tests ();
   et_forest_c_tests ();
   hash_map_tests_c_tests ();
   hash_set_tests_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index fc47b2c..a5507cc 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -188,6 +188,7 @@ extern void attribute_c_tests ();
 extern void bitmap_c_tests ();
 extern void diagnostic_c_tests ();
 extern void diagnostic_show_locus_c_tests ();
+extern void dumpfile_c_tests ();
 extern void edit_context_c_tests ();
 extern void et_forest_c_tests ();
 extern void fibonacci_heap_c_tests ();
diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
index c6e0a60..1206614 100644
--- a/gcc/tree-loop-distribution.c
+++ b/gcc/tree-loop-distribution.c
@@ -3117,7 +3117,7 @@ pass_loop_distribution::execute (function *fun)
 	    break;
 
 	  const char *str = loop->inner ? " nest" : "";
-	  location_t loc = find_loop_location (loop);
+	  dump_user_location_t loc = find_loop_location (loop);
 	  if (!cd)
 	    {
 	      calculate_dominance_info (CDI_DOMINATORS);
diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
index c49f032..e79a954 100644
--- a/gcc/tree-parloops.c
+++ b/gcc/tree-parloops.c
@@ -3286,7 +3286,6 @@ parallelize_loops (bool oacc_kernels_p)
   struct tree_niter_desc niter_desc;
   struct obstack parloop_obstack;
   HOST_WIDE_INT estimated;
-  source_location loop_loc;
 
   /* Do not parallelize loops in the functions created by parallelization.  */
   if (!oacc_kernels_p
@@ -3411,7 +3410,7 @@ parallelize_loops (bool oacc_kernels_p)
       changed = true;
       skip_loop = loop->inner;
 
-      loop_loc = find_loop_location (loop);
+      dump_user_location_t loop_loc = find_loop_location (loop);
       if (loop->inner)
 	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loop_loc,
 			 "parallelizing outer loop %d\n", loop->num);
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 24bf60e..5f741c3 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -691,7 +691,7 @@ try_unroll_loop_completely (struct loop *loop,
 			    edge exit, tree niter, bool may_be_zero,
 			    enum unroll_level ul,
 			    HOST_WIDE_INT maxiter,
-			    location_t locus, bool allow_peel)
+			    dump_user_location_t locus, bool allow_peel)
 {
   unsigned HOST_WIDE_INT n_unroll = 0;
   bool n_unroll_found = false;
@@ -1162,7 +1162,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
   tree niter;
   HOST_WIDE_INT maxiter;
   bool modified = false;
-  location_t locus = UNKNOWN_LOCATION;
+  dump_user_location_t locus;
   struct tree_niter_desc niter_desc;
   bool may_be_zero = false;
 
@@ -1177,7 +1177,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	= niter_desc.may_be_zero && !integer_zerop (niter_desc.may_be_zero);
     }
   if (TREE_CODE (niter) == INTEGER_CST)
-    locus = gimple_location (last_stmt (exit->src));
+    locus = last_stmt (exit->src);
   else
     {
       /* For non-constant niter fold may_be_zero into niter again.  */
@@ -1204,7 +1204,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
 	niter = find_loop_niter_by_eval (loop, &exit);
 
       if (exit)
-        locus = gimple_location (last_stmt (exit->src));
+        locus = last_stmt (exit->src);
 
       if (TREE_CODE (niter) != INTEGER_CST)
 	exit = NULL;
diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
index 519649a..6b445bd 100644
--- a/gcc/tree-ssa-loop-ivopts.c
+++ b/gcc/tree-ssa-loop-ivopts.c
@@ -7535,7 +7535,7 @@ tree_ssa_iv_optimize_loop (struct ivopts_data *data, struct loop *loop)
 
   gcc_assert (!data->niters);
   data->current_loop = loop;
-  data->loop_loc = find_loop_location (loop);
+  data->loop_loc = find_loop_location (loop).get_location_t ();
   data->speed = optimize_loop_for_speed_p (loop);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index f5ffc0f..03588a0 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -2627,7 +2627,7 @@ number_of_iterations_exit (struct loop *loop, edge exit,
     return true;
 
   if (warn)
-    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
+    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
 		     "missed loop optimization: niters analysis ends up "
 		     "with assumptions.\n");
 
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 3d025c2..e5eddf9 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -5866,8 +5866,7 @@ eliminate_dom_walker::before_dom_children (basic_block b)
 		    fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
 		  if (dump_enabled_p ())
 		    {
-		      location_t loc = gimple_location (stmt);
-		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+		      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
 				       "converting indirect call to "
 				       "function %s\n",
 				       lang_hooks.decl_printable_name (fn, 2));
diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c
index f52ca0d..ea648f2 100644
--- a/gcc/tree-vect-loop-manip.c
+++ b/gcc/tree-vect-loop-manip.c
@@ -1301,7 +1301,7 @@ create_lcssa_for_virtual_phi (struct loop *loop)
    location is calculated.
    Return the loop location if succeed and NULL if not.  */
 
-source_location
+dump_user_location_t
 find_loop_location (struct loop *loop)
 {
   gimple *stmt = NULL;
@@ -1309,19 +1309,19 @@ find_loop_location (struct loop *loop)
   gimple_stmt_iterator si;
 
   if (!loop)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   stmt = get_loop_exit_condition (loop);
 
   if (stmt
       && LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-    return gimple_location (stmt);
+    return stmt;
 
   /* If we got here the loop is probably not "well formed",
      try to estimate the loop location */
 
   if (!loop->header)
-    return UNKNOWN_LOCATION;
+    return dump_user_location_t ();
 
   bb = loop->header;
 
@@ -1329,10 +1329,10 @@ find_loop_location (struct loop *loop)
     {
       stmt = gsi_stmt (si);
       if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
-        return gimple_location (stmt);
+        return stmt;
     }
 
-  return UNKNOWN_LOCATION;
+  return dump_user_location_t ();
 }
 
 /* Return true if PHI defines an IV of the loop to be vectorized.  */
@@ -2498,7 +2498,7 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1,
 	}
     }
 
-  source_location loop_loc = find_loop_location (loop);
+  dump_user_location_t loop_loc = find_loop_location (loop);
   struct loop *scalar_loop = LOOP_VINFO_SCALAR_LOOP (loop_vinfo);
   if (prolog_peeling)
     {
@@ -3072,7 +3072,7 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
       loop_constraint_set (loop, LOOP_C_INFINITE);
     }
 
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
       && dump_enabled_p ())
     {
       if (version_alias)
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 2b3ced2..b9ccae7 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -2731,8 +2731,8 @@ needs_fold_left_reduction_p (tree type, tree_code code,
    reduction operation CODE has a handled computation expression.  */
 
 bool
-check_reduction_path (location_t loc, loop_p loop, gphi *phi, tree loop_arg,
-		      enum tree_code code)
+check_reduction_path (dump_user_location_t loc, loop_p loop, gphi *phi,
+		      tree loop_arg, enum tree_code code)
 {
   auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
   auto_bitmap visited;
@@ -3750,8 +3750,8 @@ vect_estimate_min_profitable_iters (loop_vec_info loop_vinfo,
   else
     {
       if (LOOP_VINFO_LOOP (loop_vinfo)->force_vectorize)
-	warning_at (vect_location, OPT_Wopenmp_simd, "vectorization "
-		    "did not happen for a simd loop");
+	warning_at (vect_location.get_location_t (), OPT_Wopenmp_simd,
+		    "vectorization did not happen for a simd loop");
 
       if (dump_enabled_p ())
         dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 6739ade..31b58db 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -1510,7 +1510,8 @@ fail:
 /* Dump a slp tree NODE using flags specified in DUMP_KIND.  */
 
 static void
-vect_print_slp_tree (dump_flags_t dump_kind, location_t loc, slp_tree node)
+vect_print_slp_tree (dump_flags_t dump_kind, dump_location_t loc,
+		     slp_tree node)
 {
   int i;
   gimple *stmt;
@@ -3003,7 +3004,7 @@ vect_slp_bb (basic_block bb)
 	  insns++;
 
 	  if (gimple_location (stmt) != UNKNOWN_LOCATION)
-	    vect_location = gimple_location (stmt);
+	    vect_location = stmt;
 
 	  if (!vect_find_stmt_data_reference (NULL, stmt, &datarefs))
 	    break;
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 504a000..971221c 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -81,8 +81,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 
 
-/* Loop or bb location.  */
-source_location vect_location;
+/* Loop or bb location, with hotness information.  */
+dump_user_location_t vect_location;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> *stmt_vec_info_vec;
@@ -696,11 +696,11 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
   unsigned ret = 0;
   vec_info_shared shared;
   vect_location = find_loop_location (loop);
-  if (LOCATION_LOCUS (vect_location) != UNKNOWN_LOCATION
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
       && dump_enabled_p ())
     dump_printf (MSG_NOTE, "\nAnalyzing loop at %s:%d\n",
-		 LOCATION_FILE (vect_location),
-		 LOCATION_LINE (vect_location));
+		 LOCATION_FILE (vect_location.get_location_t ()),
+		 LOCATION_LINE (vect_location.get_location_t ()));
 
   loop_vec_info loop_vinfo = vect_analyze_loop (loop, orig_loop_vinfo, &shared);
   loop->aux = loop_vinfo;
@@ -917,7 +917,7 @@ vectorize_loops (void)
       ret |= try_vectorize_loop (simduid_to_vf_htab, &num_vectorized_loops,
 				 loop);
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
 
   statistics_counter_event (cfun, "Vectorized loops", num_vectorized_loops);
   if (dump_enabled_p ()
@@ -1249,7 +1249,7 @@ increase_alignment (void)
 {
   varpool_node *vnode;
 
-  vect_location = UNKNOWN_LOCATION;
+  vect_location = dump_user_location_t ();
   type_align_map = new hash_map<tree, unsigned>;
 
   /* Increase the alignment of all global arrays for vectorization.  */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 55f8e6e..94a0f38 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1437,8 +1437,8 @@ vect_get_scalar_dr_size (struct data_reference *dr)
   return tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr))));
 }
 
-/* Source location */
-extern source_location vect_location;
+/* Source location + hotness information. */
+extern dump_user_location_t vect_location;
 
 /* If dumping is enabled, emit a MSG_NOTE at vect_location about
    entering MSG within the vectorizer.  MSG should be a string literal. */
@@ -1466,7 +1466,7 @@ extern void vect_loop_versioning (loop_vec_info, unsigned int, bool,
 extern struct loop *vect_do_peeling (loop_vec_info, tree, tree,
 				     tree *, tree *, tree *, int, bool, bool);
 extern void vect_prepare_for_masked_peels (loop_vec_info);
-extern source_location find_loop_location (struct loop *);
+extern dump_user_location_t find_loop_location (struct loop *);
 extern bool vect_can_advance_ivs_p (loop_vec_info);
 
 /* In tree-vect-stmts.c.  */
@@ -1582,7 +1582,7 @@ extern tree vect_create_addr_base_for_vector_ref (gimple *, gimple_seq *,
 extern gimple *vect_force_simple_reduction (loop_vec_info, gimple *,
 					    bool *, bool);
 /* Used in gimple-loop-interchange.c.  */
-extern bool check_reduction_path (location_t, loop_p, gphi *, tree,
+extern bool check_reduction_path (dump_user_location_t, loop_p, gphi *, tree,
 				  enum tree_code);
 /* Drive for loop analysis stage.  */
 extern loop_vec_info vect_analyze_loop (struct loop *, loop_vec_info,
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index d50a179..77d4849 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -585,10 +585,11 @@ check_counter (gimple *stmt, const char * name,
   gcov_type bb_count = bb_count_d.ipa ().to_gcov_type ();
   if (*all != bb_count || *count > *all)
     {
-      location_t locus;
-      locus = (stmt != NULL)
-              ? gimple_location (stmt)
-              : DECL_SOURCE_LOCATION (current_function_decl);
+      dump_user_location_t locus;
+      locus = ((stmt != NULL)
+	       ? dump_user_location_t (stmt)
+	       : dump_user_location_t::from_function_decl
+		   (current_function_decl));
       if (flag_profile_correction)
         {
           if (dump_enabled_p ())
@@ -603,7 +604,7 @@ check_counter (gimple *stmt, const char * name,
 	}
       else
 	{
-	  error_at (locus, "corrupted value profile: %s "
+	  error_at (locus.get_location_t (), "corrupted value profile: %s "
 		    "profile counter (%d out of %d) inconsistent with "
 		    "basic-block count (%d)",
 		    name,
@@ -1271,13 +1272,11 @@ find_func_by_profile_id (int profile_id)
 bool
 check_ic_target (gcall *call_stmt, struct cgraph_node *target)
 {
-   location_t locus;
    if (gimple_check_call_matching_types (call_stmt, target->decl, true))
      return true;
 
-   locus =  gimple_location (call_stmt);
    if (dump_enabled_p ())
-     dump_printf_loc (MSG_MISSED_OPTIMIZATION, locus,
+     dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
                       "Skipping target %s with mismatching types for icall\n",
                       target->name ());
    return false;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-06-25 13:35               ` Richard Biener
  2018-06-26 13:54                 ` [committed] Introduce dump_location_t David Malcolm
@ 2018-06-26 15:43                 ` David Malcolm
  2018-06-29  8:13                   ` Richard Biener
  2018-06-26 20:27                 ` [PATCH] Hide alt_dump_file within dumpfile.c David Malcolm
  2 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-26 15:43 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

This patch adds a concept of nested "scopes" to dumpfile.c's dump_*_loc
calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-vectorizer.h,
so that the nested structure is shown in -fopt-info by indentation.

For example, this converts -fopt-info-all e.g. from:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note: === analyze_loop_nest ===
test.c:8:3: note: === vect_analyze_loop_form ===
test.c:8:3: note: === get_loop_niters ===
test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

to:

test.c:8:3: note: === analyzing loop ===
test.c:8:3: note:  === analyze_loop_nest ===
test.c:8:3: note:   === vect_analyze_loop_form ===
test.c:8:3: note:    === get_loop_niters ===
test.c:8:3: note:   symbolic number of iterations is (unsigned int) n_9(D)
test.c:8:3: note:   not vectorized: loop contains function calls or data references that cannot be analyzed
test.c:8:3: note: vectorized 0 loops in function

showing that the "symbolic number of iterations" message is within
the "=== analyze_loop_nest ===" (and not within the
"=== vect_analyze_loop_form ===").

This is also enabling work for followups involving optimization records
(allowing the records to directly capture the nested structure of the
dump messages).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	* dumpfile.c (dump_loc): Add indentation based on scope depth.
	(dump_scope_depth): New variable.
	(get_dump_scope_depth): New function.
	(dump_begin_scope): New function.
	(dump_end_scope): New function.
	* dumpfile.h (get_dump_scope_depth): New declaration.
	(dump_begin_scope): New declaration.
	(dump_end_scope): New declaration.
	(class auto_dump_scope): New class.
	(AUTO_DUMP_SCOPE): New macro.
	* tree-vectorizer.h (DUMP_VECT_SCOPE): Reimplement in terms of
	AUTO_DUMP_SCOPE.
---
 gcc/dumpfile.c        | 35 +++++++++++++++++++++++++++++++++++
 gcc/dumpfile.h        | 39 +++++++++++++++++++++++++++++++++++++++
 gcc/tree-vectorizer.h | 15 ++++++++-------
 3 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 122e420..190b52d 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -419,6 +419,8 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
                  DECL_SOURCE_FILE (current_function_decl),
                  DECL_SOURCE_LINE (current_function_decl),
                  DECL_SOURCE_COLUMN (current_function_decl));
+      /* Indentation based on scope depth.  */
+      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
     }
 }
 
@@ -539,6 +541,39 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
+/* The current dump scope-nesting depth.  */
+
+static int dump_scope_depth;
+
+/* Get the current dump scope-nesting depth.
+   For use by dump_*_loc (for showing nesting via indentation).  */
+
+unsigned int
+get_dump_scope_depth ()
+{
+  return dump_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Increment the scope depth.  */
+
+void
+dump_begin_scope (const char *name, const dump_location_t &loc)
+{
+  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
+  dump_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_end_scope ()
+{
+  dump_scope_depth--;
+}
+
 /* Start a dump for PHASE. Store user-supplied dump flags in
    *FLAG_PTR.  Return the number of streams opened.  Set globals
    DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 90d8930..89d5c11 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -456,6 +456,45 @@ dump_enabled_p (void)
   return (dump_file || alt_dump_file);
 }
 
+/* Managing nested scopes, so that dumps can express the call chain
+   leading to a dump message.  */
+
+extern unsigned int get_dump_scope_depth ();
+extern void dump_begin_scope (const char *name, const dump_location_t &loc);
+extern void dump_end_scope ();
+
+/* Implementation detail of the AUTO_DUMP_SCOPE macro below.
+
+   A RAII-style class intended to make it easy to emit dump
+   information about entering and exiting a collection of nested
+   function calls.  */
+
+class auto_dump_scope
+{
+ public:
+  auto_dump_scope (const char *name, dump_location_t loc)
+  {
+    if (dump_enabled_p ())
+      dump_begin_scope (name, loc);
+  }
+  ~auto_dump_scope ()
+  {
+    if (dump_enabled_p ())
+      dump_end_scope ();
+  }
+};
+
+/* A macro for calling:
+     dump_begin_scope (NAME, LOC);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
+
+#define AUTO_DUMP_SCOPE(NAME, LOC) \
+  auto_dump_scope scope (NAME, LOC)
+
 namespace gcc {
 
 class dump_manager
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 94a0f38..a8406b3 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1440,15 +1440,16 @@ vect_get_scalar_dr_size (struct data_reference *dr)
 /* Source location + hotness information. */
 extern dump_user_location_t vect_location;
 
-/* If dumping is enabled, emit a MSG_NOTE at vect_location about
-   entering MSG within the vectorizer.  MSG should be a string literal. */
+/* A macro for calling:
+     dump_begin_scope (MSG, vect_location);
+   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
+   and then calling
+     dump_end_scope ();
+   once the object goes out of scope, thus capturing the nesting of
+   the scopes.  */
 
 #define DUMP_VECT_SCOPE(MSG) \
-  do {						\
-    if (dump_enabled_p ())			\
-      dump_printf_loc (MSG_NOTE, vect_location, \
-		       "=== " MSG " ===\n");	\
-  } while (0)
+  AUTO_DUMP_SCOPE (MSG, vect_location)
 
 /*-----------------------------------------------------------------*/
 /* Function prototypes.                                            */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH] Hide alt_dump_file within dumpfile.c
  2018-06-25 13:35               ` Richard Biener
  2018-06-26 13:54                 ` [committed] Introduce dump_location_t David Malcolm
  2018-06-26 15:43                 ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE David Malcolm
@ 2018-06-26 20:27                 ` David Malcolm
  2018-06-28  9:50                   ` Richard Biener
  2 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-26 20:27 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > Here's v3 of the patch (one big patch this time, rather than a
> > kit).
> > 
> > Like the v2 patch kit, this patch reuses the existing dump API,
> > rather than inventing its own.
> > 
> > Specifically, it uses the dump_* functions in dumpfile.h that don't
> > take a FILE *, the ones that implicitly write to dump_file and/or
> > alt_dump_file.  I needed a name for them, so I've taken to calling
> > them the "structured dump API" (better name ideas welcome).
> > 
> > v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
> > calls as delimiters when consolidating "dump_*" calls.  There's a
> > new dump_context class which has responsibility for consolidating
> > them into optimization records.
> > 
> > The dump_*_loc calls now capture more than just a location_t: they
> > capture the profile_count and the location in GCC's own sources
> > where
> > the dump is being emitted from.
> > 
> > This works by introducing a new "dump_location_t" class as the
> > argument of those dump_*_loc calls.  The dump_location_t can
> > be constructed from a gimple * or from an rtx_insn *, so that
> > rather than writing:
> > 
> >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> >                    "some message: %i", 42);
> > 
> > you can write:
> > 
> >   dump_printf_loc (MSG_NOTE, stmt,
> >                    "some message: %i", 42);
> > 
> > and the dump_location_t constructor will grab the location_t and
> > profile_count of stmt, and the location of the "dump_printf_loc"
> > callsite (and gracefully handle "stmt" being NULL).
> > 
> > Earlier versions of the patch captured the location of the
> > dump_*_loc call via preprocessor hacks, or didn't work properly;
> > this version of the patch works more cleanly: internally,
> > dump_location_t is split into two new classes:
> >   * dump_user_location_t: the location_t and profile_count within
> >     the *user's code*, and
> >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
> >     the *implementation* code (i.e. GCC or a plugin), captured
> >     "automagically" via default params
> > 
> > These classes are sometimes used elsewhere in the code.  For
> > example, "vect_location" becomes a dump_user_location_t
> > (location_t and profile_count), so that in e.g:
> > 
> >   vect_location = find_loop_location (loop);
> > 
> > it's capturing the location_t and profile_count, and then when
> > it's used here:
> > 
> >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > 
> > the dump_location_t is constructed from the vect_location
> > plus the dump_impl_location_t at that callsite.
> > 
> > In contrast, loop-unroll.c's report_unroll's "locus" param
> > becomes a dump_location_t: we're interested in where it was
> > called from, not in the locations of the various dump_*_loc calls
> > within it.
> > 
> > Previous versions of the patch captured a gimple *, and needed
> > GTY markers; in this patch, the dump_user_location_t is now just a
> > location_t and a profile_count.
> > 
> > The v2 patch added an overload for dump_printf_loc so that you
> > could pass in either a location_t, or the new type; this version
> > of the patch eliminates that: they all now take dump_location_t.
> > 
> > Doing so required adding support for rtx_insn *, so that one can
> > write this kind of thing in RTL passes:
> > 
> >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > 
> > One knock-on effect is that get_loop_location now returns a
> > dump_user_location_t rather than a location_t, so that it has
> > hotness information.
> > 
> > Richi: would you like me to split out this location-handling
> > code into a separate patch?  (It's kind of redundant without
> > adding the remarks and optimization records work, but if that's
> > easier I can do it)
> 
> I think that would be easier because it doesn't require the JSON
> stuff and so I'll happily approve it.
> 
> Thus - trying to review that bits (and sorry for the delay).
> 
> +  location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      dump_loc (dump_kind, dump_file, loc);
> +      dump_loc (dump_kind, dump_file, srcloc);
>        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> 
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
> 
> seeing this in multiple places.  I seem to remember that
> dump_file / alt_dump_file was suposed to handle dumping
> into two locations - a dump file and optinfo (or stdout).  This looks
> like the optinfo "stream" is even more separate.  Could that
> obsolete the alt_dump_file stream?  I'd need to review existing stuff
> in more detail to answer but maybe you already know from recently
> digging into this.

[...snip...]

Although I haven't yet obsoleted the alt_dump_file stream via the
optinfo "stream", I think it's possible.  The following patch would be
necessary (and it helps with the rest of the optinfo work).

This patch removes alt_dump_file from dumpfile.h, making it static
within dumpfile.c.  This allows for changing how -fopt-info is
implemented, and potentially adding other kinds of dump target, such
as remarks or optimization records.

Doing so requires changing the implementation of dump_enabled_p, so
the patch changes this to a simple lookup of a boolean global, which
is updated any time dump_file or alt_dump_file change.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

Thanks
Dave

gcc/ChangeLog:
	* cgraph.c (cgraph_node::get_body): Replace assignments to
	"dump_file" with calls to set_dump_file.
	* dumpfile.c (alt_dump_file): Make static, and group with...
	(alt_flags): ...this definition.
	(dumps_are_enabled): New variable.
	(refresh_dumps_are_enabled): New function.
	(set_dump_file): New function.
	(set_alt_dump_file): New function.
	(gcc::dump_manager::dump_start): Replace assignments to
	"dump_file" and "alt_dump_file" with calls to set_dump_file and
	set_alt_dump_file.
	(gcc::dump_manager::dump_finish): Likewise.
	* dumpfile.h (alt_dump_file): Delete decl.
	(dumps_are_enabled): New variable decl.
	(set_dump_file): New function decl.
	(dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
	global.
	* tree-nested.c (lower_nested_functions): Replace assignments to
	"dump_file" with calls to set_dump_file.
---
 gcc/cgraph.c      |  4 ++--
 gcc/dumpfile.c    | 46 ++++++++++++++++++++++++++++++++++++++++------
 gcc/dumpfile.h    |  7 +++++--
 gcc/tree-nested.c |  4 ++--
 4 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 3899467..d19f1aa 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
       const char *saved_dump_file_name = dump_file_name;
       dump_flags_t saved_dump_flags = dump_flags;
       dump_file_name = NULL;
-      dump_file = NULL;
+      set_dump_file (NULL);
 
       push_cfun (DECL_STRUCT_FUNCTION (decl));
       execute_all_ipa_transforms ();
@@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
       updated = true;
 
       current_pass = saved_current_pass;
-      dump_file = saved_dump_file;
+      set_dump_file (saved_dump_file);
       dump_file_name = saved_dump_file_name;
       dump_flags = saved_dump_flags;
     }
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 190b52d..458d1d7 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -40,18 +40,52 @@ along with GCC; see the file COPYING3.  If not see
    (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
 
 static dump_flags_t pflags;		      /* current dump_flags */
-static dump_flags_t alt_flags;		      /* current opt_info flags */
 
 static void dump_loc (dump_flags_t, FILE *, source_location);
+
+/* Current -fopt-info output stream, if any, and flags.  */
+static FILE *alt_dump_file = NULL;
+static dump_flags_t alt_flags;
+
 static FILE *dump_open_alternate_stream (struct dump_file_info *);
 
 /* These are currently used for communicating between passes.
    However, instead of accessing them directly, the passes can use
    dump_printf () for dumps.  */
 FILE *dump_file = NULL;
-FILE *alt_dump_file = NULL;
 const char *dump_file_name;
 dump_flags_t dump_flags;
+bool dumps_are_enabled = false;
+
+
+/* Update the "dumps_are_enabled" global; to be called whenever dump_file
+   or alt_dump_file change.  */
+
+static void
+refresh_dumps_are_enabled ()
+{
+  dumps_are_enabled = (dump_file || alt_dump_file);
+}
+
+/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+void
+set_dump_file (FILE *new_dump_file)
+{
+  dump_file = new_dump_file;
+  refresh_dumps_are_enabled ();
+}
+
+/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
+   global.  */
+
+static void
+set_alt_dump_file (FILE *new_alt_dump_file)
+{
+  alt_dump_file = new_alt_dump_file;
+  refresh_dumps_are_enabled ();
+}
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
   {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
@@ -603,7 +637,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
         }
       free (name);
       dfi->pstream = stream;
-      dump_file = dfi->pstream;
+      set_dump_file (dfi->pstream);
       /* Initialize current dump flags. */
       pflags = dfi->pflags;
     }
@@ -613,7 +647,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
     {
       dfi->alt_stream = stream;
       count++;
-      alt_dump_file = dfi->alt_stream;
+      set_alt_dump_file (dfi->alt_stream);
       /* Initialize current -fopt-info flags. */
       alt_flags = dfi->alt_flags;
     }
@@ -644,8 +678,8 @@ dump_finish (int phase)
 
   dfi->alt_stream = NULL;
   dfi->pstream = NULL;
-  dump_file = NULL;
-  alt_dump_file = NULL;
+  set_dump_file (NULL);
+  set_alt_dump_file (NULL);
   dump_flags = TDF_NONE;
   alt_flags = TDF_NONE;
   pflags = TDF_NONE;
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 89d5c11..9828a3f 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -445,15 +445,18 @@ extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
 
 /* Global variables used to communicate with passes.  */
 extern FILE *dump_file;
-extern FILE *alt_dump_file;
 extern dump_flags_t dump_flags;
 extern const char *dump_file_name;
 
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
+
 /* Return true if any of the dumps is enabled, false otherwise. */
 static inline bool
 dump_enabled_p (void)
 {
-  return (dump_file || alt_dump_file);
+  return dumps_are_enabled;
 }
 
 /* Managing nested scopes, so that dumps can express the call chain
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index 127a81f..4c8eda9 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -3399,7 +3399,7 @@ lower_nested_functions (tree fndecl)
 
   gimplify_all_functions (cgn);
 
-  dump_file = dump_begin (TDI_nested, &dump_flags);
+  set_dump_file (dump_begin (TDI_nested, &dump_flags));
   if (dump_file)
     fprintf (dump_file, "\n;; Function %s\n\n",
 	     lang_hooks.decl_printable_name (fndecl, 2));
@@ -3426,7 +3426,7 @@ lower_nested_functions (tree fndecl)
   if (dump_file)
     {
       dump_end (TDI_nested, dump_file);
-      dump_file = NULL;
+      set_dump_file (NULL);
     }
 }
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] Hide alt_dump_file within dumpfile.c
  2018-06-26 20:27                 ` [PATCH] Hide alt_dump_file within dumpfile.c David Malcolm
@ 2018-06-28  9:50                   ` Richard Biener
  0 siblings, 0 replies; 80+ messages in thread
From: Richard Biener @ 2018-06-28  9:50 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jun 26, 2018 at 10:27 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> > On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > Here's v3 of the patch (one big patch this time, rather than a
> > > kit).
> > >
> > > Like the v2 patch kit, this patch reuses the existing dump API,
> > > rather than inventing its own.
> > >
> > > Specifically, it uses the dump_* functions in dumpfile.h that don't
> > > take a FILE *, the ones that implicitly write to dump_file and/or
> > > alt_dump_file.  I needed a name for them, so I've taken to calling
> > > them the "structured dump API" (better name ideas welcome).
> > >
> > > v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
> > > calls as delimiters when consolidating "dump_*" calls.  There's a
> > > new dump_context class which has responsibility for consolidating
> > > them into optimization records.
> > >
> > > The dump_*_loc calls now capture more than just a location_t: they
> > > capture the profile_count and the location in GCC's own sources
> > > where
> > > the dump is being emitted from.
> > >
> > > This works by introducing a new "dump_location_t" class as the
> > > argument of those dump_*_loc calls.  The dump_location_t can
> > > be constructed from a gimple * or from an rtx_insn *, so that
> > > rather than writing:
> > >
> > >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> > >                    "some message: %i", 42);
> > >
> > > you can write:
> > >
> > >   dump_printf_loc (MSG_NOTE, stmt,
> > >                    "some message: %i", 42);
> > >
> > > and the dump_location_t constructor will grab the location_t and
> > > profile_count of stmt, and the location of the "dump_printf_loc"
> > > callsite (and gracefully handle "stmt" being NULL).
> > >
> > > Earlier versions of the patch captured the location of the
> > > dump_*_loc call via preprocessor hacks, or didn't work properly;
> > > this version of the patch works more cleanly: internally,
> > > dump_location_t is split into two new classes:
> > >   * dump_user_location_t: the location_t and profile_count within
> > >     the *user's code*, and
> > >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
> > >     the *implementation* code (i.e. GCC or a plugin), captured
> > >     "automagically" via default params
> > >
> > > These classes are sometimes used elsewhere in the code.  For
> > > example, "vect_location" becomes a dump_user_location_t
> > > (location_t and profile_count), so that in e.g:
> > >
> > >   vect_location = find_loop_location (loop);
> > >
> > > it's capturing the location_t and profile_count, and then when
> > > it's used here:
> > >
> > >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > >
> > > the dump_location_t is constructed from the vect_location
> > > plus the dump_impl_location_t at that callsite.
> > >
> > > In contrast, loop-unroll.c's report_unroll's "locus" param
> > > becomes a dump_location_t: we're interested in where it was
> > > called from, not in the locations of the various dump_*_loc calls
> > > within it.
> > >
> > > Previous versions of the patch captured a gimple *, and needed
> > > GTY markers; in this patch, the dump_user_location_t is now just a
> > > location_t and a profile_count.
> > >
> > > The v2 patch added an overload for dump_printf_loc so that you
> > > could pass in either a location_t, or the new type; this version
> > > of the patch eliminates that: they all now take dump_location_t.
> > >
> > > Doing so required adding support for rtx_insn *, so that one can
> > > write this kind of thing in RTL passes:
> > >
> > >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > >
> > > One knock-on effect is that get_loop_location now returns a
> > > dump_user_location_t rather than a location_t, so that it has
> > > hotness information.
> > >
> > > Richi: would you like me to split out this location-handling
> > > code into a separate patch?  (It's kind of redundant without
> > > adding the remarks and optimization records work, but if that's
> > > easier I can do it)
> >
> > I think that would be easier because it doesn't require the JSON
> > stuff and so I'll happily approve it.
> >
> > Thus - trying to review that bits (and sorry for the delay).
> >
> > +  location_t srcloc = loc.get_location_t ();
> > +
> >    if (dump_file && (dump_kind & pflags))
> >      {
> > -      dump_loc (dump_kind, dump_file, loc);
> > +      dump_loc (dump_kind, dump_file, srcloc);
> >        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> >      }
> >
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      {
> > -      dump_loc (dump_kind, alt_dump_file, loc);
> > +      dump_loc (dump_kind, alt_dump_file, srcloc);
> >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> >      }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_stmt (gs, extra_dump_flags);
> > +    }
> >
> > seeing this in multiple places.  I seem to remember that
> > dump_file / alt_dump_file was suposed to handle dumping
> > into two locations - a dump file and optinfo (or stdout).  This looks
> > like the optinfo "stream" is even more separate.  Could that
> > obsolete the alt_dump_file stream?  I'd need to review existing stuff
> > in more detail to answer but maybe you already know from recently
> > digging into this.
>
> [...snip...]
>
> Although I haven't yet obsoleted the alt_dump_file stream via the
> optinfo "stream", I think it's possible.  The following patch would be
> necessary (and it helps with the rest of the optinfo work).
>
> This patch removes alt_dump_file from dumpfile.h, making it static
> within dumpfile.c.  This allows for changing how -fopt-info is
> implemented, and potentially adding other kinds of dump target, such
> as remarks or optimization records.
>
> Doing so requires changing the implementation of dump_enabled_p, so
> the patch changes this to a simple lookup of a boolean global, which
> is updated any time dump_file or alt_dump_file change.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

OK.

Thanks,
Richard.

> Thanks
> Dave
>
> gcc/ChangeLog:
>         * cgraph.c (cgraph_node::get_body): Replace assignments to
>         "dump_file" with calls to set_dump_file.
>         * dumpfile.c (alt_dump_file): Make static, and group with...
>         (alt_flags): ...this definition.
>         (dumps_are_enabled): New variable.
>         (refresh_dumps_are_enabled): New function.
>         (set_dump_file): New function.
>         (set_alt_dump_file): New function.
>         (gcc::dump_manager::dump_start): Replace assignments to
>         "dump_file" and "alt_dump_file" with calls to set_dump_file and
>         set_alt_dump_file.
>         (gcc::dump_manager::dump_finish): Likewise.
>         * dumpfile.h (alt_dump_file): Delete decl.
>         (dumps_are_enabled): New variable decl.
>         (set_dump_file): New function decl.
>         (dump_enabled_p): Rewrite in terms of new "dumps_are_enabled"
>         global.
>         * tree-nested.c (lower_nested_functions): Replace assignments to
>         "dump_file" with calls to set_dump_file.
> ---
>  gcc/cgraph.c      |  4 ++--
>  gcc/dumpfile.c    | 46 ++++++++++++++++++++++++++++++++++++++++------
>  gcc/dumpfile.h    |  7 +++++--
>  gcc/tree-nested.c |  4 ++--
>  4 files changed, 49 insertions(+), 12 deletions(-)
>
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 3899467..d19f1aa 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -3582,7 +3582,7 @@ cgraph_node::get_body (void)
>        const char *saved_dump_file_name = dump_file_name;
>        dump_flags_t saved_dump_flags = dump_flags;
>        dump_file_name = NULL;
> -      dump_file = NULL;
> +      set_dump_file (NULL);
>
>        push_cfun (DECL_STRUCT_FUNCTION (decl));
>        execute_all_ipa_transforms ();
> @@ -3593,7 +3593,7 @@ cgraph_node::get_body (void)
>        updated = true;
>
>        current_pass = saved_current_pass;
> -      dump_file = saved_dump_file;
> +      set_dump_file (saved_dump_file);
>        dump_file_name = saved_dump_file_name;
>        dump_flags = saved_dump_flags;
>      }
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 190b52d..458d1d7 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -40,18 +40,52 @@ along with GCC; see the file COPYING3.  If not see
>     (strncmp (whole, part, strlen (part)) ? NULL : whole + strlen (part))
>
>  static dump_flags_t pflags;                  /* current dump_flags */
> -static dump_flags_t alt_flags;               /* current opt_info flags */
>
>  static void dump_loc (dump_flags_t, FILE *, source_location);
> +
> +/* Current -fopt-info output stream, if any, and flags.  */
> +static FILE *alt_dump_file = NULL;
> +static dump_flags_t alt_flags;
> +
>  static FILE *dump_open_alternate_stream (struct dump_file_info *);
>
>  /* These are currently used for communicating between passes.
>     However, instead of accessing them directly, the passes can use
>     dump_printf () for dumps.  */
>  FILE *dump_file = NULL;
> -FILE *alt_dump_file = NULL;
>  const char *dump_file_name;
>  dump_flags_t dump_flags;
> +bool dumps_are_enabled = false;
> +
> +
> +/* Update the "dumps_are_enabled" global; to be called whenever dump_file
> +   or alt_dump_file change.  */
> +
> +static void
> +refresh_dumps_are_enabled ()
> +{
> +  dumps_are_enabled = (dump_file || alt_dump_file);
> +}
> +
> +/* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
> +   global.  */
> +
> +void
> +set_dump_file (FILE *new_dump_file)
> +{
> +  dump_file = new_dump_file;
> +  refresh_dumps_are_enabled ();
> +}
> +
> +/* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
> +   global.  */
> +
> +static void
> +set_alt_dump_file (FILE *new_alt_dump_file)
> +{
> +  alt_dump_file = new_alt_dump_file;
> +  refresh_dumps_are_enabled ();
> +}
>
>  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
>    {suffix, swtch, NULL, NULL, NULL, NULL, NULL, dkind, TDF_NONE, TDF_NONE, \
> @@ -603,7 +637,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
>          }
>        free (name);
>        dfi->pstream = stream;
> -      dump_file = dfi->pstream;
> +      set_dump_file (dfi->pstream);
>        /* Initialize current dump flags. */
>        pflags = dfi->pflags;
>      }
> @@ -613,7 +647,7 @@ dump_start (int phase, dump_flags_t *flag_ptr)
>      {
>        dfi->alt_stream = stream;
>        count++;
> -      alt_dump_file = dfi->alt_stream;
> +      set_alt_dump_file (dfi->alt_stream);
>        /* Initialize current -fopt-info flags. */
>        alt_flags = dfi->alt_flags;
>      }
> @@ -644,8 +678,8 @@ dump_finish (int phase)
>
>    dfi->alt_stream = NULL;
>    dfi->pstream = NULL;
> -  dump_file = NULL;
> -  alt_dump_file = NULL;
> +  set_dump_file (NULL);
> +  set_alt_dump_file (NULL);
>    dump_flags = TDF_NONE;
>    alt_flags = TDF_NONE;
>    pflags = TDF_NONE;
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 89d5c11..9828a3f 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -445,15 +445,18 @@ extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
>
>  /* Global variables used to communicate with passes.  */
>  extern FILE *dump_file;
> -extern FILE *alt_dump_file;
>  extern dump_flags_t dump_flags;
>  extern const char *dump_file_name;
>
> +extern bool dumps_are_enabled;
> +
> +extern void set_dump_file (FILE *new_dump_file);
> +
>  /* Return true if any of the dumps is enabled, false otherwise. */
>  static inline bool
>  dump_enabled_p (void)
>  {
> -  return (dump_file || alt_dump_file);
> +  return dumps_are_enabled;
>  }
>
>  /* Managing nested scopes, so that dumps can express the call chain
> diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
> index 127a81f..4c8eda9 100644
> --- a/gcc/tree-nested.c
> +++ b/gcc/tree-nested.c
> @@ -3399,7 +3399,7 @@ lower_nested_functions (tree fndecl)
>
>    gimplify_all_functions (cgn);
>
> -  dump_file = dump_begin (TDI_nested, &dump_flags);
> +  set_dump_file (dump_begin (TDI_nested, &dump_flags));
>    if (dump_file)
>      fprintf (dump_file, "\n;; Function %s\n\n",
>              lang_hooks.decl_printable_name (fndecl, 2));
> @@ -3426,7 +3426,7 @@ lower_nested_functions (tree fndecl)
>    if (dump_file)
>      {
>        dump_end (TDI_nested, dump_file);
> -      dump_file = NULL;
> +      set_dump_file (NULL);
>      }
>  }
>
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [committed] Introduce dump_location_t
  2018-06-26 13:54                 ` [committed] Introduce dump_location_t David Malcolm
@ 2018-06-28 11:29                   ` Richard Biener
  2018-06-28 14:29                     ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-28 11:29 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jun 26, 2018 at 3:54 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> > On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > Here's v3 of the patch (one big patch this time, rather than a
> > > kit).
> > >
> > > Like the v2 patch kit, this patch reuses the existing dump API,
> > > rather than inventing its own.
> > >
> > > Specifically, it uses the dump_* functions in dumpfile.h that don't
> > > take a FILE *, the ones that implicitly write to dump_file and/or
> > > alt_dump_file.  I needed a name for them, so I've taken to calling
> > > them the "structured dump API" (better name ideas welcome).
> > >
> > > v3 eliminates v2's optinfo_guard class, instead using "dump_*_loc"
> > > calls as delimiters when consolidating "dump_*" calls.  There's a
> > > new dump_context class which has responsibility for consolidating
> > > them into optimization records.
> > >
> > > The dump_*_loc calls now capture more than just a location_t: they
> > > capture the profile_count and the location in GCC's own sources
> > > where
> > > the dump is being emitted from.
> > >
> > > This works by introducing a new "dump_location_t" class as the
> > > argument of those dump_*_loc calls.  The dump_location_t can
> > > be constructed from a gimple * or from an rtx_insn *, so that
> > > rather than writing:
> > >
> > >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> > >                    "some message: %i", 42);
> > >
> > > you can write:
> > >
> > >   dump_printf_loc (MSG_NOTE, stmt,
> > >                    "some message: %i", 42);
> > >
> > > and the dump_location_t constructor will grab the location_t and
> > > profile_count of stmt, and the location of the "dump_printf_loc"
> > > callsite (and gracefully handle "stmt" being NULL).
> > >
> > > Earlier versions of the patch captured the location of the
> > > dump_*_loc call via preprocessor hacks, or didn't work properly;
> > > this version of the patch works more cleanly: internally,
> > > dump_location_t is split into two new classes:
> > >   * dump_user_location_t: the location_t and profile_count within
> > >     the *user's code*, and
> > >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION within
> > >     the *implementation* code (i.e. GCC or a plugin), captured
> > >     "automagically" via default params
> > >
> > > These classes are sometimes used elsewhere in the code.  For
> > > example, "vect_location" becomes a dump_user_location_t
> > > (location_t and profile_count), so that in e.g:
> > >
> > >   vect_location = find_loop_location (loop);
> > >
> > > it's capturing the location_t and profile_count, and then when
> > > it's used here:
> > >
> > >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > >
> > > the dump_location_t is constructed from the vect_location
> > > plus the dump_impl_location_t at that callsite.
> > >
> > > In contrast, loop-unroll.c's report_unroll's "locus" param
> > > becomes a dump_location_t: we're interested in where it was
> > > called from, not in the locations of the various dump_*_loc calls
> > > within it.
> > >
> > > Previous versions of the patch captured a gimple *, and needed
> > > GTY markers; in this patch, the dump_user_location_t is now just a
> > > location_t and a profile_count.
> > >
> > > The v2 patch added an overload for dump_printf_loc so that you
> > > could pass in either a location_t, or the new type; this version
> > > of the patch eliminates that: they all now take dump_location_t.
> > >
> > > Doing so required adding support for rtx_insn *, so that one can
> > > write this kind of thing in RTL passes:
> > >
> > >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > >
> > > One knock-on effect is that get_loop_location now returns a
> > > dump_user_location_t rather than a location_t, so that it has
> > > hotness information.
> > >
> > > Richi: would you like me to split out this location-handling
> > > code into a separate patch?  (It's kind of redundant without
> > > adding the remarks and optimization records work, but if that's
> > > easier I can do it)
> >
> > I think that would be easier because it doesn't require the JSON
> > stuff and so I'll happily approve it.
> >
> > Thus - trying to review that bits (and sorry for the delay).
> >
> > +  location_t srcloc = loc.get_location_t ();
> > +
> >    if (dump_file && (dump_kind & pflags))
> >      {
> > -      dump_loc (dump_kind, dump_file, loc);
> > +      dump_loc (dump_kind, dump_file, srcloc);
> >        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> >      }
> >
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      {
> > -      dump_loc (dump_kind, alt_dump_file, loc);
> > +      dump_loc (dump_kind, alt_dump_file, srcloc);
> >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> >      }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_stmt (gs, extra_dump_flags);
> > +    }
> >
> > seeing this in multiple places.  I seem to remember that
> > dump_file / alt_dump_file was suposed to handle dumping
> > into two locations - a dump file and optinfo (or stdout).  This looks
> > like the optinfo "stream" is even more separate.  Could that
> > obsolete the alt_dump_file stream?  I'd need to review existing stuff
> > in more detail to answer but maybe you already know from recently
> > digging into this.
>
> Possibly.  I attempted this in v1 of the patch, but it was mixed in with
> a bunch of other stuff.  I'll have another go at doing this.
>
> > Oh, and all the if (optinfo_enable_p ()) stuff is for the followup
> > then, right?
>
> Yes.
>
> > I like the boiler-plate changes to dump_* using stuff a lot, so the
> > infrastructure to do that (the location wrapping) and these boiler-
> > plate
> > changes are pre-approved if split out.
>
> Thanks.  I split out the location wrapping, and have committed it to
> trunk (r262149).  It's not clear to me exactly what other parts you liked,
> so I'm going to try to split out more of the non-JSON bits in the
> hope that some parts are good enough as-is, and I'll post them for review
> as followups.
>
> For reference, here's what I've committed (I added some obvious changes
> to doc/optinfo.texi).
>
> > I think the *_REMARK stuff should get attention of the respective
> > maintainers - not sure what the difference between NOTE and REMARK
> > is ;)
>
> Me neither :)  I think it might come down to "this is purely a debugging
> message for a pass maintainer" (MSG_NOTE) vs "this is a high-level thing
> that an advanced user want to see" (MSG_REMARK???).
>
> One idea that occurred to me: are we overusing dump_flags_t?  It's a
> mixture of TDF_* bitfields (used by our internal dumps) plus MSG_*
> bitfields (used with -fopt-info).  IIRC the only TDF_ bitfield that's
> ever used with the MSG_* bitfields is TDF_DETAILS.  Might it make sense
> to split out the MSG_* bitfields into a different type (dump_level_t???)
> to reinforce this split?  (and thus the dump_* entrypoints that don't take
> a FILE * would take this new type).  I'm not sure if this is a good idea
> or not.

Making it even more complicated doesn't make it easier to use it
"correctly".  So I'd rather try to simplify it.  How passes use
TDF_DETAILS vs. non-details is already highly inconsistent.  This
is why I liked the original optinfo work because it somehow made
user-interesting vs. developer-interesting with the same API
(and to the same dump-file).  Just that MSG_NOTE is exposed to
users while I think it should be developer-only...

IMHO the TDF_ stuff should control things at the stmt/tree/BB level,
thus be IL specific flags while the MSG_ stuff should control
pass-specific dumping detail.

Richard.


> > Thanks and sorry again for the repeated delays...
> > Richard.
>
> Thanks
> Dave
>
> [...snip...]
>
> gcc/ChangeLog:
>         * cfgloop.c (get_loop_location): Convert return type from
>         location_t to dump_user_location_t, replacing INSN_LOCATION lookups
>         by implicit construction from rtx_insn *, and using
>         dump_user_location_t::from_function_decl for the fallback case.
>         * cfgloop.h (get_loop_location): Convert return type from
>         location_t to dump_user_location_t.
>         * cgraphunit.c (walk_polymorphic_call_targets): Update call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         * coverage.c (get_coverage_counts): Update calls to
>         dump_printf_loc to pass in dump_location_t rather than a
>         location_t.
>         * doc/optinfo.texi (Dump types): Convert example of
>         dump_printf_loc from taking "locus" to taking "insn".  Update
>         description of the "_loc" calls to cover dump_location_t.
>         * dumpfile.c: Include "backend.h", "gimple.h", "rtl.h", and
>         "selftest.h".
>         (dump_user_location_t::dump_user_location_t): New constructors,
>         from gimple *stmt and rtx_insn *.
>         (dump_user_location_t::from_function_decl): New function.
>         (dump_loc): Make static.
>         (dump_gimple_stmt_loc): Convert param "loc" from location_t to
>         const dump_location_t &.
>         (dump_generic_expr_loc): Delete.
>         (dump_printf_loc): Convert param "loc" from location_t to
>         const dump_location_t &.
>         (selftest::test_impl_location): New function.
>         (selftest::dumpfile_c_tests): New function.
>         * dumpfile.h: Include "profile-count.h".
>         (class dump_user_location_t): New class.
>         (struct dump_impl_location_t): New struct.
>         (class dump_location_t): New class.
>         (dump_printf_loc): Convert 2nd param from source_location to
>         const dump_location_t &.
>         (dump_generic_expr_loc): Delete.
>         (dump_gimple_stmt_loc): Convert 2nd param from source_location to
>         const dump_location_t &.
>         * gimple-fold.c (fold_gimple_assign): Update call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         (gimple_fold_call): Likewise.
>         * gimple-loop-interchange.cc
>         (loop_cand::analyze_iloop_reduction_var): Update for change to
>         check_reduction_path.
>         (tree_loop_interchange::interchange): Update for change to
>         find_loop_location.
>         * graphite-isl-ast-to-gimple.c (scop_to_isl_ast): Update for
>         change in return-type of find_loop_location.
>         (graphite_regenerate_ast_isl): Likewise.
>         * graphite-optimize-isl.c (optimize_isl): Likewise.
>         * graphite.c (graphite_transform_loops): Likewise.
>         * ipa-devirt.c (ipa_devirt): Update call to dump_printf_loc to
>         pass in a dump_location_t rather than a location_t, via the
>         gimple stmt.
>         * ipa-prop.c (ipa_make_edge_direct_to_target): Likewise.
>         * ipa.c (walk_polymorphic_call_targets): Likewise.
>         * loop-unroll.c (report_unroll): Convert "locus" param from
>         location_t to dump_location_t.
>         (decide_unrolling): Update for change to get_loop_location's
>         return type.
>         * omp-grid.c (struct grid_prop): Convert field "target_loc" from
>         location_t to dump_user_location_t.
>         (grid_find_single_omp_among_assignments_1): Updates calls to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the gimple stmt.
>         (grid_parallel_clauses_gridifiable): Convert "tloc" from
>         location_t to dump_location_t.  Updates calls to dump_printf_loc
>         to pass in a dump_location_t rather than a location_t, via the
>         gimple stmt.
>         (grid_inner_loop_gridifiable_p): Likewise.
>         (grid_dist_follows_simple_pattern): Likewise.
>         (grid_gfor_follows_tiling_pattern): Likewise.
>         (grid_target_follows_gridifiable_pattern): Likewise.
>         (grid_attempt_target_gridification): Convert initialization
>         of local "grid" from memset to zero-initialization; FIXME: does
>         this require C++11?  Update call to dump_printf_loc to pass in a
>         optinfo_location rather than a location_t, via the gimple stmt.
>         * profile.c (read_profile_edge_counts): Updates call to
>         dump_printf_loc to pass in a dump_location_t rather than a
>         location_t
>         (compute_branch_probabilities): Likewise.
>         * selftest-run-tests.c (selftest::run_tests): Call
>         dumpfile_c_tests.
>         * selftest.h (dumpfile_c_tests): New decl.
>         * tree-loop-distribution.c (pass_loop_distribution::execute):
>         Update for change in return type of find_loop_location.
>         * tree-parloops.c (parallelize_loops): Likewise.
>         * tree-ssa-loop-ivcanon.c (try_unroll_loop_completely): Convert
>         "locus" from location_t to dump_user_location_t.
>         (canonicalize_loop_induction_variables): Likewise.
>         * tree-ssa-loop-ivopts.c (tree_ssa_iv_optimize_loop): Update
>         for change in return type of find_loop_location.
>         * tree-ssa-loop-niter.c (number_of_iterations_exit): Update call
>         to dump_printf_loc to pass in a dump_location_t rather than a
>         location_t, via the stmt.
>         * tree-ssa-sccvn.c (eliminate_dom_walker::before_dom_children):
>         Likewise.
>         * tree-vect-loop-manip.c (find_loop_location): Convert return
>         type from source_location to dump_user_location_t.
>         (vect_do_peeling): Update for above change.
>         (vect_loop_versioning): Update for change in type of
>         vect_location.
>         * tree-vect-loop.c (check_reduction_path): Convert "loc" param
>         from location_t to dump_user_location_t.
>         (vect_estimate_min_profitable_iters): Update for change in type
>         of vect_location.
>         * tree-vect-slp.c (vect_print_slp_tree): Convert param "loc" from
>         location_t to dump_location_t.
>         (vect_slp_bb): Update for change in type of vect_location.
>         * tree-vectorizer.c (vect_location): Convert from source_location
>         to dump_user_location_t.
>         (try_vectorize_loop_1): Update for change in vect_location's type.
>         (vectorize_loops): Likewise.
>         (increase_alignment): Likewise.
>         * tree-vectorizer.h (vect_location): Convert from source_location
>         to dump_user_location_t.
>         (find_loop_location): Convert return type from source_location to
>         dump_user_location_t.
>         (check_reduction_path): Convert 1st param from location_t to
>         dump_user_location_t.
>         * value-prof.c (check_counter): Update call to dump_printf_loc to
>         pass in a dump_user_location_t rather than a location_t; update
>         call to error_at for change in type of "locus".
>         (check_ic_target): Update call to dump_printf_loc to
>         pass in a dump_user_location_t rather than a location_t, via the
>         call_stmt.
> ---
>  gcc/cfgloop.c                    |  12 +--
>  gcc/cfgloop.h                    |   2 +-
>  gcc/cgraphunit.c                 |   3 +-
>  gcc/coverage.c                   |  22 ++++--
>  gcc/doc/optinfo.texi             |  16 +++-
>  gcc/dumpfile.c                   | 133 ++++++++++++++++++++++++++-------
>  gcc/dumpfile.h                   | 156 +++++++++++++++++++++++++++++++++++++--
>  gcc/gimple-fold.c                |   6 +-
>  gcc/gimple-loop-interchange.cc   |   4 +-
>  gcc/graphite-isl-ast-to-gimple.c |   4 +-
>  gcc/graphite-optimize-isl.c      |   4 +-
>  gcc/graphite.c                   |   2 +-
>  gcc/ipa-devirt.c                 |   3 +-
>  gcc/ipa-prop.c                   |  10 +--
>  gcc/ipa.c                        |   9 +--
>  gcc/loop-unroll.c                |   4 +-
>  gcc/omp-grid.c                   |  47 ++++++------
>  gcc/profile.c                    |  14 +++-
>  gcc/selftest-run-tests.c         |   1 +
>  gcc/selftest.h                   |   1 +
>  gcc/tree-loop-distribution.c     |   2 +-
>  gcc/tree-parloops.c              |   3 +-
>  gcc/tree-ssa-loop-ivcanon.c      |   8 +-
>  gcc/tree-ssa-loop-ivopts.c       |   2 +-
>  gcc/tree-ssa-loop-niter.c        |   2 +-
>  gcc/tree-ssa-sccvn.c             |   3 +-
>  gcc/tree-vect-loop-manip.c       |  16 ++--
>  gcc/tree-vect-loop.c             |   8 +-
>  gcc/tree-vect-slp.c              |   5 +-
>  gcc/tree-vectorizer.c            |  14 ++--
>  gcc/tree-vectorizer.h            |   8 +-
>  gcc/value-prof.c                 |  15 ++--
>  32 files changed, 386 insertions(+), 153 deletions(-)
>
> diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c
> index 8af793c..e27cd39 100644
> --- a/gcc/cfgloop.c
> +++ b/gcc/cfgloop.c
> @@ -1800,7 +1800,7 @@ loop_exits_from_bb_p (struct loop *loop, basic_block bb)
>
>  /* Return location corresponding to the loop control condition if possible.  */
>
> -location_t
> +dump_user_location_t
>  get_loop_location (struct loop *loop)
>  {
>    rtx_insn *insn = NULL;
> @@ -1819,7 +1819,7 @@ get_loop_location (struct loop *loop)
>        FOR_BB_INSNS_REVERSE (desc->in_edge->src, insn)
>          {
>            if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -            return INSN_LOCATION (insn);
> +            return insn;
>          }
>      }
>    /* If loop has a single exit, then the loop control branch
> @@ -1829,24 +1829,24 @@ get_loop_location (struct loop *loop)
>        FOR_BB_INSNS_REVERSE (exit->src, insn)
>          {
>            if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -            return INSN_LOCATION (insn);
> +            return insn;
>          }
>      }
>    /* Next check the latch, to see if it is non-empty.  */
>    FOR_BB_INSNS_REVERSE (loop->latch, insn)
>      {
>        if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -        return INSN_LOCATION (insn);
> +        return insn;
>      }
>    /* Finally, if none of the above identifies the loop control branch,
>       return the first location in the loop header.  */
>    FOR_BB_INSNS (loop->header, insn)
>      {
>        if (INSN_P (insn) && INSN_HAS_LOCATION (insn))
> -        return INSN_LOCATION (insn);
> +        return insn;
>      }
>    /* If all else fails, simply return the current function location.  */
> -  return DECL_SOURCE_LOCATION (current_function_decl);
> +  return dump_user_location_t::from_function_decl (current_function_decl);
>  }
>
>  /* Records that every statement in LOOP is executed I_BOUND times.
> diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
> index af9bfab..80a31c4 100644
> --- a/gcc/cfgloop.h
> +++ b/gcc/cfgloop.h
> @@ -357,7 +357,7 @@ extern bool loop_exit_edge_p (const struct loop *, const_edge);
>  extern bool loop_exits_to_bb_p (struct loop *, basic_block);
>  extern bool loop_exits_from_bb_p (struct loop *, basic_block);
>  extern void mark_loop_exit_edges (void);
> -extern location_t get_loop_location (struct loop *loop);
> +extern dump_user_location_t get_loop_location (struct loop *loop);
>
>  /* Loops & cfg manipulation.  */
>  extern basic_block *get_loop_body (const struct loop *);
> diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
> index 04b6919..7cfb8a0 100644
> --- a/gcc/cgraphunit.c
> +++ b/gcc/cgraphunit.c
> @@ -928,8 +928,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
>             }
>            if (dump_enabled_p ())
>              {
> -             location_t locus = gimple_location_safe (edge->call_stmt);
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
>                                "devirtualizing call in %s to %s\n",
>                                edge->caller->name (), target->name ());
>             }
> diff --git a/gcc/coverage.c b/gcc/coverage.c
> index 84fff13..350cc45 100644
> --- a/gcc/coverage.c
> +++ b/gcc/coverage.c
> @@ -342,12 +342,16 @@ get_coverage_counts (unsigned counter, unsigned expected,
>        static int warned = 0;
>
>        if (!warned++ && dump_enabled_p ())
> -       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> -                         (flag_guess_branch_prob
> -                          ? "file %s not found, execution counts estimated\n"
> -                          : "file %s not found, execution counts assumed to "
> -                            "be zero\n"),
> -                         da_file_name);
> +       {
> +         dump_user_location_t loc
> +           = dump_user_location_t::from_location_t (input_location);
> +         dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                          (flag_guess_branch_prob
> +                           ? "file %s not found, execution counts estimated\n"
> +                           : "file %s not found, execution counts assumed to "
> +                           "be zero\n"),
> +                          da_file_name);
> +       }
>        return NULL;
>      }
>    if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
> @@ -378,7 +382,9 @@ get_coverage_counts (unsigned counter, unsigned expected,
>                     "its profile data (counter %qs)", id, ctr_names[counter]);
>        if (warning_printed && dump_enabled_p ())
>         {
> -          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> +         dump_user_location_t loc
> +           = dump_user_location_t::from_location_t (input_location);
> +          dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                             "use -Wno-error=coverage-mismatch to tolerate "
>                             "the mismatch but performance may drop if the "
>                             "function is hot\n");
> @@ -386,7 +392,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
>           if (!seen_error ()
>               && !warned++)
>             {
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                                 "coverage mismatch ignored\n");
>               dump_printf (MSG_OPTIMIZED_LOCATIONS,
>                             flag_guess_branch_prob
> diff --git a/gcc/doc/optinfo.texi b/gcc/doc/optinfo.texi
> index 8c28501..6202802 100644
> --- a/gcc/doc/optinfo.texi
> +++ b/gcc/doc/optinfo.texi
> @@ -168,7 +168,7 @@ when any of the following flags is enabled
>
>  @example
>  int report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_RTL | TDF_DETAILS;
> -dump_printf_loc (report_flags, locus,
> +dump_printf_loc (report_flags, insn,
>                   "loop turned into non-loop; it never loops.\n");
>  @end example
>
> @@ -181,7 +181,19 @@ Output gimple statement.
>
>  Note that the above methods also have variants prefixed with
>  @code{_loc}, such as @code{dump_printf_loc}, which are similar except
> -they also output the source location information.
> +they also output the source location information.  The @code{_loc} variants
> +take a @code{const dump_location_t &}.  This class can be constructed from
> +a @code{gimple *} or from a @code{rtx_insn *}, and so callers can pass
> +a @code{gimple *} or a @code{rtx_insn *} as the @code{_loc} argument.
> +The @code{dump_location_t} constructor will extract the source location
> +from the statement or instruction, along with the profile count, and
> +the location in GCC's own source code (or the plugin) from which the dump
> +call was emitted.  Only the source location is currently used.
> +There is also a @code{dump_user_location_t} class, capturing the
> +source location and profile count, but not the dump emission location,
> +so that locations in the user's code can be passed around.  This
> +can also be constructed from a @code{gimple *} and from a @code{rtx_insn *},
> +and it too can be passed as the @code{_loc} argument.
>
>  @end ftable
>
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 2f11284..122e420 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -29,6 +29,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "profile-count.h"
>  #include "tree-cfg.h"
>  #include "langhooks.h"
> +#include "backend.h" /* for gimple.h.  */
> +#include "gimple.h" /* for dump_user_location_t ctor.  */
> +#include "rtl.h" /* for dump_user_location_t ctor.  */
> +#include "selftest.h"
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -358,9 +362,51 @@ dump_open_alternate_stream (struct dump_file_info *dfi)
>    return stream;
>  }
>
> +/* Construct a dump_user_location_t from STMT (using its location and
> +   hotness).  */
> +
> +dump_user_location_t::dump_user_location_t (gimple *stmt)
> +: m_count (), m_loc (UNKNOWN_LOCATION)
> +{
> +  if (stmt)
> +    {
> +      if (stmt->bb)
> +       m_count = stmt->bb->count;
> +      m_loc = gimple_location (stmt);
> +    }
> +}
> +
> +/* Construct a dump_user_location_t from an RTL instruction (using its
> +   location and hotness).  */
> +
> +dump_user_location_t::dump_user_location_t (rtx_insn *insn)
> +: m_count (), m_loc (UNKNOWN_LOCATION)
> +{
> +  if (insn)
> +    {
> +      basic_block bb = BLOCK_FOR_INSN (insn);
> +      if (bb)
> +       m_count = bb->count;
> +      m_loc = INSN_LOCATION (insn);
> +    }
> +}
> +
> +/* Construct from a function declaration.  This one requires spelling out
> +   to avoid accidentally constructing from other kinds of tree.  */
> +
> +dump_user_location_t
> +dump_user_location_t::from_function_decl (tree fndecl)
> +{
> +  gcc_assert (fndecl);
> +
> +  // FIXME: profile count for function?
> +  return dump_user_location_t (profile_count (),
> +                              DECL_SOURCE_LOCATION (fndecl));
> +}
> +
>  /* Print source location on DFILE if enabled.  */
>
> -void
> +static void
>  dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>  {
>    if (dump_kind)
> @@ -393,18 +439,19 @@ dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
>
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, source_location loc,
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>                       dump_flags_t extra_dump_flags, gimple *gs, int spc)
>  {
> +  location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
>      {
> -      dump_loc (dump_kind, dump_file, loc);
> +      dump_loc (dump_kind, dump_file, srcloc);
>        print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
>  }
> @@ -423,27 +470,6 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
>  }
>
> -
> -/* Similar to dump_generic_expr, except additionally print the source
> -   location.  */
> -
> -void
> -dump_generic_expr_loc (dump_flags_t dump_kind, source_location loc,
> -                      dump_flags_t extra_dump_flags, tree t)
> -{
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      dump_loc (dump_kind, dump_file, loc);
> -      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      dump_loc (dump_kind, alt_dump_file, loc);
> -      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> -    }
> -}
> -
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
> @@ -469,13 +495,14 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
>  /* Similar to dump_printf, except source location is also printed.  */
>
>  void
> -dump_printf_loc (dump_flags_t dump_kind, source_location loc,
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>                  const char *format, ...)
>  {
> +  location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
>      {
>        va_list ap;
> -      dump_loc (dump_kind, dump_file, loc);
> +      dump_loc (dump_kind, dump_file, srcloc);
>        va_start (ap, format);
>        vfprintf (dump_file, format, ap);
>        va_end (ap);
> @@ -484,7 +511,7 @@ dump_printf_loc (dump_flags_t dump_kind, source_location loc,
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
>        va_list ap;
> -      dump_loc (dump_kind, alt_dump_file, loc);
> +      dump_loc (dump_kind, alt_dump_file, srcloc);
>        va_start (ap, format);
>        vfprintf (alt_dump_file, format, ap);
>        va_end (ap);
> @@ -1059,3 +1086,53 @@ enable_rtl_dump_file (void)
>                             NULL);
>    return num_enabled > 0;
>  }
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Verify that the dump_location_t constructors capture the source location
> +   at which they were called (provided that the build compiler is sufficiently
> +   recent).  */
> +
> +static void
> +test_impl_location ()
> +{
> +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
> +  /* Default ctor.  */
> +  {
> +    dump_location_t loc;
> +    const int expected_line = __LINE__ - 1;
> +    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
> +    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
> +  }
> +
> +  /* Constructing from a gimple.  */
> +  {
> +    dump_location_t loc ((gimple *)NULL);
> +    const int expected_line = __LINE__ - 1;
> +    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
> +    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
> +  }
> +
> +  /* Constructing from an rtx_insn.  */
> +  {
> +    dump_location_t loc ((rtx_insn *)NULL);
> +    const int expected_line = __LINE__ - 1;
> +    ASSERT_STR_CONTAINS (loc.get_impl_location ().m_file, "dumpfile.c");
> +    ASSERT_EQ (loc.get_impl_location ().m_line, expected_line);
> +  }
> +#endif
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +dumpfile_c_tests ()
> +{
> +  test_impl_location ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* CHECKING_P */
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index f6ad670..90d8930 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
>  #ifndef GCC_DUMPFILE_H
>  #define GCC_DUMPFILE_H 1
>
> +#include "profile-count.h"
>
>  /* Different tree dump places.  When you add new tree dump places,
>     extend the DUMP_FILES array in dumpfile.c.  */
> @@ -268,20 +269,165 @@ struct dump_file_info
>    bool graph_dump_initialized;
>  };
>
> +/* A class for describing where in the user's source that a dump message
> +   relates to, with various constructors for convenience.
> +   In particular, this lets us associate dump messages
> +   with hotness information (e.g. from PGO), allowing them to
> +   be prioritized by code hotness.  */
> +
> +class dump_user_location_t
> +{
> + public:
> +  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
> +  dump_user_location_t () : m_count (), m_loc (UNKNOWN_LOCATION) {}
> +
> +  /* Construct from a gimple statement (using its location and hotness).  */
> +  dump_user_location_t (gimple *stmt);
> +
> +  /* Construct from an RTL instruction (using its location and hotness).  */
> +  dump_user_location_t (rtx_insn *insn);
> +
> +  /* Construct from a location_t.  This one is deprecated (since it doesn't
> +     capture hotness information); it thus needs to be spelled out.  */
> +  static dump_user_location_t
> +  from_location_t (location_t loc)
> +  {
> +    return dump_user_location_t (profile_count (), loc);
> +  }
> +
> +  /* Construct from a function declaration.  This one requires spelling out
> +     to avoid accidentally constructing from other kinds of tree.  */
> +  static dump_user_location_t
> +  from_function_decl (tree fndecl);
> +
> +  profile_count get_count () const { return m_count; }
> +  location_t get_location_t () const { return m_loc; }
> +
> + private:
> +  /* Private ctor from count and location, for use by from_location_t.  */
> +  dump_user_location_t (profile_count count, location_t loc)
> +    : m_count (count), m_loc (loc)
> +  {}
> +
> +  profile_count m_count;
> +  location_t m_loc;
> +};
> +
> +/* A class for identifying where in the compiler's own source
> +   (or a plugin) that a dump message is being emitted from.  */
> +
> +struct dump_impl_location_t
> +{
> +  dump_impl_location_t (
> +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
> +                       const char *file = __builtin_FILE (),
> +                       int line = __builtin_LINE (),
> +                       const char *function = __builtin_FUNCTION ()
> +#else
> +                       const char *file = __FILE__,
> +                       int line = __LINE__,
> +                       const char *function = NULL
> +#endif
> +  )
> +  : m_file (file), m_line (line), m_function (function)
> +  {}
> +
> +  const char *m_file;
> +  int m_line;
> +  const char *m_function;
> +};
> +
> +/* A bundle of information for describing the location of a dump message:
> +   (a) the source location and hotness within the user's code, together with
> +   (b) the source location within the compiler/plugin.
> +
> +   The constructors use default parameters so that (b) gets sets up
> +   automatically.
> +
> +   The upshot is that you can pass in e.g. a gimple * to dump_printf_loc,
> +   and the dump call will automatically record where in GCC's source
> +   code the dump was emitted from.  */
> +
> +class dump_location_t
> +{
> + public:
> +  /* Default constructor, analogous to UNKNOWN_LOCATION.  */
> +  dump_location_t (const dump_impl_location_t &impl_location
> +                    = dump_impl_location_t ())
> +  : m_user_location (dump_user_location_t ()),
> +    m_impl_location (impl_location)
> +  {
> +  }
> +
> +  /* Construct from a gimple statement (using its location and hotness).  */
> +  dump_location_t (gimple *stmt,
> +                  const dump_impl_location_t &impl_location
> +                    = dump_impl_location_t ())
> +  : m_user_location (dump_user_location_t (stmt)),
> +    m_impl_location (impl_location)
> +  {
> +  }
> +
> +  /* Construct from an RTL instruction (using its location and hotness).  */
> +  dump_location_t (rtx_insn *insn,
> +                  const dump_impl_location_t &impl_location
> +                  = dump_impl_location_t ())
> +  : m_user_location (dump_user_location_t (insn)),
> +    m_impl_location (impl_location)
> +  {
> +  }
> +
> +  /* Construct from a dump_user_location_t.  */
> +  dump_location_t (const dump_user_location_t &user_location,
> +                  const dump_impl_location_t &impl_location
> +                    = dump_impl_location_t ())
> +  : m_user_location (user_location),
> +    m_impl_location (impl_location)
> +  {
> +  }
> +
> +  /* Construct from a location_t.  This one is deprecated (since it doesn't
> +     capture hotness information), and thus requires spelling out.  */
> +  static dump_location_t
> +  from_location_t (location_t loc,
> +                  const dump_impl_location_t &impl_location
> +                    = dump_impl_location_t ())
> +  {
> +    return dump_location_t (dump_user_location_t::from_location_t (loc),
> +                           impl_location);
> +  }
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_user_location; }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_impl_location; }
> +
> +  location_t get_location_t () const
> +  {
> +    return m_user_location.get_location_t ();
> +  }
> +
> +  profile_count get_count () const { return m_user_location.get_count (); }
> +
> + private:
> +  dump_user_location_t m_user_location;
> +  dump_impl_location_t m_impl_location;
> +};
> +
>  /* In dumpfile.c */
>  extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
>  extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
> -extern void dump_printf_loc (dump_flags_t, source_location,
> -                             const char *, ...) ATTRIBUTE_PRINTF_3;
> +extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
> +                            const char *, ...) ATTRIBUTE_PRINTF_3;
>  extern void dump_function (int phase, tree fn);
>  extern void dump_basic_block (dump_flags_t, basic_block, int);
> -extern void dump_generic_expr_loc (dump_flags_t, source_location, dump_flags_t, tree);
>  extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> -extern void dump_gimple_stmt_loc (dump_flags_t, source_location, dump_flags_t,
> -                                 gimple *, int);
> +extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
> +                                 dump_flags_t, gimple *, int);
>  extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
>  extern void print_combine_total_stats (void);
>  extern bool enable_rtl_dump_file (void);
> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
> index a01bce7..f12e4a7 100644
> --- a/gcc/gimple-fold.c
> +++ b/gcc/gimple-fold.c
> @@ -347,8 +347,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
>                   {
>                     if (dump_enabled_p ())
>                       {
> -                       location_t loc = gimple_location_safe (stmt);
> -                       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>                                          "resolving virtual function address "
>                                          "reference to function %s\n",
>                                          targets.length () == 1
> @@ -4061,8 +4060,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
>               tree lhs = gimple_call_lhs (stmt);
>               if (dump_enabled_p ())
>                 {
> -                 location_t loc = gimple_location_safe (stmt);
> -                 dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                 dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>                                    "folding virtual function call to %s\n",
>                                    targets.length () == 1
>                                    ? targets[0]->name ()
> diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc
> index eb35263..08aeb8e 100644
> --- a/gcc/gimple-loop-interchange.cc
> +++ b/gcc/gimple-loop-interchange.cc
> @@ -523,7 +523,7 @@ loop_cand::analyze_iloop_reduction_var (tree var)
>
>    /* Handle and verify a series of stmts feeding the reduction op.  */
>    if (single_use != next_def
> -      && !check_reduction_path (UNKNOWN_LOCATION, m_loop, phi, next,
> +      && !check_reduction_path (dump_user_location_t (), m_loop, phi, next,
>                                 gimple_assign_rhs_code (single_use)))
>      return false;
>
> @@ -1578,7 +1578,7 @@ bool
>  tree_loop_interchange::interchange (vec<data_reference_p> datarefs,
>                                     vec<ddr_p> ddrs)
>  {
> -  location_t loc = find_loop_location (m_loop_nest[0]);
> +  dump_user_location_t loc = find_loop_location (m_loop_nest[0]);
>    bool changed_p = false;
>    /* In each iteration we try to interchange I-th loop with (I+1)-th loop.
>       The overall effect is to push inner loop to outermost level in whole
> diff --git a/gcc/graphite-isl-ast-to-gimple.c b/gcc/graphite-isl-ast-to-gimple.c
> index b607b12..9e78465 100644
> --- a/gcc/graphite-isl-ast-to-gimple.c
> +++ b/gcc/graphite-isl-ast-to-gimple.c
> @@ -1409,7 +1409,7 @@ scop_to_isl_ast (scop_p scop)
>    isl_ctx_set_max_operations (scop->isl_context, old_max_operations);
>    if (isl_ctx_last_error (scop->isl_context) != isl_error_none)
>      {
> -      location_t loc = find_loop_location
> +      dump_user_location_t loc = find_loop_location
>         (scop->scop_info->region.entry->dest->loop_father);
>        if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
>         dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
> @@ -1518,7 +1518,7 @@ graphite_regenerate_ast_isl (scop_p scop)
>
>    if (t.codegen_error_p ())
>      {
> -      location_t loc = find_loop_location
> +      dump_user_location_t loc = find_loop_location
>         (scop->scop_info->region.entry->dest->loop_father);
>        dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
>                        "loop nest not optimized, code generation error\n");
> diff --git a/gcc/graphite-optimize-isl.c b/gcc/graphite-optimize-isl.c
> index 456a797..35e9ac0 100644
> --- a/gcc/graphite-optimize-isl.c
> +++ b/gcc/graphite-optimize-isl.c
> @@ -160,7 +160,7 @@ optimize_isl (scop_p scop)
>    if (!scop->transformed_schedule
>        || isl_ctx_last_error (scop->isl_context) != isl_error_none)
>      {
> -      location_t loc = find_loop_location
> +      dump_user_location_t loc = find_loop_location
>         (scop->scop_info->region.entry->dest->loop_father);
>        if (isl_ctx_last_error (scop->isl_context) == isl_error_quota)
>         dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
> @@ -182,7 +182,7 @@ optimize_isl (scop_p scop)
>
>    if (same_schedule)
>      {
> -      location_t loc = find_loop_location
> +      dump_user_location_t loc = find_loop_location
>         (scop->scop_info->region.entry->dest->loop_father);
>        dump_printf_loc (MSG_NOTE, loc,
>                        "loop nest not optimized, optimized schedule is "
> diff --git a/gcc/graphite.c b/gcc/graphite.c
> index bcf4828..ddf16a8 100644
> --- a/gcc/graphite.c
> +++ b/gcc/graphite.c
> @@ -412,7 +412,7 @@ graphite_transform_loops (void)
>         changed = true;
>         if (graphite_regenerate_ast_isl (scop))
>           {
> -           location_t loc = find_loop_location
> +           dump_user_location_t loc = find_loop_location
>               (scops[i]->scop_info->region.entry->dest->loop_father);
>             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
>                              "loop nest optimized\n");
> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
> index 308b6e6..e99d8cc 100644
> --- a/gcc/ipa-devirt.c
> +++ b/gcc/ipa-devirt.c
> @@ -3755,8 +3755,7 @@ ipa_devirt (void)
>               {
>                 if (dump_enabled_p ())
>                    {
> -                    location_t locus = gimple_location_safe (e->call_stmt);
> -                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
> +                    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, e->call_stmt,
>                                      "speculatively devirtualizing call "
>                                      "in %s to %s\n",
>                                      n->dump_name (),
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index 000c05f..8b19fe3 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -2842,8 +2842,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
>             {
>               if (dump_enabled_p ())
>                 {
> -                 location_t loc = gimple_location_safe (ie->call_stmt);
> -                 dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                 dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
>                                    "discovered direct call non-invariant %s\n",
>                                    ie->caller->dump_name ());
>                 }
> @@ -2853,8 +2852,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
>
>            if (dump_enabled_p ())
>             {
> -             location_t loc = gimple_location_safe (ie->call_stmt);
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
>                                "discovered direct call to non-function in %s, "
>                                "making it __builtin_unreachable\n",
>                                ie->caller->dump_name ());
> @@ -2942,9 +2940,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
>       }
>    if (dump_enabled_p ())
>      {
> -      location_t loc = gimple_location_safe (ie->call_stmt);
> -
> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, ie->call_stmt,
>                        "converting indirect call in %s to direct call to %s\n",
>                        ie->caller->name (), callee->name ());
>      }
> diff --git a/gcc/ipa.c b/gcc/ipa.c
> index 82fc334..3b6b5e5 100644
> --- a/gcc/ipa.c
> +++ b/gcc/ipa.c
> @@ -225,13 +225,8 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
>                        (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
>
>           if (dump_enabled_p ())
> -            {
> -             location_t locus;
> -             if (edge->call_stmt)
> -               locus = gimple_location (edge->call_stmt);
> -             else
> -               locus = UNKNOWN_LOCATION;
> -             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
> +           {
> +             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt,
>                                "devirtualizing call in %s to %s\n",
>                                edge->caller->dump_name (),
>                                target->dump_name ());
> diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c
> index 5a03932..48bbda0 100644
> --- a/gcc/loop-unroll.c
> +++ b/gcc/loop-unroll.c
> @@ -189,7 +189,7 @@ static rtx get_expansion (struct var_to_expand *);
>     appropriate given the dump or -fopt-info settings.  */
>
>  static void
> -report_unroll (struct loop *loop, location_t locus)
> +report_unroll (struct loop *loop, dump_location_t locus)
>  {
>    dump_flags_t report_flags = MSG_OPTIMIZED_LOCATIONS | TDF_DETAILS;
>
> @@ -220,7 +220,7 @@ decide_unrolling (int flags)
>    FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
>      {
>        loop->lpt_decision.decision = LPT_NONE;
> -      location_t locus = get_loop_location (loop);
> +      dump_user_location_t locus = get_loop_location (loop);
>
>        if (dump_enabled_p ())
>         dump_printf_loc (MSG_NOTE, locus,
> diff --git a/gcc/omp-grid.c b/gcc/omp-grid.c
> index ffa301e..6edc92f 100644
> --- a/gcc/omp-grid.c
> +++ b/gcc/omp-grid.c
> @@ -91,7 +91,7 @@ struct grid_prop
>    bool tiling;
>    /* Location of the target construct for optimization information
>       messages.  */
> -  location_t target_loc;
> +  dump_user_location_t target_loc;
>    /* The collapse clause of the involved loops.  Collapse value of all of them
>       must be the same for gridification to take place.  */
>    size_t collapse;
> @@ -177,10 +177,10 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
>                                    GRID_MISSED_MSG_PREFIX "%s construct "
>                                    "contains multiple OpenMP constructs\n",
>                                    name);
> -                 dump_printf_loc (MSG_NOTE, gimple_location (*ret),
> +                 dump_printf_loc (MSG_NOTE, *ret,
>                                    "The first OpenMP construct within "
>                                    "a parallel\n");
> -                 dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +                 dump_printf_loc (MSG_NOTE, stmt,
>                                    "The second OpenMP construct within "
>                                    "a parallel\n");
>                 }
> @@ -195,7 +195,7 @@ grid_find_single_omp_among_assignments_1 (gimple_seq seq, grid_prop *grid,
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                GRID_MISSED_MSG_PREFIX "%s construct contains "
>                                "a complex statement\n", name);
> -             dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +             dump_printf_loc (MSG_NOTE, stmt,
>                                "This statement cannot be analyzed for "
>                                "gridification\n");
>             }
> @@ -286,7 +286,7 @@ grid_find_ungridifiable_statement (gimple_stmt_iterator *gsi,
>     loop that is evaluated for possible gridification.  */
>
>  static bool
> -grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
> +grid_parallel_clauses_gridifiable (gomp_parallel *par, dump_user_location_t tloc)
>  {
>    tree clauses = gimple_omp_parallel_clauses (par);
>    while (clauses)
> @@ -300,7 +300,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
>                                GRID_MISSED_MSG_PREFIX "because there is "
>                                "a num_threads clause of the parallel "
>                                "construct\n");
> -             dump_printf_loc (MSG_NOTE, gimple_location (par),
> +             dump_printf_loc (MSG_NOTE, par,
>                                "Parallel construct has a num_threads clause\n");
>             }
>           return false;
> @@ -311,7 +311,7 @@ grid_parallel_clauses_gridifiable (gomp_parallel *par, location_t tloc)
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, tloc,
>                                GRID_MISSED_MSG_PREFIX "a reduction clause "
>                                "is present\n ");
> -             dump_printf_loc (MSG_NOTE, gimple_location (par),
> +             dump_printf_loc (MSG_NOTE, par,
>                                "Parallel construct has a reduction clause\n");
>             }
>           return false;
> @@ -341,7 +341,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
>                            GRID_MISSED_MSG_PREFIX "the inner loop "
>                            "loop bounds computation contains a complex "
>                            "statement\n");
> -         dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +         dump_printf_loc (MSG_NOTE, gfor,
>                            "Loop construct cannot be analyzed for "
>                            "gridification\n");
>         }
> @@ -361,7 +361,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
>                   dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                    GRID_MISSED_MSG_PREFIX "the inner loop "
>                                    "has a non-automatic schedule clause\n");
> -                 dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +                 dump_printf_loc (MSG_NOTE, gfor,
>                                    "Loop construct has a non automatic "
>                                    "schedule clause\n");
>                 }
> @@ -375,7 +375,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                GRID_MISSED_MSG_PREFIX "a reduction "
>                                "clause is present\n ");
> -             dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +             dump_printf_loc (MSG_NOTE, gfor,
>                                "Loop construct has a reduction schedule "
>                                "clause\n");
>             }
> @@ -404,7 +404,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
>                              GRID_MISSED_MSG_PREFIX "the inner loop contains "
>                              "statement %s which cannot be transformed\n",
>                              gimple_code_name[(int) gimple_code (bad)]);
> -         dump_printf_loc (MSG_NOTE, gimple_location (bad),
> +         dump_printf_loc (MSG_NOTE, bad,
>                            "This statement cannot be analyzed for "
>                            "gridification\n");
>         }
> @@ -422,7 +422,7 @@ grid_inner_loop_gridifiable_p (gomp_for *gfor, grid_prop *grid)
>  static bool
>  grid_dist_follows_simple_pattern (gomp_for *dist, grid_prop *grid)
>  {
> -  location_t tloc = grid->target_loc;
> +  dump_user_location_t tloc = grid->target_loc;
>    gimple *stmt = grid_find_single_omp_among_assignments (gimple_omp_body (dist),
>                                                          grid, "distribute");
>    gomp_parallel *par;
> @@ -468,7 +468,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
>           dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                            GRID_MISSED_MSG_PREFIX "an inner loop is not "
>                            "a simple for loop\n");
> -         dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +         dump_printf_loc (MSG_NOTE, gfor,
>                            "This statement is not a simple for loop\n");
>         }
>        return false;
> @@ -484,7 +484,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
>           dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                            GRID_MISSED_MSG_PREFIX "an inner loop does not "
>                            "have use the same collapse clause\n");
> -         dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +         dump_printf_loc (MSG_NOTE, gfor,
>                            "Loop construct uses a different collapse clause\n");
>         }
>        return false;
> @@ -524,7 +524,7 @@ grid_gfor_follows_tiling_pattern (gomp_for *gfor, grid_prop *grid)
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                GRID_MISSED_MSG_PREFIX "the distribute and "
>                                "an internal loop do not agree on tile size\n");
> -             dump_printf_loc (MSG_NOTE, gimple_location (gfor),
> +             dump_printf_loc (MSG_NOTE, gfor,
>                                "Loop construct does not seem to loop over "
>                                "a tile size\n");
>             }
> @@ -636,7 +636,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
>                   dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                    GRID_MISSED_MSG_PREFIX "the distribute "
>                                    "construct contains a try..catch region\n");
> -                 dump_printf_loc (MSG_NOTE, gimple_location (try_stmt),
> +                 dump_printf_loc (MSG_NOTE, try_stmt,
>                                    "This statement cannot be analyzed for "
>                                    "tiled gridification\n");
>                 }
> @@ -661,7 +661,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                GRID_MISSED_MSG_PREFIX "the distribute "
>                                "construct contains a call\n");
> -             dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +             dump_printf_loc (MSG_NOTE, stmt,
>                                "This statement cannot be analyzed for "
>                                "tiled gridification\n");
>             }
> @@ -677,7 +677,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
>                                    GRID_MISSED_MSG_PREFIX "a parallel "
>                                    "construct contains another parallel "
>                                    "construct\n");
> -                 dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +                 dump_printf_loc (MSG_NOTE, stmt,
>                                    "This parallel construct is nested in "
>                                    "another one\n");
>                 }
> @@ -698,7 +698,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
>                                    GRID_MISSED_MSG_PREFIX "a loop "
>                                    "construct is not nested within a parallel "
>                                    "construct\n");
> -                 dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +                 dump_printf_loc (MSG_NOTE, stmt,
>                                    "This loop construct is not nested in "
>                                    "a parallel construct\n");
>                 }
> @@ -714,7 +714,7 @@ grid_dist_follows_tiling_pattern (gimple_seq seq, grid_prop *grid,
>               dump_printf_loc (MSG_MISSED_OPTIMIZATION, grid->target_loc,
>                                GRID_MISSED_MSG_PREFIX "the distribute "
>                                "construct contains a complex statement\n");
> -             dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> +             dump_printf_loc (MSG_NOTE, stmt,
>                                "This statement cannot be analyzed for "
>                                "tiled gridification\n");
>             }
> @@ -734,7 +734,7 @@ grid_target_follows_gridifiable_pattern (gomp_target *target, grid_prop *grid)
>    if (gimple_omp_target_kind (target) != GF_OMP_TARGET_KIND_REGION)
>      return false;
>
> -  location_t tloc = gimple_location (target);
> +  dump_user_location_t tloc = target;
>    grid->target_loc = tloc;
>    gimple *stmt
>      = grid_find_single_omp_among_assignments (gimple_omp_body (target),
> @@ -1257,14 +1257,13 @@ grid_attempt_target_gridification (gomp_target *target,
>                                    gbind *tgt_bind)
>  {
>    /* removed group_size */
> -  grid_prop grid;
> -  memset (&grid, 0, sizeof (grid));
> +  grid_prop grid = {};
>    if (!target || !grid_target_follows_gridifiable_pattern (target, &grid))
>      return;
>
>    location_t loc = gimple_location (target);
>    if (dump_enabled_p ())
> -    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, target,
>                      "Target construct will be turned into a gridified HSA "
>                      "kernel\n");
>
> diff --git a/gcc/profile.c b/gcc/profile.c
> index 8ba6dc7..0cd0270 100644
> --- a/gcc/profile.c
> +++ b/gcc/profile.c
> @@ -447,9 +447,14 @@ read_profile_edge_counts (gcov_type *exec_counts)
>                       {
>                         static bool informed = 0;
>                         if (dump_enabled_p () && !informed)
> -                         dump_printf_loc (MSG_NOTE, input_location,
> -                                           "corrupted profile info: edge count"
> -                                           " exceeds maximal count\n");
> +                         {
> +                           dump_location_t loc
> +                             = dump_location_t::from_location_t
> +                               (input_location);
> +                           dump_printf_loc (MSG_NOTE, loc,
> +                                            "corrupted profile info: edge count"
> +                                            " exceeds maximal count\n");
> +                         }
>                         informed = 1;
>                       }
>                     else
> @@ -672,7 +677,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum)
>           if (dump_enabled_p () && informed == 0)
>             {
>               informed = 1;
> -             dump_printf_loc (MSG_NOTE, input_location,
> +             dump_printf_loc (MSG_NOTE,
> +                             dump_location_t::from_location_t (input_location),
>                                "correcting inconsistent profile data\n");
>             }
>           correct_negative_edge_counts ();
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index fe221ff..a9aacc02 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -59,6 +59,7 @@ selftest::run_tests ()
>    /* Low-level data structures.  */
>    bitmap_c_tests ();
>    sbitmap_c_tests ();
> +  dumpfile_c_tests ();
>    et_forest_c_tests ();
>    hash_map_tests_c_tests ();
>    hash_set_tests_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index fc47b2c..a5507cc 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -188,6 +188,7 @@ extern void attribute_c_tests ();
>  extern void bitmap_c_tests ();
>  extern void diagnostic_c_tests ();
>  extern void diagnostic_show_locus_c_tests ();
> +extern void dumpfile_c_tests ();
>  extern void edit_context_c_tests ();
>  extern void et_forest_c_tests ();
>  extern void fibonacci_heap_c_tests ();
> diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
> index c6e0a60..1206614 100644
> --- a/gcc/tree-loop-distribution.c
> +++ b/gcc/tree-loop-distribution.c
> @@ -3117,7 +3117,7 @@ pass_loop_distribution::execute (function *fun)
>             break;
>
>           const char *str = loop->inner ? " nest" : "";
> -         location_t loc = find_loop_location (loop);
> +         dump_user_location_t loc = find_loop_location (loop);
>           if (!cd)
>             {
>               calculate_dominance_info (CDI_DOMINATORS);
> diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
> index c49f032..e79a954 100644
> --- a/gcc/tree-parloops.c
> +++ b/gcc/tree-parloops.c
> @@ -3286,7 +3286,6 @@ parallelize_loops (bool oacc_kernels_p)
>    struct tree_niter_desc niter_desc;
>    struct obstack parloop_obstack;
>    HOST_WIDE_INT estimated;
> -  source_location loop_loc;
>
>    /* Do not parallelize loops in the functions created by parallelization.  */
>    if (!oacc_kernels_p
> @@ -3411,7 +3410,7 @@ parallelize_loops (bool oacc_kernels_p)
>        changed = true;
>        skip_loop = loop->inner;
>
> -      loop_loc = find_loop_location (loop);
> +      dump_user_location_t loop_loc = find_loop_location (loop);
>        if (loop->inner)
>         dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loop_loc,
>                          "parallelizing outer loop %d\n", loop->num);
> diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
> index 24bf60e..5f741c3 100644
> --- a/gcc/tree-ssa-loop-ivcanon.c
> +++ b/gcc/tree-ssa-loop-ivcanon.c
> @@ -691,7 +691,7 @@ try_unroll_loop_completely (struct loop *loop,
>                             edge exit, tree niter, bool may_be_zero,
>                             enum unroll_level ul,
>                             HOST_WIDE_INT maxiter,
> -                           location_t locus, bool allow_peel)
> +                           dump_user_location_t locus, bool allow_peel)
>  {
>    unsigned HOST_WIDE_INT n_unroll = 0;
>    bool n_unroll_found = false;
> @@ -1162,7 +1162,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
>    tree niter;
>    HOST_WIDE_INT maxiter;
>    bool modified = false;
> -  location_t locus = UNKNOWN_LOCATION;
> +  dump_user_location_t locus;
>    struct tree_niter_desc niter_desc;
>    bool may_be_zero = false;
>
> @@ -1177,7 +1177,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
>         = niter_desc.may_be_zero && !integer_zerop (niter_desc.may_be_zero);
>      }
>    if (TREE_CODE (niter) == INTEGER_CST)
> -    locus = gimple_location (last_stmt (exit->src));
> +    locus = last_stmt (exit->src);
>    else
>      {
>        /* For non-constant niter fold may_be_zero into niter again.  */
> @@ -1204,7 +1204,7 @@ canonicalize_loop_induction_variables (struct loop *loop,
>         niter = find_loop_niter_by_eval (loop, &exit);
>
>        if (exit)
> -        locus = gimple_location (last_stmt (exit->src));
> +        locus = last_stmt (exit->src);
>
>        if (TREE_CODE (niter) != INTEGER_CST)
>         exit = NULL;
> diff --git a/gcc/tree-ssa-loop-ivopts.c b/gcc/tree-ssa-loop-ivopts.c
> index 519649a..6b445bd 100644
> --- a/gcc/tree-ssa-loop-ivopts.c
> +++ b/gcc/tree-ssa-loop-ivopts.c
> @@ -7535,7 +7535,7 @@ tree_ssa_iv_optimize_loop (struct ivopts_data *data, struct loop *loop)
>
>    gcc_assert (!data->niters);
>    data->current_loop = loop;
> -  data->loop_loc = find_loop_location (loop);
> +  data->loop_loc = find_loop_location (loop).get_location_t ();
>    data->speed = optimize_loop_for_speed_p (loop);
>
>    if (dump_file && (dump_flags & TDF_DETAILS))
> diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
> index f5ffc0f..03588a0 100644
> --- a/gcc/tree-ssa-loop-niter.c
> +++ b/gcc/tree-ssa-loop-niter.c
> @@ -2627,7 +2627,7 @@ number_of_iterations_exit (struct loop *loop, edge exit,
>      return true;
>
>    if (warn)
> -    dump_printf_loc (MSG_MISSED_OPTIMIZATION, gimple_location_safe (stmt),
> +    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>                      "missed loop optimization: niters analysis ends up "
>                      "with assumptions.\n");
>
> diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
> index 3d025c2..e5eddf9 100644
> --- a/gcc/tree-ssa-sccvn.c
> +++ b/gcc/tree-ssa-sccvn.c
> @@ -5866,8 +5866,7 @@ eliminate_dom_walker::before_dom_children (basic_block b)
>                     fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
>                   if (dump_enabled_p ())
>                     {
> -                     location_t loc = gimple_location (stmt);
> -                     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
> +                     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>                                        "converting indirect call to "
>                                        "function %s\n",
>                                        lang_hooks.decl_printable_name (fn, 2));
> diff --git a/gcc/tree-vect-loop-manip.c b/gcc/tree-vect-loop-manip.c

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [committed] Introduce dump_location_t
  2018-06-28 11:29                   ` Richard Biener
@ 2018-06-28 14:29                     ` David Malcolm
  2018-06-29  7:14                       ` Richard Biener
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-06-28 14:29 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Thu, 2018-06-28 at 13:29 +0200, Richard Biener wrote:
> On Tue, Jun 26, 2018 at 3:54 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> > > On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.co
> > > m>
> > > wrote:
> > > > 
> > > > Here's v3 of the patch (one big patch this time, rather than a
> > > > kit).
> > > > 
> > > > Like the v2 patch kit, this patch reuses the existing dump API,
> > > > rather than inventing its own.
> > > > 
> > > > Specifically, it uses the dump_* functions in dumpfile.h that
> > > > don't
> > > > take a FILE *, the ones that implicitly write to dump_file
> > > > and/or
> > > > alt_dump_file.  I needed a name for them, so I've taken to
> > > > calling
> > > > them the "structured dump API" (better name ideas welcome).
> > > > 
> > > > v3 eliminates v2's optinfo_guard class, instead using
> > > > "dump_*_loc"
> > > > calls as delimiters when consolidating "dump_*" calls.  There's
> > > > a
> > > > new dump_context class which has responsibility for
> > > > consolidating
> > > > them into optimization records.
> > > > 
> > > > The dump_*_loc calls now capture more than just a location_t:
> > > > they
> > > > capture the profile_count and the location in GCC's own sources
> > > > where
> > > > the dump is being emitted from.
> > > > 
> > > > This works by introducing a new "dump_location_t" class as the
> > > > argument of those dump_*_loc calls.  The dump_location_t can
> > > > be constructed from a gimple * or from an rtx_insn *, so that
> > > > rather than writing:
> > > > 
> > > >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> > > >                    "some message: %i", 42);
> > > > 
> > > > you can write:
> > > > 
> > > >   dump_printf_loc (MSG_NOTE, stmt,
> > > >                    "some message: %i", 42);
> > > > 
> > > > and the dump_location_t constructor will grab the location_t
> > > > and
> > > > profile_count of stmt, and the location of the
> > > > "dump_printf_loc"
> > > > callsite (and gracefully handle "stmt" being NULL).
> > > > 
> > > > Earlier versions of the patch captured the location of the
> > > > dump_*_loc call via preprocessor hacks, or didn't work
> > > > properly;
> > > > this version of the patch works more cleanly: internally,
> > > > dump_location_t is split into two new classes:
> > > >   * dump_user_location_t: the location_t and profile_count
> > > > within
> > > >     the *user's code*, and
> > > >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION
> > > > within
> > > >     the *implementation* code (i.e. GCC or a plugin), captured
> > > >     "automagically" via default params
> > > > 
> > > > These classes are sometimes used elsewhere in the code.  For
> > > > example, "vect_location" becomes a dump_user_location_t
> > > > (location_t and profile_count), so that in e.g:
> > > > 
> > > >   vect_location = find_loop_location (loop);
> > > > 
> > > > it's capturing the location_t and profile_count, and then when
> > > > it's used here:
> > > > 
> > > >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > > > 
> > > > the dump_location_t is constructed from the vect_location
> > > > plus the dump_impl_location_t at that callsite.
> > > > 
> > > > In contrast, loop-unroll.c's report_unroll's "locus" param
> > > > becomes a dump_location_t: we're interested in where it was
> > > > called from, not in the locations of the various dump_*_loc
> > > > calls
> > > > within it.
> > > > 
> > > > Previous versions of the patch captured a gimple *, and needed
> > > > GTY markers; in this patch, the dump_user_location_t is now
> > > > just a
> > > > location_t and a profile_count.
> > > > 
> > > > The v2 patch added an overload for dump_printf_loc so that you
> > > > could pass in either a location_t, or the new type; this
> > > > version
> > > > of the patch eliminates that: they all now take
> > > > dump_location_t.
> > > > 
> > > > Doing so required adding support for rtx_insn *, so that one
> > > > can
> > > > write this kind of thing in RTL passes:
> > > > 
> > > >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > > > 
> > > > One knock-on effect is that get_loop_location now returns a
> > > > dump_user_location_t rather than a location_t, so that it has
> > > > hotness information.
> > > > 
> > > > Richi: would you like me to split out this location-handling
> > > > code into a separate patch?  (It's kind of redundant without
> > > > adding the remarks and optimization records work, but if that's
> > > > easier I can do it)
> > > 
> > > I think that would be easier because it doesn't require the JSON
> > > stuff and so I'll happily approve it.
> > > 
> > > Thus - trying to review that bits (and sorry for the delay).
> > > 
> > > +  location_t srcloc = loc.get_location_t ();
> > > +
> > >    if (dump_file && (dump_kind & pflags))
> > >      {
> > > -      dump_loc (dump_kind, dump_file, loc);
> > > +      dump_loc (dump_kind, dump_file, srcloc);
> > >        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > >      }
> > > 
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      {
> > > -      dump_loc (dump_kind, alt_dump_file, loc);
> > > +      dump_loc (dump_kind, alt_dump_file, srcloc);
> > >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > >      }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = begin_next_optinfo (loc);
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_stmt (gs, extra_dump_flags);
> > > +    }
> > > 
> > > seeing this in multiple places.  I seem to remember that
> > > dump_file / alt_dump_file was suposed to handle dumping
> > > into two locations - a dump file and optinfo (or stdout).  This
> > > looks
> > > like the optinfo "stream" is even more separate.  Could that
> > > obsolete the alt_dump_file stream?  I'd need to review existing
> > > stuff
> > > in more detail to answer but maybe you already know from recently
> > > digging into this.
> > 
> > Possibly.  I attempted this in v1 of the patch, but it was mixed in
> > with
> > a bunch of other stuff.  I'll have another go at doing this.
> > 
> > > Oh, and all the if (optinfo_enable_p ()) stuff is for the
> > > followup
> > > then, right?
> > 
> > Yes.
> > 
> > > I like the boiler-plate changes to dump_* using stuff a lot, so
> > > the
> > > infrastructure to do that (the location wrapping) and these
> > > boiler-
> > > plate
> > > changes are pre-approved if split out.
> > 
> > Thanks.  I split out the location wrapping, and have committed it
> > to
> > trunk (r262149).  It's not clear to me exactly what other parts you
> > liked,
> > so I'm going to try to split out more of the non-JSON bits in the
> > hope that some parts are good enough as-is, and I'll post them for
> > review
> > as followups.
> > 
> > For reference, here's what I've committed (I added some obvious
> > changes
> > to doc/optinfo.texi).
> > 
> > > I think the *_REMARK stuff should get attention of the respective
> > > maintainers - not sure what the difference between NOTE and
> > > REMARK
> > > is ;)
> > 
> > Me neither :)  I think it might come down to "this is purely a
> > debugging
> > message for a pass maintainer" (MSG_NOTE) vs "this is a high-level
> > thing
> > that an advanced user want to see" (MSG_REMARK???).
> > 
> > One idea that occurred to me: are we overusing dump_flags_t?  It's
> > a
> > mixture of TDF_* bitfields (used by our internal dumps) plus MSG_*
> > bitfields (used with -fopt-info).  IIRC the only TDF_ bitfield
> > that's
> > ever used with the MSG_* bitfields is TDF_DETAILS.  Might it make
> > sense
> > to split out the MSG_* bitfields into a different type
> > (dump_level_t???)
> > to reinforce this split?  (and thus the dump_* entrypoints that
> > don't take
> > a FILE * would take this new type).  I'm not sure if this is a good
> > idea
> > or not.
> 
> Making it even more complicated doesn't make it easier to use it
> "correctly".  So I'd rather try to simplify it.  How passes use
> TDF_DETAILS vs. non-details is already highly inconsistent.  This
> is why I liked the original optinfo work because it somehow made
> user-interesting vs. developer-interesting with the same API
> (and to the same dump-file).  Just that MSG_NOTE is exposed to
> users while I think it should be developer-only...
> 
> IMHO the TDF_ stuff should control things at the stmt/tree/BB level,
> thus be IL specific flags while the MSG_ stuff should control
> pass-specific dumping detail.

You mention user-interesting vs developer-interesting, and whether it
would be the same API and/or the same dump-file.

I've posted a bunch of patches here, some small, some large, trying
various different appoaches, coming at the problem from at least
two directions, so maybe it's worth discussing the overall direction
here (with some ASCII art!)

* what is the status quo?
* what do we want to achieve?
* how do we get there?

Status quo:
* We have the "dump_*(MSG_*)" API which hides the destination of
  where we're dumping to (currently dump_file and/or alt_file_file,
  conditionalized via flags).

  dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
                                    |
                                    `--> alt_dump_file

* As of r262149 (the dump_location_t commit) dumpfile.c receives the
  hotness and emission location of each dump_*_loc call, but currently
  it does nothing with that information.
* The messages that we emit through the "dump_*(MSG_*)" API and their
  "levels" are currently rather haphazard.  Some are close to being
  suitable for end-users, but most seem never intended to be
  end-user-facing.  For reference:
    grep -nH -e MSG_MISSED_OPTIMIZATION *.c *.cc | wc -l
    452
    grep -nH -e MSG_NOTE *.c *.cc | wc -l
    551
    grep -nH -e MSG_OPTIMIZED_LOCATIONS *.c *.cc | wc -l
    39
  (though some of these are support code e.g. in dumpfile.c, rather
  than uses).
* The API builds up messages programatically, which is hostile to
  i18n.  The API isn't marked as needing i18n for its strings (which
  IIRC is via using "gmsgid" as a param name being special-cased in
  the gettext toolchain).

What do we want to achieve?
* I want end-users to be able to enable high-level dump messages about
  optimizations, and for those messages to be decipherable without
  reading GCC sources e.g. the opt_problem idea posted here:
  * "[PATCH] [RFC] Higher-level reporting of vectorization problems"
    * https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html
  Presumably such messages would need to be internationalized.  Many
  other messages are really internal only, and would be a burden to
  impose on our translators.
* I want to support additional destinations for any/all dump messages
  beyond just the dump file and -fopt-info:
  * putting them through the diagnostics subsystem (as "remarks")
  * storing them to disk in a machine-readable format, so that
    3rd-party tools can present useful visualizations of how the
    user's code is being optimized, e.g. prioritized by hotness
* I want to preserve the existing API and most existing uses of it
  (to minimize churn and thus keep our lives easier)

The patches I've been posting so far add additional dump destinations,
like this:

  dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
                                    |
                                    +--> alt_dump_file
                                    |
                                    +--> diagnostic "remarks"
                                    |
                                    +--> "optimization records"
                                         (saved to disk)

I believe you favor an approach of "MSG_NOTE is internal, whereas
MSG_MISSED_OPTIMIZATION and MSG_OPTIMIZED_LOCATIONS are for end users".

I think the "should this dump message be internationalized?" issue
introduces a natural separation between dump messages aimed at
end-users vs purely "internal" dumps; this suggests to me that
we need a new API for such dump messages, designed to support i18n,
and marked as such for gettext, so I favor something like this:

  SOURCES                                DESTINATIONS
  dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
                   |                |
  new dump API -->-+                +--> alt_dump_file
                                    |
                                    +--> diagnostic "remarks"
                                    |
                                    +--> "optimization records"
                                         (saved to disk)

(I'm deliberately being vague about what this i18n-enabled dump API
might look like)

That said, I have a version of the patch kit which does just this:

  dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
                                    |
                                    +--> alt_dump_file
                                    |
                                    +--> diagnostic "remarks"

i.e. generalizing the dump destinations (by adding optinfo internally),
but without requiring the JSON support or touching the dump API . Would
that be suitable as a next step?

[...snip...]

Thoughts?

Thanks
Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [committed] Introduce dump_location_t
  2018-06-28 14:29                     ` David Malcolm
@ 2018-06-29  7:14                       ` Richard Biener
  2018-07-02 20:51                         ` [PATCH 0/2] v4: optinfo framework and remarks David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-29  7:14 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Thu, Jun 28, 2018 at 4:29 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Thu, 2018-06-28 at 13:29 +0200, Richard Biener wrote:
> > On Tue, Jun 26, 2018 at 3:54 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> > > > On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redhat.co
> > > > m>
> > > > wrote:
> > > > >
> > > > > Here's v3 of the patch (one big patch this time, rather than a
> > > > > kit).
> > > > >
> > > > > Like the v2 patch kit, this patch reuses the existing dump API,
> > > > > rather than inventing its own.
> > > > >
> > > > > Specifically, it uses the dump_* functions in dumpfile.h that
> > > > > don't
> > > > > take a FILE *, the ones that implicitly write to dump_file
> > > > > and/or
> > > > > alt_dump_file.  I needed a name for them, so I've taken to
> > > > > calling
> > > > > them the "structured dump API" (better name ideas welcome).
> > > > >
> > > > > v3 eliminates v2's optinfo_guard class, instead using
> > > > > "dump_*_loc"
> > > > > calls as delimiters when consolidating "dump_*" calls.  There's
> > > > > a
> > > > > new dump_context class which has responsibility for
> > > > > consolidating
> > > > > them into optimization records.
> > > > >
> > > > > The dump_*_loc calls now capture more than just a location_t:
> > > > > they
> > > > > capture the profile_count and the location in GCC's own sources
> > > > > where
> > > > > the dump is being emitted from.
> > > > >
> > > > > This works by introducing a new "dump_location_t" class as the
> > > > > argument of those dump_*_loc calls.  The dump_location_t can
> > > > > be constructed from a gimple * or from an rtx_insn *, so that
> > > > > rather than writing:
> > > > >
> > > > >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> > > > >                    "some message: %i", 42);
> > > > >
> > > > > you can write:
> > > > >
> > > > >   dump_printf_loc (MSG_NOTE, stmt,
> > > > >                    "some message: %i", 42);
> > > > >
> > > > > and the dump_location_t constructor will grab the location_t
> > > > > and
> > > > > profile_count of stmt, and the location of the
> > > > > "dump_printf_loc"
> > > > > callsite (and gracefully handle "stmt" being NULL).
> > > > >
> > > > > Earlier versions of the patch captured the location of the
> > > > > dump_*_loc call via preprocessor hacks, or didn't work
> > > > > properly;
> > > > > this version of the patch works more cleanly: internally,
> > > > > dump_location_t is split into two new classes:
> > > > >   * dump_user_location_t: the location_t and profile_count
> > > > > within
> > > > >     the *user's code*, and
> > > > >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION
> > > > > within
> > > > >     the *implementation* code (i.e. GCC or a plugin), captured
> > > > >     "automagically" via default params
> > > > >
> > > > > These classes are sometimes used elsewhere in the code.  For
> > > > > example, "vect_location" becomes a dump_user_location_t
> > > > > (location_t and profile_count), so that in e.g:
> > > > >
> > > > >   vect_location = find_loop_location (loop);
> > > > >
> > > > > it's capturing the location_t and profile_count, and then when
> > > > > it's used here:
> > > > >
> > > > >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > > > >
> > > > > the dump_location_t is constructed from the vect_location
> > > > > plus the dump_impl_location_t at that callsite.
> > > > >
> > > > > In contrast, loop-unroll.c's report_unroll's "locus" param
> > > > > becomes a dump_location_t: we're interested in where it was
> > > > > called from, not in the locations of the various dump_*_loc
> > > > > calls
> > > > > within it.
> > > > >
> > > > > Previous versions of the patch captured a gimple *, and needed
> > > > > GTY markers; in this patch, the dump_user_location_t is now
> > > > > just a
> > > > > location_t and a profile_count.
> > > > >
> > > > > The v2 patch added an overload for dump_printf_loc so that you
> > > > > could pass in either a location_t, or the new type; this
> > > > > version
> > > > > of the patch eliminates that: they all now take
> > > > > dump_location_t.
> > > > >
> > > > > Doing so required adding support for rtx_insn *, so that one
> > > > > can
> > > > > write this kind of thing in RTL passes:
> > > > >
> > > > >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > > > >
> > > > > One knock-on effect is that get_loop_location now returns a
> > > > > dump_user_location_t rather than a location_t, so that it has
> > > > > hotness information.
> > > > >
> > > > > Richi: would you like me to split out this location-handling
> > > > > code into a separate patch?  (It's kind of redundant without
> > > > > adding the remarks and optimization records work, but if that's
> > > > > easier I can do it)
> > > >
> > > > I think that would be easier because it doesn't require the JSON
> > > > stuff and so I'll happily approve it.
> > > >
> > > > Thus - trying to review that bits (and sorry for the delay).
> > > >
> > > > +  location_t srcloc = loc.get_location_t ();
> > > > +
> > > >    if (dump_file && (dump_kind & pflags))
> > > >      {
> > > > -      dump_loc (dump_kind, dump_file, loc);
> > > > +      dump_loc (dump_kind, dump_file, srcloc);
> > > >        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > > > extra_dump_flags);
> > > >      }
> > > >
> > > >    if (alt_dump_file && (dump_kind & alt_flags))
> > > >      {
> > > > -      dump_loc (dump_kind, alt_dump_file, loc);
> > > > +      dump_loc (dump_kind, alt_dump_file, srcloc);
> > > >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > > > extra_dump_flags);
> > > >      }
> > > > +
> > > > +  if (optinfo_enabled_p ())
> > > > +    {
> > > > +      optinfo &info = begin_next_optinfo (loc);
> > > > +      info.handle_dump_file_kind (dump_kind);
> > > > +      info.add_stmt (gs, extra_dump_flags);
> > > > +    }
> > > >
> > > > seeing this in multiple places.  I seem to remember that
> > > > dump_file / alt_dump_file was suposed to handle dumping
> > > > into two locations - a dump file and optinfo (or stdout).  This
> > > > looks
> > > > like the optinfo "stream" is even more separate.  Could that
> > > > obsolete the alt_dump_file stream?  I'd need to review existing
> > > > stuff
> > > > in more detail to answer but maybe you already know from recently
> > > > digging into this.
> > >
> > > Possibly.  I attempted this in v1 of the patch, but it was mixed in
> > > with
> > > a bunch of other stuff.  I'll have another go at doing this.
> > >
> > > > Oh, and all the if (optinfo_enable_p ()) stuff is for the
> > > > followup
> > > > then, right?
> > >
> > > Yes.
> > >
> > > > I like the boiler-plate changes to dump_* using stuff a lot, so
> > > > the
> > > > infrastructure to do that (the location wrapping) and these
> > > > boiler-
> > > > plate
> > > > changes are pre-approved if split out.
> > >
> > > Thanks.  I split out the location wrapping, and have committed it
> > > to
> > > trunk (r262149).  It's not clear to me exactly what other parts you
> > > liked,
> > > so I'm going to try to split out more of the non-JSON bits in the
> > > hope that some parts are good enough as-is, and I'll post them for
> > > review
> > > as followups.
> > >
> > > For reference, here's what I've committed (I added some obvious
> > > changes
> > > to doc/optinfo.texi).
> > >
> > > > I think the *_REMARK stuff should get attention of the respective
> > > > maintainers - not sure what the difference between NOTE and
> > > > REMARK
> > > > is ;)
> > >
> > > Me neither :)  I think it might come down to "this is purely a
> > > debugging
> > > message for a pass maintainer" (MSG_NOTE) vs "this is a high-level
> > > thing
> > > that an advanced user want to see" (MSG_REMARK???).
> > >
> > > One idea that occurred to me: are we overusing dump_flags_t?  It's
> > > a
> > > mixture of TDF_* bitfields (used by our internal dumps) plus MSG_*
> > > bitfields (used with -fopt-info).  IIRC the only TDF_ bitfield
> > > that's
> > > ever used with the MSG_* bitfields is TDF_DETAILS.  Might it make
> > > sense
> > > to split out the MSG_* bitfields into a different type
> > > (dump_level_t???)
> > > to reinforce this split?  (and thus the dump_* entrypoints that
> > > don't take
> > > a FILE * would take this new type).  I'm not sure if this is a good
> > > idea
> > > or not.
> >
> > Making it even more complicated doesn't make it easier to use it
> > "correctly".  So I'd rather try to simplify it.  How passes use
> > TDF_DETAILS vs. non-details is already highly inconsistent.  This
> > is why I liked the original optinfo work because it somehow made
> > user-interesting vs. developer-interesting with the same API
> > (and to the same dump-file).  Just that MSG_NOTE is exposed to
> > users while I think it should be developer-only...
> >
> > IMHO the TDF_ stuff should control things at the stmt/tree/BB level,
> > thus be IL specific flags while the MSG_ stuff should control
> > pass-specific dumping detail.
>
> You mention user-interesting vs developer-interesting, and whether it
> would be the same API and/or the same dump-file.
>
> I've posted a bunch of patches here, some small, some large, trying
> various different appoaches, coming at the problem from at least
> two directions, so maybe it's worth discussing the overall direction
> here (with some ASCII art!)
>
> * what is the status quo?
> * what do we want to achieve?
> * how do we get there?
>
> Status quo:
> * We have the "dump_*(MSG_*)" API which hides the destination of
>   where we're dumping to (currently dump_file and/or alt_file_file,
>   conditionalized via flags).
>
>   dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
>                                     |
>                                     `--> alt_dump_file
>
> * As of r262149 (the dump_location_t commit) dumpfile.c receives the
>   hotness and emission location of each dump_*_loc call, but currently
>   it does nothing with that information.
> * The messages that we emit through the "dump_*(MSG_*)" API and their
>   "levels" are currently rather haphazard.  Some are close to being
>   suitable for end-users, but most seem never intended to be
>   end-user-facing.  For reference:
>     grep -nH -e MSG_MISSED_OPTIMIZATION *.c *.cc | wc -l
>     452
>     grep -nH -e MSG_NOTE *.c *.cc | wc -l
>     551
>     grep -nH -e MSG_OPTIMIZED_LOCATIONS *.c *.cc | wc -l
>     39
>   (though some of these are support code e.g. in dumpfile.c, rather
>   than uses).

Yep.  I believe MSG_NOTE was the fallout of optinfo introduction and my
request to share the same API with dumping to the dumpfile.  MSG_NOTE
just received "anything else" ...

> * The API builds up messages programatically, which is hostile to
>   i18n.  The API isn't marked as needing i18n for its strings (which
>   IIRC is via using "gmsgid" as a param name being special-cased in
>   the gettext toolchain).
>
> What do we want to achieve?
> * I want end-users to be able to enable high-level dump messages about
>   optimizations, and for those messages to be decipherable without
>   reading GCC sources e.g. the opt_problem idea posted here:
>   * "[PATCH] [RFC] Higher-level reporting of vectorization problems"
>     * https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html
>   Presumably such messages would need to be internationalized.  Many
>   other messages are really internal only, and would be a burden to
>   impose on our translators.
> * I want to support additional destinations for any/all dump messages
>   beyond just the dump file and -fopt-info:
>   * putting them through the diagnostics subsystem (as "remarks")
>   * storing them to disk in a machine-readable format, so that
>     3rd-party tools can present useful visualizations of how the
>     user's code is being optimized, e.g. prioritized by hotness
> * I want to preserve the existing API and most existing uses of it
>   (to minimize churn and thus keep our lives easier)
>
> The patches I've been posting so far add additional dump destinations,
> like this:
>
>   dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
>                                     |
>                                     +--> alt_dump_file
>                                     |
>                                     +--> diagnostic "remarks"
>                                     |
>                                     +--> "optimization records"
>                                          (saved to disk)
>
> I believe you favor an approach of "MSG_NOTE is internal, whereas
> MSG_MISSED_OPTIMIZATION and MSG_OPTIMIZED_LOCATIONS are for end users".

Yeah, kind of.  You introduced another MSG_ variant and in the end we might
need to go that way.  I guess renaming MSG_NOTE to MSG_DEBUG would
be a step in the direction to reflect reality ;)

> I think the "should this dump message be internationalized?" issue
> introduces a natural separation between dump messages aimed at
> end-users vs purely "internal" dumps; this suggests to me that
> we need a new API for such dump messages, designed to support i18n,
> and marked as such for gettext, so I favor something like this:
>
>   SOURCES                                DESTINATIONS
>   dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
>                    |                |
>   new dump API -->-+                +--> alt_dump_file
>                                     |
>                                     +--> diagnostic "remarks"
>                                     |
>                                     +--> "optimization records"
>                                          (saved to disk)
>
> (I'm deliberately being vague about what this i18n-enabled dump API
> might look like)

I know I threw i18n into the discussion - but I'd say we should focus
on other details first.  I think there's no need to _require_ the "debug"
dumpfile stuff not be translated, but to make it easier for translators
I suppose being able to "amend" the API calls with a "do not translate"
variant would be OK.

So in what way is the dump_* API not suitable for translation
that the diagnostic machinery (warning/error) does not suffer from?

That is, if we internally make dump_* go through the pretty-printers
we can handle stuff like quoting or even stmts/expressions.  I think
that would be useful for dumping to dumpfiles as well.

>
> That said, I have a version of the patch kit which does just this:
>
>   dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
>                                     |
>                                     +--> alt_dump_file
>                                     |
>                                     +--> diagnostic "remarks"
>
> i.e. generalizing the dump destinations (by adding optinfo internally),
> but without requiring the JSON support or touching the dump API . Would
> that be suitable as a next step?

Yes.  Thanks for going step-by-step btw, this is really useful in simplifying
the picture.

Richard.

>
> [...snip...]
>
> Thoughts?
>
> Thanks
> Dave
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-06-26 15:43                 ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE David Malcolm
@ 2018-06-29  8:13                   ` Richard Biener
  2018-07-02 12:25                     ` Christophe Lyon
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-06-29  8:13 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch adds a concept of nested "scopes" to dumpfile.c's dump_*_loc
> calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-vectorizer.h,
> so that the nested structure is shown in -fopt-info by indentation.
>
> For example, this converts -fopt-info-all e.g. from:
>
> test.c:8:3: note: === analyzing loop ===
> test.c:8:3: note: === analyze_loop_nest ===
> test.c:8:3: note: === vect_analyze_loop_form ===
> test.c:8:3: note: === get_loop_niters ===
> test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
> test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
> test.c:8:3: note: vectorized 0 loops in function
>
> to:
>
> test.c:8:3: note: === analyzing loop ===
> test.c:8:3: note:  === analyze_loop_nest ===
> test.c:8:3: note:   === vect_analyze_loop_form ===
> test.c:8:3: note:    === get_loop_niters ===
> test.c:8:3: note:   symbolic number of iterations is (unsigned int) n_9(D)
> test.c:8:3: note:   not vectorized: loop contains function calls or data references that cannot be analyzed
> test.c:8:3: note: vectorized 0 loops in function
>
> showing that the "symbolic number of iterations" message is within
> the "=== analyze_loop_nest ===" (and not within the
> "=== vect_analyze_loop_form ===").
>
> This is also enabling work for followups involving optimization records
> (allowing the records to directly capture the nested structure of the
> dump messages).
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

OK and sorry for the delay.
Richard.

> gcc/ChangeLog:
>         * dumpfile.c (dump_loc): Add indentation based on scope depth.
>         (dump_scope_depth): New variable.
>         (get_dump_scope_depth): New function.
>         (dump_begin_scope): New function.
>         (dump_end_scope): New function.
>         * dumpfile.h (get_dump_scope_depth): New declaration.
>         (dump_begin_scope): New declaration.
>         (dump_end_scope): New declaration.
>         (class auto_dump_scope): New class.
>         (AUTO_DUMP_SCOPE): New macro.
>         * tree-vectorizer.h (DUMP_VECT_SCOPE): Reimplement in terms of
>         AUTO_DUMP_SCOPE.
> ---
>  gcc/dumpfile.c        | 35 +++++++++++++++++++++++++++++++++++
>  gcc/dumpfile.h        | 39 +++++++++++++++++++++++++++++++++++++++
>  gcc/tree-vectorizer.h | 15 ++++++++-------
>  3 files changed, 82 insertions(+), 7 deletions(-)
>
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 122e420..190b52d 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -419,6 +419,8 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>                   DECL_SOURCE_FILE (current_function_decl),
>                   DECL_SOURCE_LINE (current_function_decl),
>                   DECL_SOURCE_COLUMN (current_function_decl));
> +      /* Indentation based on scope depth.  */
> +      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
>      }
>  }
>
> @@ -539,6 +541,39 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
>  template void dump_dec (dump_flags_t, const poly_offset_int &);
>  template void dump_dec (dump_flags_t, const poly_widest_int &);
>
> +/* The current dump scope-nesting depth.  */
> +
> +static int dump_scope_depth;
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by dump_*_loc (for showing nesting via indentation).  */
> +
> +unsigned int
> +get_dump_scope_depth ()
> +{
> +  return dump_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Increment the scope depth.  */
> +
> +void
> +dump_begin_scope (const char *name, const dump_location_t &loc)
> +{
> +  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> +  dump_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_end_scope ()
> +{
> +  dump_scope_depth--;
> +}
> +
>  /* Start a dump for PHASE. Store user-supplied dump flags in
>     *FLAG_PTR.  Return the number of streams opened.  Set globals
>     DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 90d8930..89d5c11 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -456,6 +456,45 @@ dump_enabled_p (void)
>    return (dump_file || alt_dump_file);
>  }
>
> +/* Managing nested scopes, so that dumps can express the call chain
> +   leading to a dump message.  */
> +
> +extern unsigned int get_dump_scope_depth ();
> +extern void dump_begin_scope (const char *name, const dump_location_t &loc);
> +extern void dump_end_scope ();
> +
> +/* Implementation detail of the AUTO_DUMP_SCOPE macro below.
> +
> +   A RAII-style class intended to make it easy to emit dump
> +   information about entering and exiting a collection of nested
> +   function calls.  */
> +
> +class auto_dump_scope
> +{
> + public:
> +  auto_dump_scope (const char *name, dump_location_t loc)
> +  {
> +    if (dump_enabled_p ())
> +      dump_begin_scope (name, loc);
> +  }
> +  ~auto_dump_scope ()
> +  {
> +    if (dump_enabled_p ())
> +      dump_end_scope ();
> +  }
> +};
> +
> +/* A macro for calling:
> +     dump_begin_scope (NAME, LOC);
> +   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
> +   and then calling
> +     dump_end_scope ();
> +   once the object goes out of scope, thus capturing the nesting of
> +   the scopes.  */
> +
> +#define AUTO_DUMP_SCOPE(NAME, LOC) \
> +  auto_dump_scope scope (NAME, LOC)
> +
>  namespace gcc {
>
>  class dump_manager
> diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> index 94a0f38..a8406b3 100644
> --- a/gcc/tree-vectorizer.h
> +++ b/gcc/tree-vectorizer.h
> @@ -1440,15 +1440,16 @@ vect_get_scalar_dr_size (struct data_reference *dr)
>  /* Source location + hotness information. */
>  extern dump_user_location_t vect_location;
>
> -/* If dumping is enabled, emit a MSG_NOTE at vect_location about
> -   entering MSG within the vectorizer.  MSG should be a string literal. */
> +/* A macro for calling:
> +     dump_begin_scope (MSG, vect_location);
> +   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
> +   and then calling
> +     dump_end_scope ();
> +   once the object goes out of scope, thus capturing the nesting of
> +   the scopes.  */
>
>  #define DUMP_VECT_SCOPE(MSG) \
> -  do {                                         \
> -    if (dump_enabled_p ())                     \
> -      dump_printf_loc (MSG_NOTE, vect_location, \
> -                      "=== " MSG " ===\n");    \
> -  } while (0)
> +  AUTO_DUMP_SCOPE (MSG, vect_location)
>
>  /*-----------------------------------------------------------------*/
>  /* Function prototypes.                                            */
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-06-29  8:13                   ` Richard Biener
@ 2018-07-02 12:25                     ` Christophe Lyon
  2018-07-02 17:00                       ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Christophe Lyon @ 2018-07-02 12:25 UTC (permalink / raw)
  To: Richard Biener; +Cc: David Malcolm, gcc Patches

[-- Attachment #1: Type: text/plain, Size: 9457 bytes --]

On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gmail.com> wrote:
>
> On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > This patch adds a concept of nested "scopes" to dumpfile.c's dump_*_loc
> > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-vectorizer.h,
> > so that the nested structure is shown in -fopt-info by indentation.
> >
> > For example, this converts -fopt-info-all e.g. from:
> >
> > test.c:8:3: note: === analyzing loop ===
> > test.c:8:3: note: === analyze_loop_nest ===
> > test.c:8:3: note: === vect_analyze_loop_form ===
> > test.c:8:3: note: === get_loop_niters ===
> > test.c:8:3: note: symbolic number of iterations is (unsigned int) n_9(D)
> > test.c:8:3: note: not vectorized: loop contains function calls or data references that cannot be analyzed
> > test.c:8:3: note: vectorized 0 loops in function
> >
> > to:
> >
> > test.c:8:3: note: === analyzing loop ===
> > test.c:8:3: note:  === analyze_loop_nest ===
> > test.c:8:3: note:   === vect_analyze_loop_form ===
> > test.c:8:3: note:    === get_loop_niters ===
> > test.c:8:3: note:   symbolic number of iterations is (unsigned int) n_9(D)
> > test.c:8:3: note:   not vectorized: loop contains function calls or data references that cannot be analyzed
> > test.c:8:3: note: vectorized 0 loops in function
> >
> > showing that the "symbolic number of iterations" message is within
> > the "=== analyze_loop_nest ===" (and not within the
> > "=== vect_analyze_loop_form ===").
> >
> > This is also enabling work for followups involving optimization records
> > (allowing the records to directly capture the nested structure of the
> > dump messages).
> >
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> >
> > OK for trunk?
>

Hi,

I've noticed that this patch (r262246) caused regressions on aarch64:
    gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-dump
vect "note: Built SLP cancelled: can use load/store-lanes"
    gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
cancelled: can use load/store-lanes"

The problem is that now there are more spaces between "note:" and
"Built", the attached small patch does that for slp-perm-1.c.
Is it the right way of fixing it or do we want to accept any amount of
spaces for instance?

I'm surprised there is such little impact on the testsuite though.

If OK, I'll update the patch to take the other slp-perm-[235678].c
tests into account.

Thanks,

Christophe

> OK and sorry for the delay.
> Richard.
>
> > gcc/ChangeLog:
> >         * dumpfile.c (dump_loc): Add indentation based on scope depth.
> >         (dump_scope_depth): New variable.
> >         (get_dump_scope_depth): New function.
> >         (dump_begin_scope): New function.
> >         (dump_end_scope): New function.
> >         * dumpfile.h (get_dump_scope_depth): New declaration.
> >         (dump_begin_scope): New declaration.
> >         (dump_end_scope): New declaration.
> >         (class auto_dump_scope): New class.
> >         (AUTO_DUMP_SCOPE): New macro.
> >         * tree-vectorizer.h (DUMP_VECT_SCOPE): Reimplement in terms of
> >         AUTO_DUMP_SCOPE.
> > ---
> >  gcc/dumpfile.c        | 35 +++++++++++++++++++++++++++++++++++
> >  gcc/dumpfile.h        | 39 +++++++++++++++++++++++++++++++++++++++
> >  gcc/tree-vectorizer.h | 15 ++++++++-------
> >  3 files changed, 82 insertions(+), 7 deletions(-)
> >
> > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > index 122e420..190b52d 100644
> > --- a/gcc/dumpfile.c
> > +++ b/gcc/dumpfile.c
> > @@ -419,6 +419,8 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
> >                   DECL_SOURCE_FILE (current_function_decl),
> >                   DECL_SOURCE_LINE (current_function_decl),
> >                   DECL_SOURCE_COLUMN (current_function_decl));
> > +      /* Indentation based on scope depth.  */
> > +      fprintf (dfile, "%*s", get_dump_scope_depth (), "");
> >      }
> >  }
> >
> > @@ -539,6 +541,39 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
> >  template void dump_dec (dump_flags_t, const poly_offset_int &);
> >  template void dump_dec (dump_flags_t, const poly_widest_int &);
> >
> > +/* The current dump scope-nesting depth.  */
> > +
> > +static int dump_scope_depth;
> > +
> > +/* Get the current dump scope-nesting depth.
> > +   For use by dump_*_loc (for showing nesting via indentation).  */
> > +
> > +unsigned int
> > +get_dump_scope_depth ()
> > +{
> > +  return dump_scope_depth;
> > +}
> > +
> > +/* Push a nested dump scope.
> > +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> > +   destination, if any.
> > +   Increment the scope depth.  */
> > +
> > +void
> > +dump_begin_scope (const char *name, const dump_location_t &loc)
> > +{
> > +  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> > +  dump_scope_depth++;
> > +}
> > +
> > +/* Pop a nested dump scope.  */
> > +
> > +void
> > +dump_end_scope ()
> > +{
> > +  dump_scope_depth--;
> > +}
> > +
> >  /* Start a dump for PHASE. Store user-supplied dump flags in
> >     *FLAG_PTR.  Return the number of streams opened.  Set globals
> >     DUMP_FILE, and ALT_DUMP_FILE to point to the opened streams, and
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 90d8930..89d5c11 100644
> > --- a/gcc/dumpfile.h
> > +++ b/gcc/dumpfile.h
> > @@ -456,6 +456,45 @@ dump_enabled_p (void)
> >    return (dump_file || alt_dump_file);
> >  }
> >
> > +/* Managing nested scopes, so that dumps can express the call chain
> > +   leading to a dump message.  */
> > +
> > +extern unsigned int get_dump_scope_depth ();
> > +extern void dump_begin_scope (const char *name, const dump_location_t &loc);
> > +extern void dump_end_scope ();
> > +
> > +/* Implementation detail of the AUTO_DUMP_SCOPE macro below.
> > +
> > +   A RAII-style class intended to make it easy to emit dump
> > +   information about entering and exiting a collection of nested
> > +   function calls.  */
> > +
> > +class auto_dump_scope
> > +{
> > + public:
> > +  auto_dump_scope (const char *name, dump_location_t loc)
> > +  {
> > +    if (dump_enabled_p ())
> > +      dump_begin_scope (name, loc);
> > +  }
> > +  ~auto_dump_scope ()
> > +  {
> > +    if (dump_enabled_p ())
> > +      dump_end_scope ();
> > +  }
> > +};
> > +
> > +/* A macro for calling:
> > +     dump_begin_scope (NAME, LOC);
> > +   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
> > +   and then calling
> > +     dump_end_scope ();
> > +   once the object goes out of scope, thus capturing the nesting of
> > +   the scopes.  */
> > +
> > +#define AUTO_DUMP_SCOPE(NAME, LOC) \
> > +  auto_dump_scope scope (NAME, LOC)
> > +
> >  namespace gcc {
> >
> >  class dump_manager
> > diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> > index 94a0f38..a8406b3 100644
> > --- a/gcc/tree-vectorizer.h
> > +++ b/gcc/tree-vectorizer.h
> > @@ -1440,15 +1440,16 @@ vect_get_scalar_dr_size (struct data_reference *dr)
> >  /* Source location + hotness information. */
> >  extern dump_user_location_t vect_location;
> >
> > -/* If dumping is enabled, emit a MSG_NOTE at vect_location about
> > -   entering MSG within the vectorizer.  MSG should be a string literal. */
> > +/* A macro for calling:
> > +     dump_begin_scope (MSG, vect_location);
> > +   via an RAII object, thus printing "=== MSG ===\n" to the dumpfile etc,
> > +   and then calling
> > +     dump_end_scope ();
> > +   once the object goes out of scope, thus capturing the nesting of
> > +   the scopes.  */
> >
> >  #define DUMP_VECT_SCOPE(MSG) \
> > -  do {                                         \
> > -    if (dump_enabled_p ())                     \
> > -      dump_printf_loc (MSG_NOTE, vect_location, \
> > -                      "=== " MSG " ===\n");    \
> > -  } while (0)
> > +  AUTO_DUMP_SCOPE (MSG, vect_location)
> >
> >  /*-----------------------------------------------------------------*/
> >  /* Function prototypes.                                            */
> > --
> > 1.8.5.3
> >

[-- Attachment #2: slp.chlog.txt --]
[-- Type: text/plain, Size: 171 bytes --]

gcc/testsuite/ChangeLog:

2018-07-02  Christophe Lyon  <christophe.lyon@linaro.org>

	* gcc.dg/vect/slp-perm-1.c: Adjust scan-tree-dump directive after
	new indentation.


[-- Attachment #3: slp.patch.txt --]
[-- Type: text/plain, Size: 1093 bytes --]

diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
index 6bd16ef..612139ba 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
@@ -82,7 +82,7 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && {! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "note:   Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-02 12:25                     ` Christophe Lyon
@ 2018-07-02 17:00                       ` David Malcolm
  2018-07-02 17:09                         ` Christophe Lyon
  2018-07-03  7:37                         ` Richard Biener
  0 siblings, 2 replies; 80+ messages in thread
From: David Malcolm @ 2018-07-02 17:00 UTC (permalink / raw)
  To: Christophe Lyon, Richard Biener; +Cc: gcc Patches

On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gmail.
> com> wrote:
> > 
> > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > > 
> > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > dump_*_loc
> > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > vectorizer.h,
> > > so that the nested structure is shown in -fopt-info by
> > > indentation.
> > > 
> > > For example, this converts -fopt-info-all e.g. from:
> > > 
> > > test.c:8:3: note: === analyzing loop ===
> > > test.c:8:3: note: === analyze_loop_nest ===
> > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > test.c:8:3: note: === get_loop_niters ===
> > > test.c:8:3: note: symbolic number of iterations is (unsigned int)
> > > n_9(D)
> > > test.c:8:3: note: not vectorized: loop contains function calls or
> > > data references that cannot be analyzed
> > > test.c:8:3: note: vectorized 0 loops in function
> > > 
> > > to:
> > > 
> > > test.c:8:3: note: === analyzing loop ===
> > > test.c:8:3: note:  === analyze_loop_nest ===
> > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > test.c:8:3: note:    === get_loop_niters ===
> > > test.c:8:3: note:   symbolic number of iterations is (unsigned
> > > int) n_9(D)
> > > test.c:8:3: note:   not vectorized: loop contains function calls
> > > or data references that cannot be analyzed
> > > test.c:8:3: note: vectorized 0 loops in function
> > > 
> > > showing that the "symbolic number of iterations" message is
> > > within
> > > the "=== analyze_loop_nest ===" (and not within the
> > > "=== vect_analyze_loop_form ===").
> > > 
> > > This is also enabling work for followups involving optimization
> > > records
> > > (allowing the records to directly capture the nested structure of
> > > the
> > > dump messages).
> > > 
> > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > > 
> > > OK for trunk?
> 
> Hi,
> 
> I've noticed that this patch (r262246) caused regressions on aarch64:
>     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-dump
> vect "note: Built SLP cancelled: can use load/store-lanes"
>     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> cancelled: can use load/store-lanes"
> 
> The problem is that now there are more spaces between "note:" and
> "Built", the attached small patch does that for slp-perm-1.c.

Sorry about the breakage.

> Is it the right way of fixing it or do we want to accept any amount
> of
> spaces for instance?

I don't think we want to hardcode the amount of space in the dumpfile. 
The idea of my patch was to make the dump more human-readable (I hope)
by visualizing the nesting structure of the dump messages, but I think
we shouldn't "bake" that into the expected strings, as someone might
want to add an intermediate nesting level.

Do we really need to look for the "note:" in the scan-tree-dump? 
Should that directive be rewritten to:

-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */

which I believe would match any amount of spaces.

Alternatively a regex accepting any amount of space ought to work, if
we care that the message begins with "note: ".

The "note: " comes from dumpfile.c's dump_loc, and is emitted
regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION or
whatever.  Given that, maybe we should just drop the "note: " prefix
from these scan-tree-dump expected regexes?  (assuming that works)

> I'm surprised there is such little impact on the testsuite though.

I see lots of scan-tree-dump* directives in the vect part of the
testsuite, but it seems that only these ones use the "note: " prefix; I
think everything else was matching against the message whilst ignoring
the prefix, so it didn't matter when the prefix changed
(I double-checked and these scan-tree-dump directives didn't trigger on
my x86_64 testing of the patch, due to { target { vect_perm3_int &&
vect_load_lanes } }, where I see
check_effective_target_vect_load_lanes: returning 0 in the log)

> If OK, I'll update the patch to take the other slp-perm-[235678].c
> tests into account.
> 
> Thanks,
> 
> Christophe

Sorry again about the breakage.
Dave

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-02 17:00                       ` David Malcolm
@ 2018-07-02 17:09                         ` Christophe Lyon
  2018-07-03  7:37                         ` Richard Biener
  1 sibling, 0 replies; 80+ messages in thread
From: Christophe Lyon @ 2018-07-02 17:09 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, gcc Patches

On Mon, 2 Jul 2018 at 19:00, David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gmail.
> > com> wrote:
> > >
> > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.com>
> > > wrote:
> > > >
> > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > dump_*_loc
> > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > vectorizer.h,
> > > > so that the nested structure is shown in -fopt-info by
> > > > indentation.
> > > >
> > > > For example, this converts -fopt-info-all e.g. from:
> > > >
> > > > test.c:8:3: note: === analyzing loop ===
> > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > test.c:8:3: note: === get_loop_niters ===
> > > > test.c:8:3: note: symbolic number of iterations is (unsigned int)
> > > > n_9(D)
> > > > test.c:8:3: note: not vectorized: loop contains function calls or
> > > > data references that cannot be analyzed
> > > > test.c:8:3: note: vectorized 0 loops in function
> > > >
> > > > to:
> > > >
> > > > test.c:8:3: note: === analyzing loop ===
> > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > test.c:8:3: note:    === get_loop_niters ===
> > > > test.c:8:3: note:   symbolic number of iterations is (unsigned
> > > > int) n_9(D)
> > > > test.c:8:3: note:   not vectorized: loop contains function calls
> > > > or data references that cannot be analyzed
> > > > test.c:8:3: note: vectorized 0 loops in function
> > > >
> > > > showing that the "symbolic number of iterations" message is
> > > > within
> > > > the "=== analyze_loop_nest ===" (and not within the
> > > > "=== vect_analyze_loop_form ===").
> > > >
> > > > This is also enabling work for followups involving optimization
> > > > records
> > > > (allowing the records to directly capture the nested structure of
> > > > the
> > > > dump messages).
> > > >
> > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > > >
> > > > OK for trunk?
> >
> > Hi,
> >
> > I've noticed that this patch (r262246) caused regressions on aarch64:
> >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >
> > The problem is that now there are more spaces between "note:" and
> > "Built", the attached small patch does that for slp-perm-1.c.
>
> Sorry about the breakage.
>
> > Is it the right way of fixing it or do we want to accept any amount
> > of
> > spaces for instance?
>
> I don't think we want to hardcode the amount of space in the dumpfile.
> The idea of my patch was to make the dump more human-readable (I hope)
> by visualizing the nesting structure of the dump messages, but I think
> we shouldn't "bake" that into the expected strings, as someone might
> want to add an intermediate nesting level.
>
> Do we really need to look for the "note:" in the scan-tree-dump?
> Should that directive be rewritten to:
>
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>
> which I believe would match any amount of spaces.

I thought about that too, and like you noticed that almost all other
scan-tree-dump directives omitted the "note:" part.

It seems like a better approach, unless someone objects?

>
> Alternatively a regex accepting any amount of space ought to work, if
> we care that the message begins with "note: ".
>
> The "note: " comes from dumpfile.c's dump_loc, and is emitted
> regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION or
> whatever.  Given that, maybe we should just drop the "note: " prefix
> from these scan-tree-dump expected regexes?  (assuming that works)
>
> > I'm surprised there is such little impact on the testsuite though.
>
> I see lots of scan-tree-dump* directives in the vect part of the
> testsuite, but it seems that only these ones use the "note: " prefix; I
> think everything else was matching against the message whilst ignoring
> the prefix, so it didn't matter when the prefix changed
> (I double-checked and these scan-tree-dump directives didn't trigger on
> my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> vect_load_lanes } }, where I see
> check_effective_target_vect_load_lanes: returning 0 in the log)
>
> > If OK, I'll update the patch to take the other slp-perm-[235678].c
> > tests into account.
> >
> > Thanks,
> >
> > Christophe
>
> Sorry again about the breakage.
> Dave
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 1/2] Add "optinfo" framework
  2018-07-02 20:51                         ` [PATCH 0/2] v4: optinfo framework and remarks David Malcolm
  2018-07-02 20:51                           ` [PATCH 2/2] optinfo: add diagnostic remarks David Malcolm
@ 2018-07-02 20:51                           ` David Malcolm
  2018-07-09 13:01                             ` Richard Biener
  1 sibling, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-02 20:51 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, David Malcolm

This patch implements a way to consolidate dump_* calls into
optinfo objects, as enabling work towards being able to write out
optimization records to a file, or emit them as diagnostic "remarks".

The patch adds the support for building optinfo instances from dump_*
calls, but leaves implementing any *users* of them to followup patches.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o.
	* coretypes.h (class symtab_node): New forward decl.
	(struct cgraph_node): New forward decl.
	(class varpool_node): New forward decl.
	* dump-context.h: New file.
	* dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
	"tree-pass.h", "optinfo-internal.h".
	(refresh_dumps_are_enabled): Use optinfo_enabled_p.
	(set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
	(set_alt_dump_file): Likewise.
	(dump_context::~dump_context): New dtor.
	(dump_gimple_stmt): Move implementation to...
	(dump_context::dump_gimple_stmt): ...this new member function.
	Add the stmt to any pending optinfo, creating one if need be.
	(dump_gimple_stmt_loc): Move implementation to...
	(dump_context::dump_gimple_stmt_loc): ...this new member function.
	Convert param "loc" from location_t to const dump_location_t &.
	Start a new optinfo and add the stmt to it.
	(dump_generic_expr): Move implementation to...
	(dump_context::dump_generic_expr): ...this new member function.
	Add the tree to any pending optinfo, creating one if need be.
	(dump_generic_expr_loc): Move implementation to...
	(dump_context::dump_generic_expr_loc): ...this new member
	function.  Add the tree to any pending optinfo, creating one if
	need be.
	(dump_printf): Move implementation to...
	(dump_context::dump_printf_va): ...this new member function.  Add
	the text to any pending optinfo, creating one if need be.
	(dump_printf_loc): Move implementation to...
	(dump_context::dump_printf_loc_va): ...this new member function.
	Convert param "loc" from location_t to const dump_location_t &.
	Start a new optinfo and add the stmt to it.
	(dump_dec): Move implementation to...
	(dump_context::dump_dec): ...this new member function.  Add the
	value to any pending optinfo, creating one if need be.
	(dump_context::dump_symtab_node): New member function.
	(dump_context::get_scope_depth): New member function.
	(dump_context::begin_scope): New member function.
	(dump_context::end_scope): New member function.
	(dump_context::ensure_pending_optinfo): New member function.
	(dump_context::begin_next_optinfo): New member function.
	(dump_context::end_any_optinfo): New member function.
	(dump_context::s_current): New global.
	(dump_context::s_default): New global.
	(dump_scope_depth): Delete global.
	(dumpfile_ensure_any_optinfo_are_flushed): New function.
	(dump_symtab_node): New function.
	(get_dump_scope_depth): Reimplement in terms of dump_context.
	(dump_begin_scope): Likewise.
	(dump_end_scope): Likewise.
	(selftest::temp_dump_context::temp_dump_context): New ctor.
	(selftest::temp_dump_context::~temp_dump_context): New dtor.
	(selftest::assert_is_text): New support function.
	(selftest::assert_is_tree): New support function.
	(selftest::assert_is_gimple): New support function.
	(selftest::test_capture_of_dump_calls): New test.
	(selftest::dumpfile_c_tests): Call it.
	* dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block,
	dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc,
	dump_gimple_stmt, dump_dec): Gather these related decls and add a
	descriptive comment.
	(dump_function, print_combine_total_stats, enable_rtl_dump_file,
	dump_node, dump_bb): Move these unrelated decls.
	(class dump_manager): Add leading comment.
	* ggc-page.c (ggc_collect): Call
	dumpfile_ensure_any_optinfo_are_flushed.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::optinfo_cc_tests.
	* selftest.h (selftest::optinfo_cc_tests): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/coretypes.h          |   7 +
 gcc/dump-context.h       | 128 ++++++++++++
 gcc/dumpfile.c           | 498 +++++++++++++++++++++++++++++++++++++++++++----
 gcc/dumpfile.h           |  82 +++++---
 gcc/ggc-page.c           |   2 +
 gcc/optinfo-internal.h   | 148 ++++++++++++++
 gcc/optinfo.cc           | 251 ++++++++++++++++++++++++
 gcc/optinfo.h            | 175 +++++++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 11 files changed, 1233 insertions(+), 61 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 66c8b6e..7d36a77 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1426,6 +1426,7 @@ OBJS = \
 	optabs-libfuncs.o \
 	optabs-query.o \
 	optabs-tree.o \
+	optinfo.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..ed0e825 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@ struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
new file mode 100644
index 0000000..753f714
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,128 @@
+/* Support code for handling the various dump_* calls in dumpfile.h
+   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_DUMP_CONTEXT_H
+#define GCC_DUMP_CONTEXT_H 1
+
+/* A class for handling the various dump_* calls.
+
+   In particular, this class has responsibility for consolidating
+   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
+   calls), and emitting them.
+
+   Putting this in a class (rather than as global state) allows
+   for selftesting of this code.  */
+
+class dump_context
+{
+  friend class temp_dump_context;
+ public:
+  static dump_context &get () { return *s_current; }
+
+  ~dump_context ();
+
+  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
+			     const dump_location_t &loc,
+			     dump_flags_t extra_dump_flags,
+			     gimple *gs, int spc);
+
+  void dump_generic_expr (dump_flags_t dump_kind,
+			  dump_flags_t extra_dump_flags,
+			  tree t);
+
+  void dump_generic_expr_loc (dump_flags_t dump_kind,
+			      const dump_location_t &loc,
+			      dump_flags_t extra_dump_flags,
+			      tree t);
+
+  void dump_printf_va (dump_flags_t dump_kind, const char *format,
+		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
+
+  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+			   const char *format, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0);
+
+  template<unsigned int N, typename C>
+  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
+
+  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
+
+  /* Managing nested scopes.  */
+  unsigned int get_scope_depth () const;
+  void begin_scope (const char *name, const dump_location_t &loc);
+  void end_scope ();
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool forcibly_enable_optinfo_p () const
+  {
+    return m_forcibly_enable_optinfo;
+  }
+
+  void end_any_optinfo ();
+
+ private:
+  optinfo &ensure_pending_optinfo ();
+  optinfo &begin_next_optinfo (const dump_location_t &loc);
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool m_forcibly_enable_optinfo;
+
+  /* The current nesting depth of dump scopes, for showing nesting
+     via indentation).  */
+  unsigned int m_scope_depth;
+
+  /* The optinfo currently being accumulated since the last dump_*_loc call,
+     if any.  */
+  optinfo *m_pending;
+
+  /* The currently active dump_context, for use by the dump_* API calls.  */
+  static dump_context *s_current;
+
+  /* The default active context.  */
+  static dump_context s_default;
+};
+
+#if CHECKING_P
+
+/* An RAII-style class for use in selftests for temporarily using a different
+   dump_context.  */
+
+class temp_dump_context
+{
+ public:
+  temp_dump_context (bool forcibly_enable_optinfo);
+  ~temp_dump_context ();
+
+  /* Support for selftests.  */
+  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+
+ private:
+  dump_context m_context;
+  dump_context *m_saved;
+  bool m_saved_flag_remarks;
+};
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 5f69f9b..6e089ef 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -33,6 +33,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h" /* for dump_user_location_t ctor.  */
 #include "rtl.h" /* for dump_user_location_t ctor.  */
 #include "selftest.h"
+#include "optinfo.h"
+#include "dump-context.h"
+#include "cgraph.h"
+#include "tree-pass.h" /* for "current_pass".  */
+#include "optinfo-internal.h" /* for selftests.  */
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -64,7 +69,7 @@ bool dumps_are_enabled = false;
 static void
 refresh_dumps_are_enabled ()
 {
-  dumps_are_enabled = (dump_file || alt_dump_file);
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
 }
 
 /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
@@ -73,6 +78,7 @@ refresh_dumps_are_enabled ()
 void
 set_dump_file (FILE *new_dump_file)
 {
+  dumpfile_ensure_any_optinfo_are_flushed ();
   dump_file = new_dump_file;
   refresh_dumps_are_enabled ();
 }
@@ -83,6 +89,7 @@ set_dump_file (FILE *new_dump_file)
 static void
 set_alt_dump_file (FILE *new_alt_dump_file)
 {
+  dumpfile_ensure_any_optinfo_are_flushed ();
   alt_dump_file = new_alt_dump_file;
   refresh_dumps_are_enabled ();
 }
@@ -458,25 +465,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Implementation of dump_context member functions.  */
+
+/* dump_context's dtor.  */
+
+dump_context::~dump_context ()
+{
+  delete m_pending;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
 void
-dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
+				dump_flags_t extra_dump_flags,
+				gimple *gs, int spc)
 {
   if (dump_file && (dump_kind & pflags))
     print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs, int spc)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -490,20 +516,35 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
    DUMP_KIND is enabled.  */
 
 void
-dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		   tree t)
+dump_context::dump_generic_expr (dump_flags_t dump_kind,
+				 dump_flags_t extra_dump_flags,
+				 tree t)
 {
   if (dump_file && (dump_kind & pflags))
       print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, extra_dump_flags);
+    }
 }
 
 
@@ -511,8 +552,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
    location.  */
 
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
+				     const dump_location_t &loc,
+				     dump_flags_t extra_dump_flags,
+				     tree t)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -526,53 +569,82 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       dump_loc (dump_kind, alt_dump_file, srcloc);
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, extra_dump_flags);
+    }
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
+			      va_list ap)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
 
 void
-dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		 const char *format, ...)
+dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+				  const char *format, va_list ap)
 {
   location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
       dump_loc (dump_kind, dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
       dump_loc (dump_kind, alt_dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
@@ -580,7 +652,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 
 template<unsigned int N, typename C>
 void
-dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
   STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
   signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
@@ -589,6 +661,203 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
+    }
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", node->dump_name ());
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", node->dump_name ());
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
+    }
+}
+
+/* Get the current dump scope-nesting depth.
+   For use by -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+dump_context::get_scope_depth () const
+{
+  return m_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Emit a "scope" optinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_context::begin_scope (const char *name, const dump_location_t &loc)
+{
+  /* Specialcase, to avoid going through dump_printf_loc,
+     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
+
+  if (dump_file)
+    {
+      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      fprintf (dump_file, "=== %s ===\n", name);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      end_any_optinfo ();
+      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
+      info.add_printf ("=== %s ===", name);
+      info.emit ();
+    }
+
+  m_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_context::end_scope ()
+{
+  end_any_optinfo ();
+  m_scope_depth--;
+}
+
+/* Return the optinfo currently being accumulated, creating one if
+   necessary.  */
+
+optinfo &
+dump_context::ensure_pending_optinfo ()
+{
+  if (!m_pending)
+    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
+  return *m_pending;
+}
+
+/* Start a new optinfo and return it, ending any optinfo that was already
+   accumulated.  */
+
+optinfo &
+dump_context::begin_next_optinfo (const dump_location_t &loc)
+{
+  end_any_optinfo ();
+  gcc_assert (m_pending == NULL);
+  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
+  return *m_pending;
+}
+
+/* End any optinfo that has been accumulated within this context; emitting
+   it to any destinations as appropriate - though none have currently been
+   implemented.  */
+
+void
+dump_context::end_any_optinfo ()
+{
+  if (m_pending)
+    m_pending->emit ();
+  delete m_pending;
+  m_pending = NULL;
+}
+
+/* The current singleton dump_context, and its default.  */
+
+dump_context *dump_context::s_current = &dump_context::s_default;
+dump_context dump_context::s_default;
+
+/* Implementation of dump_* API calls, calling into dump_context
+   member functions.  */
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_stmt, except additionally print source location.  */
+
+void
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
+   DUMP_KIND is enabled.  */
+
+void
+dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		   tree t)
+{
+  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
+}
+
+/* Similar to dump_generic_expr, except additionally print the source
+   location.  */
+
+void
+dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		       dump_flags_t extra_dump_flags, tree t)
+{
+  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
+					      t);
+}
+
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
+void
+dump_printf (dump_flags_t dump_kind, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  va_end (ap);
+}
+
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
+
+void
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		 const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  va_end (ap);
+}
+
+/* Output VALUE in decimal to appropriate dump streams.  */
+
+template<unsigned int N, typename C>
+void
+dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+{
+  dump_context::get ().dump_dec (dump_kind, value);
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
-/* The current dump scope-nesting depth.  */
+/* Emit and delete the currently pending optinfo, if there is one,
+   without the caller needing to know about class dump_context.  */
+
+void
+dumpfile_ensure_any_optinfo_are_flushed ()
+{
+  dump_context::get().end_any_optinfo ();
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
 
-static int dump_scope_depth;
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  dump_context::get ().dump_symtab_node (dump_kind, node);
+}
 
 /* Get the current dump scope-nesting depth.
-   For use by dump_*_loc (for showing nesting via indentation).  */
+   For use by -fopt-info (for showing nesting via indentation).  */
 
 unsigned int
 get_dump_scope_depth ()
 {
-  return dump_scope_depth;
+  return dump_context::get ().get_scope_depth ();
 }
 
 /* Push a nested dump scope.
    Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
    destination, if any.
+   Emit a "scope" opinfo if optinfos are enabled.
    Increment the scope depth.  */
 
 void
 dump_begin_scope (const char *name, const dump_location_t &loc)
 {
-  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
-  dump_scope_depth++;
+  dump_context::get ().begin_scope (name, loc);
 }
 
 /* Pop a nested dump scope.  */
@@ -627,7 +909,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc)
 void
 dump_end_scope ()
 {
-  dump_scope_depth--;
+  dump_context::get ().end_scope ();
 }
 
 /* Start a dump for PHASE. Store user-supplied dump flags in
@@ -1180,6 +1462,24 @@ enable_rtl_dump_file (void)
 
 #if CHECKING_P
 
+/* temp_dump_context's ctor.  Temporarily override the dump_context
+   (to forcibly enable optinfo-generation).  */
+
+temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
+: m_context (),
+  m_saved (&dump_context ().get ())
+{
+  dump_context::s_current = &m_context;
+  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
+}
+
+/* temp_dump_context's dtor.  Restore the saved dump_context.  */
+
+temp_dump_context::~temp_dump_context ()
+{
+  dump_context::s_current = m_saved;
+}
+
 namespace selftest {
 
 /* Verify that the dump_location_t constructors capture the source location
@@ -1216,12 +1516,136 @@ test_impl_location ()
 #endif
 }
 
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+static void
+assert_is_text (const optinfo_item *item, const char *expected_text)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
+  const optinfo_item_text * text_item
+    = static_cast <const optinfo_item_text *> (item);
+  ASSERT_STREQ (text_item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+static void
+assert_is_tree (const optinfo_item *item, tree expected_tree,
+		dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
+  const optinfo_item_tree * tree_item
+    = static_cast <const optinfo_item_tree *> (item);
+  ASSERT_EQ (tree_item->get_node (), expected_tree);
+  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+static void
+assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
+		  dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
+  const optinfo_item_gimple * gimple_item
+    = static_cast <const optinfo_item_gimple *> (item);
+  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
+  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls ()
+{
+  dump_location_t loc;
+
+  /* Tree, via dump_generic_expr.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    assert_is_text (info->get_item (0), "test of tree: ");
+    assert_is_tree (info->get_item (1), integer_zero_node, TDF_SLIM);
+  }
+
+  /* Tree, via dump_generic_expr_loc.  */
+  {
+    temp_dump_context tmp (true);
+    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_tree (info->get_item (0), integer_one_node, TDF_SLIM);
+  }
+
+  /* Gimple.  */
+  {
+    greturn *stmt = gimple_build_return (NULL);
+
+    temp_dump_context tmp (true);
+    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
+
+    /* Verify that optinfo instances are flushed if a GC is about to
+       happen (and thus don't need to be GTY-marked).
+       We don't want them in the PCH file, but we don't want the
+       items to have their data collected from under them.  */
+    selftest::forcibly_ggc_collect ();
+    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);
+  }
+
+  /* poly_int.  */
+  {
+    temp_dump_context tmp (true);
+    dump_dec (MSG_NOTE, poly_int64 (42));
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_text (info->get_item (0), "42");
+  }
+
+  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
+     above.  */
+  {
+    /* MSG_OPTIMIZED_LOCATIONS.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_SUCCESS);
+    }
+
+    /* MSG_MISSED_OPTIMIZATION.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_FAILURE);
+    }
+  }
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 dumpfile_c_tests ()
 {
   test_impl_location ();
+  test_capture_of_dump_calls ();
 }
 
 } // namespace selftest
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 0e588a6..899bb89 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,30 +420,6 @@ extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
-extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
-extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
-			     const char *, ...) ATTRIBUTE_PRINTF_3;
-extern void dump_function (int phase, tree fn);
-extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
-				   dump_flags_t, tree);
-extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
-				  dump_flags_t, gimple *, int);
-extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
-
-template<unsigned int N, typename C>
-void dump_dec (dump_flags_t, const poly_int<N, C> &);
-
-/* In tree-dump.c  */
-extern void dump_node (const_tree, dump_flags_t, FILE *);
-
-/* In combine.c  */
-extern void dump_combine_total_stats (FILE *);
-/* In cfghooks.c  */
-extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
 
 /* Global variables used to communicate with passes.  */
 extern FILE *dump_file;
@@ -461,6 +437,49 @@ dump_enabled_p (void)
   return dumps_are_enabled;
 }
 
+/* The following API calls (which *don't* take a "FILE *")
+   write the output to zero or more locations:
+   (a) the active dump_file, if any
+   (b) the -fopt-info destination, if any
+   (c) to the "optinfo" destinations, if any:
+
+   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
+                                   |
+                                   +--> (b) alt_dump_file
+                                   |
+                                   `--> (c) optinfo
+                                            `---> optinfo destinations
+
+   For optinfos, the dump_*_loc mark the beginning of an optinfo
+   instance: all subsequent dump_* calls are consolidated into
+   that optinfo, until the next dump_*_loc call (or a change in
+   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
+
+   A group of dump_* calls should be guarded by:
+
+     if (dump_enabled_p ())
+
+   to minimize the work done for the common case where dumps
+   are disabled.  */
+
+extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
+extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
+			     const char *, ...) ATTRIBUTE_PRINTF_3;
+extern void dump_function (int phase, tree fn);
+extern void dump_basic_block (dump_flags_t, basic_block, int);
+extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
+extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
+				   dump_flags_t, tree);
+extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
+				  dump_flags_t, gimple *, int);
+extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
+
+template<unsigned int N, typename C>
+void dump_dec (dump_flags_t, const poly_int<N, C> &);
+
+extern void dumpfile_ensure_any_optinfo_are_flushed ();
+
 /* Managing nested scopes, so that dumps can express the call chain
    leading to a dump message.  */
 
@@ -500,8 +519,23 @@ class auto_dump_scope
 #define AUTO_DUMP_SCOPE(NAME, LOC) \
   auto_dump_scope scope (NAME, LOC)
 
+extern void dump_function (int phase, tree fn);
+extern void print_combine_total_stats (void);
+extern bool enable_rtl_dump_file (void);
+
+/* In tree-dump.c  */
+extern void dump_node (const_tree, dump_flags_t, FILE *);
+
+/* In combine.c  */
+extern void dump_combine_total_stats (FILE *);
+/* In cfghooks.c  */
+extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
+
 namespace gcc {
 
+/* A class for managing all of the various dump files used by the
+   optimization passes.  */
+
 class dump_manager
 {
 public:
diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
index 51783e5..f3a2119 100644
--- a/gcc/ggc-page.c
+++ b/gcc/ggc-page.c
@@ -2177,6 +2177,8 @@ ggc_collect (void)
   if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)
     return;
 
+  dumpfile_ensure_any_optinfo_are_flushed ();
+
   timevar_push (TV_GC);
   if (!quiet_flag)
     fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024);
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..8144174
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,148 @@
+/* Implementation details of optinfo.
+   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_OPTINFO_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Abstract base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+  void trim_trailing_whitespace ();
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node, dump_flags_t dump_flags)
+    : m_node (node), m_dump_flags (dump_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  tree m_node;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
+    : m_stmt (stmt), m_dump_flags (dump_flags) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  gimple *m_stmt;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..1da7d37
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,251 @@
+/* Optimization information.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "optinfo-internal.h"
+#include "dump-context.h"
+#include "selftest.h"
+
+/* Remove any trailing whitespace characters from this text item.
+   Primarily for use in stripping trailing newline characters when
+   emitting remarks (since the diagnostic subsystem doesn't expect
+   trailing newlines in messages).  */
+
+void
+optinfo_item_text::trim_trailing_whitespace ()
+{
+  size_t len = strlen (m_text);
+  if (len == 0)
+    return;
+
+  size_t new_len = len;
+  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
+    new_len--;
+
+  if (new_len == len)
+    return;
+
+  if (m_owned)
+    m_text[new_len] = '\0';
+  else
+    {
+      m_text = xstrndup (m_text, new_len);
+      m_owned = true;
+    }
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit ()
+{
+  /* Eliminate any trailing whitespace.  */
+  while (m_items.length () > 0)
+    {
+      optinfo_item *last_item = m_items[m_items.length () - 1];
+      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
+	break;
+
+      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
+      last_text->trim_trailing_whitespace ();
+
+      if (strlen (last_text->get_text ()) > 0)
+	break;
+
+      m_items.pop ();
+      delete last_item;
+    }
+
+  /* currently this is a no-op.  */
+}
+
+/* Update the optinfo's kind based on DUMP_KIND.  */
+
+void
+optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    m_kind = OPTINFO_KIND_NOTE;
+}
+
+/* Append a string literal to this optinfo.  */
+
+void
+optinfo::add_string (const char *str)
+{
+  optinfo_item *item
+    = new optinfo_item_text (const_cast <char *> (str), false);
+  m_items.safe_push (item);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  add_printf_va (format, ap);
+  va_end (ap);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf_va (const char *format, va_list ap)
+{
+  char *formatted_text = xvasprintf (format, ap);
+  optinfo_item *item
+    = new optinfo_item_text (formatted_text, true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo.  */
+
+void
+optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
+}
+
+/* Append a tree node to this optinfo.  */
+
+void
+optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
+}
+
+/* Append a symbol table node to this optinfo.  */
+
+void
+optinfo::add_symtab_node (symtab_node *node)
+{
+  m_items.safe_push (new optinfo_item_symtab_node (node));
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   optinfo.  */
+
+void
+optinfo::add_dec (const wide_int_ref &wi, signop sgn)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (wi, buf, sgn);
+  optinfo_item *item
+    = new optinfo_item_text (xstrdup (buf), true);
+  m_items.safe_push (item);
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  /* Currently no destinations are implemented, just a hook for
+     selftests.  */
+  return dump_context::get ().forcibly_enable_optinfo_p ();
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return false;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that optinfo_item_text::trim_trailing_whitespace turns
+   INPUT into EXPECTED.  */
+
+static void
+test_trim_trailing_whitespace (const char *input, const char *expected)
+{
+  optinfo_item_text item (const_cast <char *> (input), false);
+  item.trim_trailing_whitespace ();
+  ASSERT_STREQ (item.get_text (), expected);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_cc_tests ()
+{
+  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
+  test_trim_trailing_whitespace ("", "");
+  test_trim_trailing_whitespace ("\n", "");
+  test_trim_trailing_whitespace ("foo", "foo");
+  test_trim_trailing_whitespace ("foo\n", "foo");
+  test_trim_trailing_whitespace ("foo\n\n", "foo");
+  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..0d49823
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,175 @@
+/* Optimization information.
+   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_OPTINFO_H
+#define GCC_OPTINFO_H
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations, such as:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file as an "optimization record"
+
+   Currently no such destinations are implemented.
+
+   They are generated in response to calls to the "dump_*" API in
+   dumpfile.h; repeated calls to the "dump_*" API are consolidated
+   into a pending optinfo instance, with a "dump_*_loc" starting a new
+   optinfo instance.
+
+   The data sent to the dump calls are captured within the pending optinfo
+   instance as a sequence of optinfo_items.  For example, given:
+
+      if (dump_enabled_p ())
+        {
+          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                           "not vectorized: live stmt not supported: ");
+          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+        }
+
+   the "dump_printf_loc" call begins a new optinfo containing two items:
+   (1) a text item containing "not vectorized: live stmt not supported: "
+   (2) a gimple item for "stmt"
+
+   Dump destinations are thus able to access rich metadata about the
+   items when the optinfo is emitted to them, rather than just having plain
+   text.  For example, when saving the above optinfo to a file as an
+   "optimization record", the record could capture the source location of
+   "stmt" above, rather than just its textual form.
+
+   The currently pending optinfo is emitted and deleted:
+   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
+   * when the dump files are changed (at the end of a pass), or
+   * when a garbage collection is about to happen.  This safety measure
+     ensures that no GC happens during the lifetime of an optinfo instance,
+     and thus that any optinfo_items within the optinfo instances can refer
+     to GC-allocated objects without needing to be GTY-marked: they will never
+     refer to collected garbage.  optinfo and optinfo_item are not GTY-marked
+     as it would make no sense for them to be in PCH files.
+
+   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
+   instances), so all usage should be guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.  */
+
+
+/* Forward decls.  */
+struct opt_pass;
+class optinfo_item; /* optinfo-internal.h.  */
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.  */
+
+class optinfo
+{
+  friend class dump_context;
+
+ public:
+  optinfo (const dump_location_t &loc,
+	   enum optinfo_kind kind,
+	   opt_pass *pass)
+  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
+  {}
+  ~optinfo ();
+
+  const dump_user_location_t &
+  get_user_location () const { return m_loc.get_user_location (); }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_loc.get_impl_location (); }
+
+  enum optinfo_kind get_kind () const { return m_kind; }
+  opt_pass *get_pass () const { return m_pass; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const { return m_loc.get_location_t (); }
+  profile_count get_count () const { return m_loc.get_count (); }
+
+ private:
+  void emit ();
+
+  /* Pre-canned ways of manipulating the optinfo, for use by friend class
+     dump_context.  */
+  void handle_dump_file_kind (dump_flags_t);
+  void add_string (const char *str);
+  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
+  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
+  void add_tree (tree node, dump_flags_t dump_flags);
+  void add_symtab_node (symtab_node *node);
+  void add_dec (const wide_int_ref &wi, signop sgn);
+
+  template<unsigned int N, typename C>
+  void add_poly_int (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      add_dec (value.coeffs[0], sgn);
+    else
+      {
+	add_string ("[");
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    add_dec (value.coeffs[i], sgn);
+	    add_string (i == N - 1 ? "]" : ",");
+	  }
+      }
+  }
+
+ private:
+  dump_location_t m_loc;
+  enum optinfo_kind m_kind;
+  opt_pass *m_pass;
+  auto_vec <optinfo_item *> m_items;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 7f4d6f3..989c50a 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,7 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
+  optinfo_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 54fc488..48881c9 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -228,6 +228,7 @@ extern void gimple_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void optinfo_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 2/2] optinfo: add diagnostic remarks
  2018-07-02 20:51                         ` [PATCH 0/2] v4: optinfo framework and remarks David Malcolm
@ 2018-07-02 20:51                           ` David Malcolm
  2018-07-09 13:05                             ` Richard Biener
  2018-07-02 20:51                           ` [PATCH 1/2] " David Malcolm
  1 sibling, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-02 20:51 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, David Malcolm

This patch adds the first destination for optinfo instances to be
emitted to: as "remarks" through the diagnostics subsystem.

Examples can be seen at
  https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html

Remarks look a lot like the output of -fopt-info, with the following
differences:

* announcing "In function 'blah':" etc when the function changes.

* printing the corresponding source lines (unless
  "-fno-diagnostics-show-caret"), as per other diagnostics, and

* colorizing the various parts of the message if stderr is at a tty.

* showing extra metadata:
  * the pass that emitted the remark,
  * the execution count of the code in question
  * which file/line/function of GCC emitted the remark

* possibly allowing for remarks to be used in DejaGnu tests to better
  associate testing of an optimization with the source line in
  question, rather than relying on a scan of the dumpfile (which is
  per-source file and thus rather "coarse-grained").*

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

(see the notes in the cover letter about the state of this patch;
posting for motivation of the optinfo stuff).

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo-emit-diagnostics.o.
	* common.opt (fremarks): New option.
	(fdiagnostics-show-remark-hotness): New option.
	(fdiagnostics-show-remark-origin): New option.
	(fdiagnostics-show-remark-pass): New option.
	* diagnostic-color.c (color_dict): Add "remark", as bold green.
	* diagnostic-core.h (remark): New decl.
	* diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
	(remark): New function.
	* diagnostic.def (DK_REMARK): New diagnostic kind.
	* doc/invoke.texi (Remarks): New section.
	(-fremarks): New option.
	(-fno-diagnostics-show-remark-hotness): New option.
	(-fno-diagnostics-show-remark-origin): New option.
	(-fno-diagnostics-show-remark-pass): New option.
	* dumpfile.c (dump_context::get_scope_depth): Update comment.
	(dump_context::end_any_optinfo): Likewise.
	* dumpfile.h: Update comment.
	* opt-functions.awk (function): Handle "Remark" by adding
	CL_REMARK.
	* optinfo-emit-diagnostics.cc: New file.
	* optinfo-emit-diagnostics.h: New file.
	* optinfo.cc: Include "optinfo-emit-diagnostics.h".
	(optinfo::emit): Call emit_optinfo_as_diagnostic_remark.
	(optinfo_enabled_p): Use flag_remarks.
	* optinfo.h: Update comment.
	* opts.c (print_specific_help): Handle CL_REMARK.
	(common_handle_option): Likewise.
	* opts.h (CL_REMARK): New macro.
	(CL_MAX_OPTION_CLASS): Update for CL_REMARK.
	(CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD)
	(CL_PCH_IGNORE): Likewise.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::optinfo_emit_diagnostics_cc_tests.
	* selftest.h (selftest::optinfo_emit_diagnostics_cc_tests): New
	decl.

gcc/fortran/ChangeLog:
	* gfc-diagnostic.def (DK_REMARK): New diagnostic kind.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
	remarks_plugin.c.
	* gcc.dg/plugin/remarks-1.c: New test.
	* gcc.dg/plugin/remarks_plugin.c: New test plugin.
	* lib/gcc-dg.exp (dg-remark): New function.
---
 gcc/Makefile.in                              |   1 +
 gcc/common.opt                               |  17 ++
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 ++
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  67 ++++++
 gcc/dumpfile.c                               |   5 +-
 gcc/dumpfile.h                               |   2 +
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++++++++++++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 +++
 gcc/optinfo.cc                               |   9 +-
 gcc/optinfo.h                                |   4 +-
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/profile-count.c                          |  28 +++
 gcc/profile-count.h                          |   5 +
 gcc/selftest-run-tests.c                     |   1 +
 gcc/selftest.h                               |   1 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 +++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 +++++++++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 25 files changed, 701 insertions(+), 16 deletions(-)
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7d36a77..232cae4 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1427,6 +1427,7 @@ OBJS = \
 	optabs-query.o \
 	optabs-tree.o \
 	optinfo.o \
+	optinfo-emit-diagnostics.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 5a50bc27..908432e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -510,6 +510,11 @@ Driver Negative(Qn)
 R
 Driver Joined Separate
 
+fremarks
+Common Remark Var(flag_remarks)
+Emit diagnostic remarks about optimizations
+FIXME: should this be -fdiagnostics-foo or somesuch?
+
 S
 Driver
 
@@ -1281,6 +1286,18 @@ fdiagnostics-show-option
 Common Var(flag_diagnostics_show_option) Init(1)
 Amend appropriate diagnostic messages with the command line option that controls them.
 
+fdiagnostics-show-remark-hotness
+Common Var(flag_diagnostics_show_remark_hotness) Init(1)
+When emitting optimization remarks, show the execution count of the code being optimized.
+
+fdiagnostics-show-remark-origin
+Common Var(flag_diagnostics_show_remark_origin) Init(1)
+When emitting optimization remarks, show which line of GCC code produced the remark.
+
+fdiagnostics-show-remark-pass
+Common Var(flag_diagnostics_show_remark_pass) Init(1)
+When emitting optimization remarks, show which optimization pass produced the remark.
+
 fdisable-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 disables an optimization pass.
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 3ee21bc..bcc3767 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
+  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
+	       6, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index aa5807e..63166b8 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool remark (location_t, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index e22c17b..97ed88b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
     {
     case DK_DEBUG:
     case DK_NOTE:
+    case DK_REMARK:
     case DK_ANACHRONISM:
     case DK_WARNING:
       break;
@@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   return ret;
 }
 
+/* Emit an optimization remark at LOCATION.  This is used by the optinfo
+   framework.
+   Return true if the remark was printed, false if it was inhibited.  */
+// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
+
+bool
+remark (location_t location, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  rich_location richloc (line_table, location);
+  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
index ce3dc56..b58095d 100644
--- a/gcc/diagnostic.def
+++ b/gcc/diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 248e603..1a3c1a6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
 * Debugging Options::   Producing debuggable code.
 * Optimize Options::    How much optimization?
 * Instrumentation Options:: Enabling profiling and extra run-time error checking.
+* Remarks::             Details on how your code is being optimized.
 * Preprocessor Options:: Controlling header files and macro definitions.
                          Also, getting dependency information for Make.
 * Assembler Options::   Passing options to the assembler.
@@ -471,6 +472,11 @@ Objective-C and Objective-C++ Dialects}.
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
 
+@item Remarks
+@xref{Remarks,,Options to Control Remarks from the Compiler}.
+@gccoptlist{-fremarks -fno-diagnostics-show-remark-hotness @gol
+-fno-diagnostics-show-remark-origin -fno-diagnostics-show-remark-pass}
+
 @item Preprocessor Options
 @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
 @gccoptlist{-A@var{question}=@var{answer} @gol
@@ -12040,6 +12046,67 @@ The NOP instructions are inserted at---and maybe before, depending on
 @end table
 
 
+@node Remarks
+@section Options to Control Remarks from the Compiler
+@cindex remarks
+@cindex options, remarks
+
+These options are aimed at advanced users who may be interested
+in seeing additional diagnostics from the compiler, giving information
+on the decisions it is making on the code.
+
+The precise messages and their format are subject to change.
+
+@table @gcctabopt
+@item -fremarks
+@opindex fremarks
+Emit diagnostic remarks about optimizations.
+
+Enabling this option leads to GCC emitting diagnostics detailing the
+optimization decisions it is making.
+
+For example, this message:
+
+@smallexample
+test.c:13:3: remark:   Symbolic number of iterations is '(unsigned int) n_8(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
+@end smallexample
+
+describes an internal detail of the ``vect'' optimization pass, acting at
+the given source location within ``test.c'', where the remark was emitted
+by the function ``vect_analyze_loop_form'' at line 1387 of GCC's source
+file ``tree-vect-loop.c''.
+
+@item -fno-diagnostics-show-remark-hotness
+@opindex fno-diagnostics-show-remark-hotness
+@opindex fdiagnostics-show-remark-hotness
+By default, if diagnostic remarks are enabled, they include information
+on the execution count, or ``hotness'', of the code being optimized:
+the ``[count(precise)=76800000]'' in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-origin
+@opindex fno-diagnostics-show-remark-origin
+@opindex fdiagnostics-show-remark-origin
+By default, if diagnostic remarks are enabled, they include information
+on where in GCC's own source code (or a plugin's source code) the remark
+is being emitted from; the
+``[../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@item -fno-diagnostics-show-remark-pass
+@opindex fno-diagnostics-show-remark-pass
+@opindex fdiagnostics-show-remark-pass
+By default, if diagnostic remarks are enabled, they include information
+on which optimization pass emitted the remark: the ``[pass=vect]''
+in the example above.
+
+This option suppresses this part of the remark.
+
+@end table
+
 @node Preprocessor Options
 @section Options Controlling the Preprocessor
 @cindex preprocessor options
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 6e089ef..955fd57 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -690,7 +690,7 @@ dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
 }
 
 /* Get the current dump scope-nesting depth.
-   For use by -fopt-info (for showing nesting via indentation).  */
+   For use by remarks and -fopt-info (for showing nesting via indentation).  */
 
 unsigned int
 dump_context::get_scope_depth () const
@@ -766,8 +766,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc)
 }
 
 /* End any optinfo that has been accumulated within this context; emitting
-   it to any destinations as appropriate - though none have currently been
-   implemented.  */
+   it to any destinations as appropriate, such as a diagnostic "remark".  */
 
 void
 dump_context::end_any_optinfo ()
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 899bb89..514efe3 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -442,6 +442,7 @@ dump_enabled_p (void)
    (a) the active dump_file, if any
    (b) the -fopt-info destination, if any
    (c) to the "optinfo" destinations, if any:
+       (c.1) as diagnostic "remarks"
 
    dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                    |
@@ -449,6 +450,7 @@ dump_enabled_p (void)
                                    |
                                    `--> (c) optinfo
                                             `---> optinfo destinations
+                                                  (c.1) diagnostic "remarks"
 
    For optinfos, the dump_*_loc mark the beginning of an optinfo
    instance: all subsequent dump_* calls are consolidated into
diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
index 565fa83..a10b6aa 100644
--- a/gcc/fortran/gfc-diagnostic.def
+++ b/gcc/fortran/gfc-diagnostic.def
@@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
 DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
 DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
+DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
 DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
 /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
 prefix does not matter.  */
diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
index 2c371e5..48ecac5 100644
--- a/gcc/opt-functions.awk
+++ b/gcc/opt-functions.awk
@@ -105,6 +105,7 @@ function switch_flags (flags)
 	  test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
 	  test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
 	  test_flag("Warning", flags,  " | CL_WARNING") \
+	  test_flag("Remark", flags,  " | CL_REMARK") \
 	  test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
 	sub( "^0 \\| ", "", result )
 	return result
diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
new file mode 100644
index 0000000..5320379
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.cc
@@ -0,0 +1,317 @@
+/* Emit optimization information as "remark" diagnostics.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "gimple.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "options.h"
+#include "cgraph.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-diagnostics.h"
+#include "optinfo-internal.h"
+#include "dump-context.h"
+#include "selftest.h"
+#include "context.h"
+#include "pass_manager.h"
+
+/* Print the items within OPTINFO to PP (as part of a remark).  */
+
+static void
+print_optinfo_items (pretty_printer *pp, const optinfo *optinfo)
+{
+  bool show_color = pp_show_color (pp);
+
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *as_text
+	      = (const optinfo_item_text *)item;
+	    pp_string (pp, as_text->get_text ());
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *as_tree
+	      = (const optinfo_item_tree *)item;
+	    pp_begin_quote (pp, show_color);
+	    dump_generic_node (pp, as_tree->get_node (), 0, TDF_DETAILS,
+			       false);
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    const optinfo_item_gimple *as_gimple
+	      = (const optinfo_item_gimple *)item;
+	    gimple *stmt = as_gimple->get_stmt ();
+	    pp_begin_quote (pp, show_color);
+	    pp_gimple_stmt_1 (pp, stmt, 0, TDF_SLIM);
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    const optinfo_item_symtab_node *as_symtab_node
+	      = (const optinfo_item_symtab_node *)item;
+	    symtab_node *node = as_symtab_node->get_node ();
+	    pp_begin_quote (pp, show_color);
+	    pp_string (pp, node->dump_name ());
+	    pp_end_quote (pp, show_color);
+	  }
+	  break;
+	}
+    }
+}
+
+/* Print PASS to PP (as part of a remark).  */
+
+static void
+print_pass (pretty_printer *pp, opt_pass *pass)
+{
+  if (pass == NULL)
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp, colorize_start (show_color,
+				 diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_string (pp, "pass=");
+  pp_string (pp, pass->name);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print COUNT to PP (as part of a remark).  */
+
+static void
+print_count (pretty_printer *pp, profile_count count)
+{
+  if (!count.initialized_p ())
+    return;
+
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp,
+	     colorize_start (show_color,
+			     diagnostic_get_color_for_kind (DK_NOTE)));
+  pp_string (pp, "count(");
+  pp_string (pp, profile_quality_as_string (count.quality ()));
+  pp_string (pp, ")=");
+  pp_scalar (pp, "%li", count.to_gcov_type ());
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print IMPL_LOC to PP (as part of a remark).  */
+
+static void
+print_impl_location (pretty_printer *pp, const dump_impl_location_t &impl_loc)
+{
+  bool show_color = pp_show_color (pp);
+
+  pp_string (pp, " [");
+  pp_string (pp,
+	     colorize_start (show_color,
+			     diagnostic_get_color_for_kind (DK_REMARK)));
+  pp_printf (pp, "%s:%i", impl_loc.m_file, impl_loc.m_line);
+  if (impl_loc.m_function)
+    pp_printf (pp, ":%s", impl_loc.m_function);
+  pp_string (pp, colorize_stop (show_color));
+  pp_string (pp, "]");
+}
+
+/* Print OPTINFO to PP.  */
+
+static void
+print_optinfo_as_remark (pretty_printer *pp, const optinfo *optinfo)
+{
+  /* Start with scope-based indentation.  */
+  for (unsigned i = get_dump_scope_depth (); i > 0; i--)
+    pp_space (pp);
+
+  /* Print the items into PP.  */
+  print_optinfo_items (pp, optinfo);
+
+  /* Add metadata: which pass?  */
+  if (flag_diagnostics_show_remark_pass)
+    print_pass (pp, optinfo->get_pass ());
+
+  /* Add metadata: hotness.  */
+  if (flag_diagnostics_show_remark_hotness)
+    print_count (pp, optinfo->get_count ());
+
+  /* Add metadata: where was this emitted from.  */
+  if (flag_diagnostics_show_remark_origin)
+    print_impl_location (pp, optinfo->get_impl_location ());
+}
+
+/* Subclass of pretty_printer for building up the text of a remark.  */
+
+class remark_printer : public pretty_printer
+{
+public:
+  remark_printer (bool show_color_);
+};
+
+/* remark_printer's ctor.  */
+
+remark_printer::remark_printer (bool show_color_)
+{
+  pp_needs_newline (this) = true;
+  pp_translate_identifiers (this) = false;
+  pp_show_color (this) = show_color_;
+}
+
+/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
+
+void
+emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
+{
+  if (!flag_remarks)
+    return;
+
+  remark_printer pp (pp_show_color (global_dc->printer));
+
+  print_optinfo_as_remark (&pp, optinfo);
+
+  const char *msg = pp_formatted_text (&pp);
+  location_t loc = optinfo->get_location_t ();
+
+  remark (loc, 0, "%s", msg);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that print_optinfo_items works.  */
+
+static void
+test_print_optinfo_items ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+  optinfo *info = tmp.get_pending_optinfo ();
+  ASSERT_TRUE (info != NULL);
+
+  /* Avoid introducing locale-specific differences in the results
+     by hardcoding open_quote and close_quote.  */
+  auto_fix_quotes fix_quotes;
+
+  remark_printer pp (false);
+  print_optinfo_items (&pp, info);
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, "test of tree: `0'");
+}
+
+/* Verify that print_pass works.  */
+
+static void
+test_print_pass ()
+{
+  /* NULL pass.  */
+  {
+    remark_printer pp (false);
+    print_pass (&pp, NULL);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, "");
+  }
+
+  /* Non-NULL pass.  */
+  {
+    remark_printer pp (false);
+    opt_pass *pass = make_pass_ipa_increase_alignment (NULL);
+    print_pass (&pp, pass);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [pass=increase_alignment]");
+    delete pass;
+  }
+}
+
+/* Verify that print_count works.  */
+
+static void
+test_print_count ()
+{
+  remark_printer pp (false);
+  print_count (&pp, profile_count ());
+  const char *msg = pp_formatted_text (&pp);
+  ASSERT_STREQ (msg, " [count(uninitialized)=0]");
+}
+
+/* Verify that print_impl_location works.  */
+
+static void
+test_print_impl_location ()
+{
+  /* Non-NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, "funcname");
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42:funcname]");
+  }
+
+  /* NULL function.  */
+  {
+    remark_printer pp (false);
+    dump_impl_location_t loc ("foo.c", 42, NULL);
+    print_impl_location (&pp, loc);
+    const char *msg = pp_formatted_text (&pp);
+    ASSERT_STREQ (msg, " [foo.c:42]");
+  }
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_diagnostics_cc_tests ()
+{
+  test_print_optinfo_items ();
+  test_print_pass ();
+  test_print_count ();
+  test_print_impl_location ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
new file mode 100644
index 0000000..820cefd
--- /dev/null
+++ b/gcc/optinfo-emit-diagnostics.h
@@ -0,0 +1,26 @@
+/* Emit optimization information as "remark" diagnostics.
+   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_OPTINFO_EMIT_DIAGNOSTICS_H
+#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
+
+extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index 1da7d37..bc86696 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "optinfo.h"
 #include "optinfo-internal.h"
+#include "optinfo-emit-diagnostics.h"
 #include "dump-context.h"
 #include "selftest.h"
 
@@ -112,7 +113,8 @@ optinfo::emit ()
       delete last_item;
     }
 
-  /* currently this is a no-op.  */
+  /* -fremarks.  */
+  emit_optinfo_as_diagnostic_remark (this);
 }
 
 /* Update the optinfo's kind based on DUMP_KIND.  */
@@ -203,9 +205,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn)
 
 bool optinfo_enabled_p ()
 {
-  /* Currently no destinations are implemented, just a hook for
-     selftests.  */
-  return dump_context::get ().forcibly_enable_optinfo_p ();
+  return (dump_context::get ().forcibly_enable_optinfo_p ()
+	  || flag_remarks);
 }
 
 /* Return true if any of the active optinfo destinations make use
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index 0d49823..8a8f812 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -27,9 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 
    * as a "remark" through the diagnostics subsystem
 
-   * saved to a file as an "optimization record"
-
-   Currently no such destinations are implemented.
+   * saved to a file as an "optimization record" (not yet implemented)
 
    They are generated in response to calls to the "dump_*" API in
    dumpfile.h; repeated calls to the "dump_*" API are consolidated
diff --git a/gcc/opts.c b/gcc/opts.c
index ed102c0..5cc8801 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
 	case CL_WARNING:
 	  description = _("The following options control compiler warning messages");
 	  break;
+	case CL_REMARK:
+	  description = _("The following options control compiler remarks");
+	  break;
 	case CL_OPTIMIZATION:
 	  description = _("The following options control optimizations");
 	  break;
@@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
 	      { "optimizers", CL_OPTIMIZATION },
 	      { "target", CL_TARGET },
 	      { "warnings", CL_WARNING },
+	      { "remarks", CL_REMARK },
 	      { "undocumented", CL_UNDOCUMENTED },
 	      { "params", CL_PARAMS },
 	      { "joined", CL_JOINED },
diff --git a/gcc/opts.h b/gcc/opts.h
index 3c4065ea..d9df788 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -137,20 +137,21 @@ extern const unsigned int cl_lang_count;
 #define CL_DRIVER		(1U << 19) /* Driver option.  */
 #define CL_TARGET		(1U << 20) /* Target-specific option.  */
 #define CL_COMMON		(1U << 21) /* Language-independent.  */
+#define CL_REMARK		(1U << 22) /* Enables an (optional) remark.  */
 
 #define CL_MIN_OPTION_CLASS	CL_PARAMS
-#define CL_MAX_OPTION_CLASS	CL_COMMON
+#define CL_MAX_OPTION_CLASS	CL_REMARK
 
 /* From here on the bits describe attributes of the options.
    Before this point the bits have described the class of the option.
    This distinction is important because --help will not list options
    which only have these higher bits set.  */
 
-#define CL_JOINED		(1U << 22) /* If takes joined argument.  */
-#define CL_SEPARATE		(1U << 23) /* If takes a separate argument.  */
-#define CL_UNDOCUMENTED		(1U << 24) /* Do not output with --help.  */
-#define CL_NO_DWARF_RECORD	(1U << 25) /* Do not add to producer string.  */
-#define CL_PCH_IGNORE		(1U << 26) /* Do compare state for pch.  */
+#define CL_JOINED		(1U << 23) /* If takes joined argument.  */
+#define CL_SEPARATE		(1U << 24) /* If takes a separate argument.  */
+#define CL_UNDOCUMENTED		(1U << 25) /* Do not output with --help.  */
+#define CL_NO_DWARF_RECORD	(1U << 26) /* Do not add to producer string.  */
+#define CL_PCH_IGNORE		(1U << 27) /* Do compare state for pch.  */
 
 /* Flags for an enumerated option argument.  */
 #define CL_ENUM_CANONICAL	(1 << 0) /* Canonical for this value.  */
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@ enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@ public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 989c50a..c0acf19 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -73,6 +73,7 @@ selftest::run_tests ()
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
   optinfo_cc_tests ();
+  optinfo_emit_diagnostics_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 48881c9..1594d1d 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -229,6 +229,7 @@ extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void optinfo_cc_tests ();
+extern void optinfo_emit_diagnostics_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5a19fc9..1f0a079 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -96,6 +96,8 @@ set plugin_test_list [list \
 	  must-tail-call-2.c } \
     { expensive_selftests_plugin.c \
 	  expensive-selftests-1.c } \
+    { remarks_plugin.c \
+	  remarks-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
new file mode 100644
index 0000000..9139b9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-fremarks" } */
+
+extern void test_string_literal (void);
+extern void test_tree (void);
+extern void test_gimple (int);
+extern void test_cgraph_node (void);
+extern void test_printf (void);
+extern void test_wide_int (void);
+extern void test_poly_int (void);
+extern void test_scopes (void);
+
+void test_remarks (void)
+{
+  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
+  test_tree (); /* { dg-remark "test of tree: '0'" } */
+  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
+  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
+  test_printf (); /* { dg-remark "test of optinfo printf: 42" } */
+  test_wide_int (); /* { dg-remark "test of wide int: 0" } */
+  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
+
+  test_scopes (); /* { dg-line test_scopes_line } */
+  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
+  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
new file mode 100644
index 0000000..332bba6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
@@ -0,0 +1,152 @@
+/* Test of remark-emission by optinfo.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "optinfo.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cgraph.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_remarks =
+{
+  GIMPLE_PASS, /* type */
+  "test_remarks", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_remarks : public gimple_opt_pass
+{
+public:
+  pass_test_remarks(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_remarks, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_remarks
+
+unsigned int
+pass_test_remarks::execute (function *fun)
+{
+  basic_block bb;
+
+  if (!dump_enabled_p ())
+    return 0;
+  
+  FOR_ALL_BB_FN (bb, fun)
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+	 !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+	gimple *stmt = gsi_stmt (gsi);
+	gcall *call = dyn_cast <gcall *> (stmt);
+	if (!call)
+	  continue;
+	tree callee_decl = gimple_call_fndecl (call);
+	if (!callee_decl)
+	  continue;
+	tree callee_name = DECL_NAME (callee_decl);
+	if (!callee_name)
+	  continue;
+	const char *callee = IDENTIFIER_POINTER (callee_name);
+
+	/* Various optinfo tests, done at callsites,
+	   controlled by the callee name.  */
+	if (strcmp (callee, "test_string_literal") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of remark for ");
+	    dump_printf (MSG_NOTE, callee);
+	  }
+	else if (strcmp (callee, "test_tree") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of tree: ");
+	    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+	  }
+	else if (strcmp (callee, "test_gimple") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of gimple: ");
+	    dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
+	  }
+	else if (strcmp (callee, "test_cgraph_node") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of callgraph node: ");
+	    dump_symtab_node (MSG_NOTE, cgraph_node::get (callee_decl));
+	  }
+	else if (strcmp (callee, "test_printf") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of optinfo printf: %d", 42);
+	  }
+	else if (strcmp (callee, "test_wide_int") == 0)
+	  {
+	    HOST_WIDE_INT val = 0;
+	    dump_printf_loc (MSG_NOTE, stmt,
+			     "test of wide int: " HOST_WIDE_INT_PRINT_DEC,
+			     val);
+	  }
+	else if (strcmp (callee, "test_poly_int") == 0)
+	  {
+	    dump_printf_loc (MSG_NOTE, stmt, "test of poly int: ");
+	    dump_dec (MSG_NOTE, poly_int64 (42));
+	  }
+	else if (strcmp (callee, "test_scopes") == 0)
+	  {
+	    AUTO_DUMP_SCOPE ("outer scope", stmt);
+	    {
+	      dump_printf_loc (MSG_NOTE, stmt, "at outer scope");
+	      AUTO_DUMP_SCOPE ("middle scope", stmt);
+	      {
+		dump_printf_loc (MSG_NOTE, stmt, "at middle scope");
+		AUTO_DUMP_SCOPE ("innermost scope", stmt);
+		dump_printf_loc (MSG_NOTE, stmt, "at innermost scope");
+	      }
+	    }
+	  }
+      }
+
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_remarks (gcc::context *ctxt)
+{
+  return new pass_test_remarks (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  pass_info.pass = make_pass_test_remarks (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index a15c5d5..906ee3b 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -1154,6 +1154,15 @@ proc dg-locus { args } {
     verbose "process-message:\n${dg-messages}" 2
 }
 
+# Handle remarks.
+
+proc dg-remark { args } {
+    # Make this variable available here and to the saved proc.
+    upvar dg-messages dg-messages
+
+    process-message saved-dg-error "remark: " "$args"
+}
+
 # Check the existence of a gdb in the path, and return true if there
 # is one.
 #
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 0/2] v4: optinfo framework and remarks
  2018-06-29  7:14                       ` Richard Biener
@ 2018-07-02 20:51                         ` David Malcolm
  2018-07-02 20:51                           ` [PATCH 2/2] optinfo: add diagnostic remarks David Malcolm
  2018-07-02 20:51                           ` [PATCH 1/2] " David Malcolm
  0 siblings, 2 replies; 80+ messages in thread
From: David Malcolm @ 2018-07-02 20:51 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, David Malcolm

On Fri, 2018-06-29 at 09:09 +0200, Richard Biener wrote:
> On Thu, Jun 28, 2018 at 4:29 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > On Thu, 2018-06-28 at 13:29 +0200, Richard Biener wrote:
> > > On Tue, Jun 26, 2018 at 3:54 PM David Malcolm <dmalcolm@redhat.co
> > > m>
> > > wrote:
> > > > 
> > > > On Mon, 2018-06-25 at 15:34 +0200, Richard Biener wrote:
> > > > > On Wed, Jun 20, 2018 at 6:34 PM David Malcolm <dmalcolm@redha
> > > > > t.co
> > > > > m>
> > > > > wrote:
> > > > > > 
> > > > > > Here's v3 of the patch (one big patch this time, rather
> > > > > > than a
> > > > > > kit).
> > > > > > 
> > > > > > Like the v2 patch kit, this patch reuses the existing dump
> > > > > > API,
> > > > > > rather than inventing its own.
> > > > > > 
> > > > > > Specifically, it uses the dump_* functions in dumpfile.h
> > > > > > that
> > > > > > don't
> > > > > > take a FILE *, the ones that implicitly write to dump_file
> > > > > > and/or
> > > > > > alt_dump_file.  I needed a name for them, so I've taken to
> > > > > > calling
> > > > > > them the "structured dump API" (better name ideas welcome).
> > > > > > 
> > > > > > v3 eliminates v2's optinfo_guard class, instead using
> > > > > > "dump_*_loc"
> > > > > > calls as delimiters when consolidating "dump_*"
> > > > > > calls.  There's
> > > > > > a
> > > > > > new dump_context class which has responsibility for
> > > > > > consolidating
> > > > > > them into optimization records.
> > > > > > 
> > > > > > The dump_*_loc calls now capture more than just a
> > > > > > location_t:
> > > > > > they
> > > > > > capture the profile_count and the location in GCC's own
> > > > > > sources
> > > > > > where
> > > > > > the dump is being emitted from.
> > > > > > 
> > > > > > This works by introducing a new "dump_location_t" class as
> > > > > > the
> > > > > > argument of those dump_*_loc calls.  The dump_location_t
> > > > > > can
> > > > > > be constructed from a gimple * or from an rtx_insn *, so
> > > > > > that
> > > > > > rather than writing:
> > > > > > 
> > > > > >   dump_printf_loc (MSG_NOTE, gimple_location (stmt),
> > > > > >                    "some message: %i", 42);
> > > > > > 
> > > > > > you can write:
> > > > > > 
> > > > > >   dump_printf_loc (MSG_NOTE, stmt,
> > > > > >                    "some message: %i", 42);
> > > > > > 
> > > > > > and the dump_location_t constructor will grab the
> > > > > > location_t
> > > > > > and
> > > > > > profile_count of stmt, and the location of the
> > > > > > "dump_printf_loc"
> > > > > > callsite (and gracefully handle "stmt" being NULL).
> > > > > > 
> > > > > > Earlier versions of the patch captured the location of the
> > > > > > dump_*_loc call via preprocessor hacks, or didn't work
> > > > > > properly;
> > > > > > this version of the patch works more cleanly: internally,
> > > > > > dump_location_t is split into two new classes:
> > > > > >   * dump_user_location_t: the location_t and profile_count
> > > > > > within
> > > > > >     the *user's code*, and
> > > > > >   * dump_impl_location_t: the __builtin_FILE/LINE/FUNCTION
> > > > > > within
> > > > > >     the *implementation* code (i.e. GCC or a plugin),
> > > > > > captured
> > > > > >     "automagically" via default params
> > > > > > 
> > > > > > These classes are sometimes used elsewhere in the
> > > > > > code.  For
> > > > > > example, "vect_location" becomes a dump_user_location_t
> > > > > > (location_t and profile_count), so that in e.g:
> > > > > > 
> > > > > >   vect_location = find_loop_location (loop);
> > > > > > 
> > > > > > it's capturing the location_t and profile_count, and then
> > > > > > when
> > > > > > it's used here:
> > > > > > 
> > > > > >   dump_printf_loc (MSG_NOTE, vect_location, "foo");
> > > > > > 
> > > > > > the dump_location_t is constructed from the vect_location
> > > > > > plus the dump_impl_location_t at that callsite.
> > > > > > 
> > > > > > In contrast, loop-unroll.c's report_unroll's "locus" param
> > > > > > becomes a dump_location_t: we're interested in where it was
> > > > > > called from, not in the locations of the various dump_*_loc
> > > > > > calls
> > > > > > within it.
> > > > > > 
> > > > > > Previous versions of the patch captured a gimple *, and
> > > > > > needed
> > > > > > GTY markers; in this patch, the dump_user_location_t is now
> > > > > > just a
> > > > > > location_t and a profile_count.
> > > > > > 
> > > > > > The v2 patch added an overload for dump_printf_loc so that
> > > > > > you
> > > > > > could pass in either a location_t, or the new type; this
> > > > > > version
> > > > > > of the patch eliminates that: they all now take
> > > > > > dump_location_t.
> > > > > > 
> > > > > > Doing so required adding support for rtx_insn *, so that
> > > > > > one
> > > > > > can
> > > > > > write this kind of thing in RTL passes:
> > > > > > 
> > > > > >   dump_printf_loc (MSG_NOTE, insn, "foo");
> > > > > > 
> > > > > > One knock-on effect is that get_loop_location now returns a
> > > > > > dump_user_location_t rather than a location_t, so that it
> > > > > > has
> > > > > > hotness information.
> > > > > > 
> > > > > > Richi: would you like me to split out this location-
> > > > > > handling
> > > > > > code into a separate patch?  (It's kind of redundant
> > > > > > without
> > > > > > adding the remarks and optimization records work, but if
> > > > > > that's
> > > > > > easier I can do it)
> > > > > 
> > > > > I think that would be easier because it doesn't require the
> > > > > JSON
> > > > > stuff and so I'll happily approve it.
> > > > > 
> > > > > Thus - trying to review that bits (and sorry for the delay).
> > > > > 
> > > > > +  location_t srcloc = loc.get_location_t ();
> > > > > +
> > > > >    if (dump_file && (dump_kind & pflags))
> > > > >      {
> > > > > -      dump_loc (dump_kind, dump_file, loc);
> > > > > +      dump_loc (dump_kind, dump_file, srcloc);
> > > > >        print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > > > > extra_dump_flags);
> > > > >      }
> > > > > 
> > > > >    if (alt_dump_file && (dump_kind & alt_flags))
> > > > >      {
> > > > > -      dump_loc (dump_kind, alt_dump_file, loc);
> > > > > +      dump_loc (dump_kind, alt_dump_file, srcloc);
> > > > >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags
> > > > > |
> > > > > extra_dump_flags);
> > > > >      }
> > > > > +
> > > > > +  if (optinfo_enabled_p ())
> > > > > +    {
> > > > > +      optinfo &info = begin_next_optinfo (loc);
> > > > > +      info.handle_dump_file_kind (dump_kind);
> > > > > +      info.add_stmt (gs, extra_dump_flags);
> > > > > +    }
> > > > > 
> > > > > seeing this in multiple places.  I seem to remember that
> > > > > dump_file / alt_dump_file was suposed to handle dumping
> > > > > into two locations - a dump file and optinfo (or
> > > > > stdout).  This
> > > > > looks
> > > > > like the optinfo "stream" is even more separate.  Could that
> > > > > obsolete the alt_dump_file stream?  I'd need to review
> > > > > existing
> > > > > stuff
> > > > > in more detail to answer but maybe you already know from
> > > > > recently
> > > > > digging into this.
> > > > 
> > > > Possibly.  I attempted this in v1 of the patch, but it was
> > > > mixed in
> > > > with
> > > > a bunch of other stuff.  I'll have another go at doing this.
> > > > 
> > > > > Oh, and all the if (optinfo_enable_p ()) stuff is for the
> > > > > followup
> > > > > then, right?
> > > > 
> > > > Yes.
> > > > 
> > > > > I like the boiler-plate changes to dump_* using stuff a lot,
> > > > > so
> > > > > the
> > > > > infrastructure to do that (the location wrapping) and these
> > > > > boiler-
> > > > > plate
> > > > > changes are pre-approved if split out.
> > > > 
> > > > Thanks.  I split out the location wrapping, and have committed
> > > > it
> > > > to
> > > > trunk (r262149).  It's not clear to me exactly what other parts
> > > > you
> > > > liked,
> > > > so I'm going to try to split out more of the non-JSON bits in
> > > > the
> > > > hope that some parts are good enough as-is, and I'll post them
> > > > for
> > > > review
> > > > as followups.
> > > > 
> > > > For reference, here's what I've committed (I added some obvious
> > > > changes
> > > > to doc/optinfo.texi).
> > > > 
> > > > > I think the *_REMARK stuff should get attention of the
> > > > > respective
> > > > > maintainers - not sure what the difference between NOTE and
> > > > > REMARK
> > > > > is ;)
> > > > 
> > > > Me neither :)  I think it might come down to "this is purely a
> > > > debugging
> > > > message for a pass maintainer" (MSG_NOTE) vs "this is a high-
> > > > level
> > > > thing
> > > > that an advanced user want to see" (MSG_REMARK???).
> > > > 
> > > > One idea that occurred to me: are we overusing
> > > > dump_flags_t?  It's
> > > > a
> > > > mixture of TDF_* bitfields (used by our internal dumps) plus
> > > > MSG_*
> > > > bitfields (used with -fopt-info).  IIRC the only TDF_ bitfield
> > > > that's
> > > > ever used with the MSG_* bitfields is TDF_DETAILS.  Might it
> > > > make
> > > > sense
> > > > to split out the MSG_* bitfields into a different type
> > > > (dump_level_t???)
> > > > to reinforce this split?  (and thus the dump_* entrypoints that
> > > > don't take
> > > > a FILE * would take this new type).  I'm not sure if this is a
> > > > good
> > > > idea
> > > > or not.
> > > 
> > > Making it even more complicated doesn't make it easier to use it
> > > "correctly".  So I'd rather try to simplify it.  How passes use
> > > TDF_DETAILS vs. non-details is already highly inconsistent.  This
> > > is why I liked the original optinfo work because it somehow made
> > > user-interesting vs. developer-interesting with the same API
> > > (and to the same dump-file).  Just that MSG_NOTE is exposed to
> > > users while I think it should be developer-only...
> > > 
> > > IMHO the TDF_ stuff should control things at the stmt/tree/BB
> > > level,
> > > thus be IL specific flags while the MSG_ stuff should control
> > > pass-specific dumping detail.
> > 
> > You mention user-interesting vs developer-interesting, and whether
> > it
> > would be the same API and/or the same dump-file.
> > 
> > I've posted a bunch of patches here, some small, some large, trying
> > various different appoaches, coming at the problem from at least
> > two directions, so maybe it's worth discussing the overall
> > direction
> > here (with some ASCII art!)
> > 
> > * what is the status quo?
> > * what do we want to achieve?
> > * how do we get there?
> > 
> > Status quo:
> > * We have the "dump_*(MSG_*)" API which hides the destination of
> >   where we're dumping to (currently dump_file and/or alt_file_file,
> >   conditionalized via flags).
> > 
> >   dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
> >                                     |
> >                                     `--> alt_dump_file
> > 
> > * As of r262149 (the dump_location_t commit) dumpfile.c receives
> > the
> >   hotness and emission location of each dump_*_loc call, but
> > currently
> >   it does nothing with that information.
> > * The messages that we emit through the "dump_*(MSG_*)" API and
> > their
> >   "levels" are currently rather haphazard.  Some are close to being
> >   suitable for end-users, but most seem never intended to be
> >   end-user-facing.  For reference:
> >     grep -nH -e MSG_MISSED_OPTIMIZATION *.c *.cc | wc -l
> >     452
> >     grep -nH -e MSG_NOTE *.c *.cc | wc -l
> >     551
> >     grep -nH -e MSG_OPTIMIZED_LOCATIONS *.c *.cc | wc -l
> >     39
> >   (though some of these are support code e.g. in dumpfile.c, rather
> >   than uses).
> 
> Yep.  I believe MSG_NOTE was the fallout of optinfo introduction and
> my
> request to share the same API with dumping to the dumpfile.  MSG_NOTE
> just received "anything else" ...
> 
> > * The API builds up messages programatically, which is hostile to
> >   i18n.  The API isn't marked as needing i18n for its strings
> > (which
> >   IIRC is via using "gmsgid" as a param name being special-cased in
> >   the gettext toolchain).
> > 
> > What do we want to achieve?
> > * I want end-users to be able to enable high-level dump messages
> > about
> >   optimizations, and for those messages to be decipherable without
> >   reading GCC sources e.g. the opt_problem idea posted here:
> >   * "[PATCH] [RFC] Higher-level reporting of vectorization
> > problems"
> >     * https://gcc.gnu.org/ml/gcc-patches/2018-06/msg01462.html
> >   Presumably such messages would need to be
> > internationalized.  Many
> >   other messages are really internal only, and would be a burden to
> >   impose on our translators.
> > * I want to support additional destinations for any/all dump
> > messages
> >   beyond just the dump file and -fopt-info:
> >   * putting them through the diagnostics subsystem (as "remarks")
> >   * storing them to disk in a machine-readable format, so that
> >     3rd-party tools can present useful visualizations of how the
> >     user's code is being optimized, e.g. prioritized by hotness
> > * I want to preserve the existing API and most existing uses of it
> >   (to minimize churn and thus keep our lives easier)
> > 
> > The patches I've been posting so far add additional dump
> > destinations,
> > like this:
> > 
> >   dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
> >                                     |
> >                                     +--> alt_dump_file
> >                                     |
> >                                     +--> diagnostic "remarks"
> >                                     |
> >                                     +--> "optimization records"
> >                                          (saved to disk)
> > 
> > I believe you favor an approach of "MSG_NOTE is internal, whereas
> > MSG_MISSED_OPTIMIZATION and MSG_OPTIMIZED_LOCATIONS are for end
> > users".
> 
> Yeah, kind of.  You introduced another MSG_ variant 

"MSG_REMARK" above, though I'm not sure I like that name.

> and in the end we might
> need to go that way.  I guess renaming MSG_NOTE to MSG_DEBUG would
> be a step in the direction to reflect reality ;)

I think I want to look more at the "opt_problem" idea about high-level
vectorization messages, and attack the problem from that direction;
maybe that will suggest a good way forward for our existing dump messages.

> > I think the "should this dump message be internationalized?" issue
> > introduces a natural separation between dump messages aimed at
> > end-users vs purely "internal" dumps; this suggests to me that
> > we need a new API for such dump messages, designed to support i18n,
> > and marked as such for gettext, so I favor something like this:
> > 
> >   SOURCES                                DESTINATIONS
> >   dump_* (MSG_*) >---> dumpfile.c --+--> dump_file
> >                    |                |
> >   new dump API -->-+                +--> alt_dump_file
> >                                     |
> >                                     +--> diagnostic "remarks"
> >                                     |
> >                                     +--> "optimization records"
> >                                          (saved to disk)
> > 
> > (I'm deliberately being vague about what this i18n-enabled dump API
> > might look like)
> 
> I know I threw i18n into the discussion - but I'd say we should focus
> on other details first.  I think there's no need to _require_ the
> "debug"
> dumpfile stuff not be translated, but to make it easier for
> translators
> I suppose being able to "amend" the API calls with a "do not
> translate"
> variant would be OK.

I'm thinking that only a small fraction of the dump messages will be
user-facing, so the default would be "do not translate" with the rare
special-case being "do translate".

> So in what way is the dump_* API not suitable for translation
> that the diagnostic machinery (warning/error) does not suffer from?

Consider this example, from "vect_create_data_ref_ptr" (I used this
example way back in the v1 patch kit):

  if (dump_enabled_p ())
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       dump_printf_loc (MSG_NOTE, vect_location,
                        "create %s-pointer variable to type: ",
                        get_tree_code_name (TREE_CODE (aggr_type)));
       dump_generic_expr (MSG_NOTE, TDF_SLIM, aggr_type);
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing an array ref: ");
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a vector ref: ");
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         dump_printf (MSG_NOTE, "  vectorizing a record based array ref: ");
       else
         dump_printf (MSG_NOTE, "  vectorizing a pointer ref: ");
       dump_generic_expr (MSG_NOTE, TDF_SLIM, DR_BASE_OBJECT (dr));
       dump_printf (MSG_NOTE, "\n");
     }

where there are two problems with translation:
(a) the message is built up piecewise, with conditional logic.
(b) there's nothing yet marking the string fragments as needing
    translation

> That is, if we internally make dump_* go through the pretty-printers
> we can handle stuff like quoting or even stmts/expressions.  I think
> that would be useful for dumping to dumpfiles as well.

That could work.  Currently dump_printf* use fprintf internally, but I
don't think we make much use of the codes there.  So we could make it
use a pretty_printer internally.  The core pretty-print.c code doesn't
handle any of the interesting middle-end types we'd want to talk about
(gimple, tree, symtab_node *, etc), so I think to have a custom
middle-end dump pp_format_decoder callback.  The above example could
then look something like:

  if (dump_enabled_p ())
     {
       tree dr_base_type = TREE_TYPE (DR_BASE_OBJECT (dr));
       const char *msgid;
       if (TREE_CODE (dr_base_type) == ARRAY_TYPE)
         msgid = ("create %s-pointer variable to type: %T"
                  "  vectorizing an array ref: %T");
       else if (TREE_CODE (dr_base_type) == VECTOR_TYPE)
         msgid = ("create %s-pointer variable to type: %T"
                  "  vectorizing a vector ref: %T");
       else if (TREE_CODE (dr_base_type) == RECORD_TYPE)
         msgid = ("create %s-pointer variable to type: %T"
                  "  vectorizing a record based array ref: %T");
       else
         msgid = ("create %s-pointer variable to type: %T"
                  "  vectorizing a pointer ref: %T");
       dump_printf_loc (MSG_NOTE, vect_location, msgid,
                        get_tree_code_name (TREE_CODE (aggr_type)));
                        aggr_type, DR_BASE_OBJECT (dr));
       dump_printf (MSG_NOTE, "\n");
     }

where I'm using "%T" as the format code for tree with TDF_SLIM.  The
optinfo could could then "print" these more interesting types by
capturing the metadata (and thus expose it in optimization records).

Though, that said, I don't think that message as-is is something we'd
want end-users to see, and thus not something for translators.

Maybe something like:

extern void dump_userfacing_printf_loc (dump_flags_t,
                                        const dump_location_t &,
                                        const char *gmsgid, ...)
  ATTRIBUTE_DUMP_PRINTF_3;

for the small subset of messages that we'd want to be translated?
(with ATTRIBUTE_DUMP_PRINTF_3 to signify that we have a new family
of format codes).

(I'm not in love with that function name)

> > 
> > That said, I have a version of the patch kit which does just this:
> > 
> >   dump_* (MSG_*) ----> dumpfile.c --+--> dump_file
> >                                     |
> >                                     +--> alt_dump_file
> >                                     |
> >                                     +--> diagnostic "remarks"
> > 
> > i.e. generalizing the dump destinations (by adding optinfo
> > internally),
> > but without requiring the JSON support or touching the dump API .
> > Would
> > that be suitable as a next step?
> 
> Yes.  Thanks for going step-by-step btw, this is really useful in
> simplifying
> the picture.
> 
> Richard.

Thanks.

Here's a reduced version of the patch kit which builds on what's
already in trunk.

Patch 1 adds the basic optinfo machinery, consolidating the dump_*
calls into optinfo objects that can be sent to multiple dump
destinations  - but it doesn't add any destinations.  It captures
"rich" information about what is dumped, tagged with things like
code hotness information, enough to eventually implement optimization
records in a followup.

   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                   |
                                   +--> (b) alt_dump_file
                                   |
                                   `--> (c) optinfo

Patch 2 adds in the first destination for them: diagnostic
"remarks" (updated to work on top of patch 1).  It's not perfect
yet (we probably need to be able to filter them using
optinfo_group_t and probably other criteria), and I'm posting it now
to provide more motivation for patch 1, but it's stable enough for
trunk (though a couple of FIXMEs remain).

   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                   |
                                   +--> (b) alt_dump_file
                                   |
                                   `--> (c) optinfo
                                            `---> optinfo destinations
                                                  (c.1) diagnostic "remarks"

I have followups to add JSON optimizations records as another kind
of optinfo destination, so that the picture would become:

   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                   |
                                   +--> (b) alt_dump_file
                                   |
                                   `--> (c) optinfo
                                            `---> optinfo destinations
                                                  (c.1) diagnostic "remarks"
                                                  (c.2) optimization records

(the patches add this ASCII art to dumpfile.h, btw)

As mentioned before, the optinfo consolidation works by making the
dump_*_loc calls implicitly start a new optinfo, terminating and
emitting any pending one.  This avoids having to touch all the dump
code to explicitly terminate messages, but means that optinfo
instances can be pending for a while, until the next dump_*_loc
happens (or the end of the pass, or a GC; see the patch).  Sadly,
this delay seems to make them more suitable for user-facing messages
than for debugging GCC itself.  Because of this, I didn't think it
was appropriate to port the alt_dump_file code to using it (better to
have that be more direct).  But I can do that if you want.
(Alternatively, maybe a dump_printf* call that ends with a newline
should terminate the optinfo?  Doing that would make most of them
terminate themselves)

Dave

David Malcolm (2):
  Add "optinfo" framework
  optinfo: add diagnostic remarks

 gcc/Makefile.in                              |   2 +
 gcc/common.opt                               |  17 +
 gcc/coretypes.h                              |   7 +
 gcc/diagnostic-color.c                       |   2 +
 gcc/diagnostic-core.h                        |   2 +
 gcc/diagnostic.c                             |  17 +
 gcc/diagnostic.def                           |   1 +
 gcc/doc/invoke.texi                          |  67 ++++
 gcc/dump-context.h                           | 128 +++++++
 gcc/dumpfile.c                               | 497 +++++++++++++++++++++++++--
 gcc/dumpfile.h                               |  84 +++--
 gcc/fortran/gfc-diagnostic.def               |   1 +
 gcc/ggc-page.c                               |   2 +
 gcc/opt-functions.awk                        |   1 +
 gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++++++++
 gcc/optinfo-emit-diagnostics.h               |  26 ++
 gcc/optinfo-internal.h                       | 148 ++++++++
 gcc/optinfo.cc                               | 252 ++++++++++++++
 gcc/optinfo.h                                | 173 ++++++++++
 gcc/opts.c                                   |   4 +
 gcc/opts.h                                   |  13 +-
 gcc/profile-count.c                          |  28 ++
 gcc/profile-count.h                          |   5 +
 gcc/selftest-run-tests.c                     |   2 +
 gcc/selftest.h                               |   2 +
 gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
 gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 ++
 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 ++++++++
 gcc/testsuite/lib/gcc-dg.exp                 |   9 +
 29 files changed, 1924 insertions(+), 67 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo-emit-diagnostics.cc
 create mode 100644 gcc/optinfo-emit-diagnostics.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-02 17:00                       ` David Malcolm
  2018-07-02 17:09                         ` Christophe Lyon
@ 2018-07-03  7:37                         ` Richard Biener
  2018-07-03 13:52                           ` David Malcolm
  1 sibling, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-07-03  7:37 UTC (permalink / raw)
  To: David Malcolm; +Cc: christophe.lyon, GCC Patches

On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gmail.
> > com> wrote:
> > >
> > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.com>
> > > wrote:
> > > >
> > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > dump_*_loc
> > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > vectorizer.h,
> > > > so that the nested structure is shown in -fopt-info by
> > > > indentation.
> > > >
> > > > For example, this converts -fopt-info-all e.g. from:
> > > >
> > > > test.c:8:3: note: === analyzing loop ===
> > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > test.c:8:3: note: === get_loop_niters ===
> > > > test.c:8:3: note: symbolic number of iterations is (unsigned int)
> > > > n_9(D)
> > > > test.c:8:3: note: not vectorized: loop contains function calls or
> > > > data references that cannot be analyzed
> > > > test.c:8:3: note: vectorized 0 loops in function
> > > >
> > > > to:
> > > >
> > > > test.c:8:3: note: === analyzing loop ===
> > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > test.c:8:3: note:    === get_loop_niters ===
> > > > test.c:8:3: note:   symbolic number of iterations is (unsigned
> > > > int) n_9(D)
> > > > test.c:8:3: note:   not vectorized: loop contains function calls
> > > > or data references that cannot be analyzed
> > > > test.c:8:3: note: vectorized 0 loops in function
> > > >
> > > > showing that the "symbolic number of iterations" message is
> > > > within
> > > > the "=== analyze_loop_nest ===" (and not within the
> > > > "=== vect_analyze_loop_form ===").
> > > >
> > > > This is also enabling work for followups involving optimization
> > > > records
> > > > (allowing the records to directly capture the nested structure of
> > > > the
> > > > dump messages).
> > > >
> > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > > >
> > > > OK for trunk?
> >
> > Hi,
> >
> > I've noticed that this patch (r262246) caused regressions on aarch64:
> >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-dump
> > vect "note: Built SLP cancelled: can use load/store-lanes"
> >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > cancelled: can use load/store-lanes"
> >
> > The problem is that now there are more spaces between "note:" and
> > "Built", the attached small patch does that for slp-perm-1.c.
>
> Sorry about the breakage.
>
> > Is it the right way of fixing it or do we want to accept any amount
> > of
> > spaces for instance?
>
> I don't think we want to hardcode the amount of space in the dumpfile.
> The idea of my patch was to make the dump more human-readable (I hope)
> by visualizing the nesting structure of the dump messages, but I think
> we shouldn't "bake" that into the expected strings, as someone might
> want to add an intermediate nesting level.
>
> Do we really need to look for the "note:" in the scan-tree-dump?
> Should that directive be rewritten to:
>
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>
> which I believe would match any amount of spaces.
>
> Alternatively a regex accepting any amount of space ought to work, if
> we care that the message begins with "note: ".
>
> The "note: " comes from dumpfile.c's dump_loc, and is emitted
> regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION or
> whatever.  Given that, maybe we should just drop the "note: " prefix
> from these scan-tree-dump expected regexes?  (assuming that works)

I guess it was done for the -fopt-info output channel to match what
we emit with inform () given those are neither warnings nor errors.

But yes, lets elide "note: " from dumpfiles.  Be prepared to fiddle
with expected scan-dumps though.

> > I'm surprised there is such little impact on the testsuite though.
>
> I see lots of scan-tree-dump* directives in the vect part of the
> testsuite, but it seems that only these ones use the "note: " prefix; I
> think everything else was matching against the message whilst ignoring
> the prefix, so it didn't matter when the prefix changed
> (I double-checked and these scan-tree-dump directives didn't trigger on
> my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> vect_load_lanes } }, where I see
> check_effective_target_vect_load_lanes: returning 0 in the log)
>
> > If OK, I'll update the patch to take the other slp-perm-[235678].c
> > tests into account.
> >
> > Thanks,
> >
> > Christophe
>
> Sorry again about the breakage.
> Dave
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-03  7:37                         ` Richard Biener
@ 2018-07-03 13:52                           ` David Malcolm
  2018-07-03 13:53                             ` Richard Biener
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-03 13:52 UTC (permalink / raw)
  To: Richard Biener; +Cc: christophe.lyon, GCC Patches

On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gm
> > > ail.
> > > com> wrote:
> > > > 
> > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.
> > > > com>
> > > > wrote:
> > > > > 
> > > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > > dump_*_loc
> > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > > vectorizer.h,
> > > > > so that the nested structure is shown in -fopt-info by
> > > > > indentation.
> > > > > 
> > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > 
> > > > > test.c:8:3: note: === analyzing loop ===
> > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > test.c:8:3: note: symbolic number of iterations is (unsigned
> > > > > int)
> > > > > n_9(D)
> > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > calls or
> > > > > data references that cannot be analyzed
> > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > 
> > > > > to:
> > > > > 
> > > > > test.c:8:3: note: === analyzing loop ===
> > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > (unsigned
> > > > > int) n_9(D)
> > > > > test.c:8:3: note:   not vectorized: loop contains function
> > > > > calls
> > > > > or data references that cannot be analyzed
> > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > 
> > > > > showing that the "symbolic number of iterations" message is
> > > > > within
> > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > "=== vect_analyze_loop_form ===").
> > > > > 
> > > > > This is also enabling work for followups involving
> > > > > optimization
> > > > > records
> > > > > (allowing the records to directly capture the nested
> > > > > structure of
> > > > > the
> > > > > dump messages).
> > > > > 
> > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-
> > > > > gnu.
> > > > > 
> > > > > OK for trunk?
> > > 
> > > Hi,
> > > 
> > > I've noticed that this patch (r262246) caused regressions on
> > > aarch64:
> > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-
> > > dump
> > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > > cancelled: can use load/store-lanes"
> > > 
> > > The problem is that now there are more spaces between "note:" and
> > > "Built", the attached small patch does that for slp-perm-1.c.
> > 
> > Sorry about the breakage.
> > 
> > > Is it the right way of fixing it or do we want to accept any
> > > amount
> > > of
> > > spaces for instance?
> > 
> > I don't think we want to hardcode the amount of space in the
> > dumpfile.
> > The idea of my patch was to make the dump more human-readable (I
> > hope)
> > by visualizing the nesting structure of the dump messages, but I
> > think
> > we shouldn't "bake" that into the expected strings, as someone
> > might
> > want to add an intermediate nesting level.
> > 
> > Do we really need to look for the "note:" in the scan-tree-dump?
> > Should that directive be rewritten to:
> > 
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use
> > load/store-lanes" "vect" { target { vect_perm3_int &&
> > vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > load/store-lanes" "vect" { target { vect_perm3_int &&
> > vect_load_lanes } } } } */
> > 
> > which I believe would match any amount of spaces.
> > 
> > Alternatively a regex accepting any amount of space ought to work,
> > if
> > we care that the message begins with "note: ".
> > 
> > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION
> > or
> > whatever.  Given that, maybe we should just drop the "note: "
> > prefix
> > from these scan-tree-dump expected regexes?  (assuming that works)
> 
> I guess it was done for the -fopt-info output channel to match what
> we emit with inform () given those are neither warnings nor errors.
> 
> But yes, lets elide "note: " from dumpfiles.  Be prepared to fiddle
> with expected scan-dumps though.

Re-reading, I think I was unclear, but I was proposing removing it from
the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open to
the latter).

> 
> > > I'm surprised there is such little impact on the testsuite
> > > though.
> > 
> > I see lots of scan-tree-dump* directives in the vect part of the
> > testsuite, but it seems that only these ones use the "note: "
> > prefix; I
> > think everything else was matching against the message whilst
> > ignoring
> > the prefix, so it didn't matter when the prefix changed
> > (I double-checked and these scan-tree-dump directives didn't
> > trigger on
> > my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> > vect_load_lanes } }, where I see
> > check_effective_target_vect_load_lanes: returning 0 in the log)
> > 
> > > If OK, I'll update the patch to take the other slp-perm-
> > > [235678].c
> > > tests into account.
> > > 
> > > Thanks,
> > > 
> > > Christophe
> > 
> > Sorry again about the breakage.
> > Dave
> > 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-03 13:52                           ` David Malcolm
@ 2018-07-03 13:53                             ` Richard Biener
  2018-07-03 14:10                               ` [PATCH] Remove "note: " prefix from some scan-tree-dump directives David Malcolm
  2018-07-05  8:42                               ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE Christophe Lyon
  0 siblings, 2 replies; 80+ messages in thread
From: Richard Biener @ 2018-07-03 13:53 UTC (permalink / raw)
  To: David Malcolm; +Cc: christophe.lyon, GCC Patches

On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gm
> > > > ail.
> > > > com> wrote:
> > > > >
> > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.
> > > > > com>
> > > > > wrote:
> > > > > >
> > > > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > > > dump_*_loc
> > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > > > vectorizer.h,
> > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > indentation.
> > > > > >
> > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > >
> > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > test.c:8:3: note: symbolic number of iterations is (unsigned
> > > > > > int)
> > > > > > n_9(D)
> > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > calls or
> > > > > > data references that cannot be analyzed
> > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > >
> > > > > > to:
> > > > > >
> > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > (unsigned
> > > > > > int) n_9(D)
> > > > > > test.c:8:3: note:   not vectorized: loop contains function
> > > > > > calls
> > > > > > or data references that cannot be analyzed
> > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > >
> > > > > > showing that the "symbolic number of iterations" message is
> > > > > > within
> > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > "=== vect_analyze_loop_form ===").
> > > > > >
> > > > > > This is also enabling work for followups involving
> > > > > > optimization
> > > > > > records
> > > > > > (allowing the records to directly capture the nested
> > > > > > structure of
> > > > > > the
> > > > > > dump messages).
> > > > > >
> > > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-
> > > > > > gnu.
> > > > > >
> > > > > > OK for trunk?
> > > >
> > > > Hi,
> > > >
> > > > I've noticed that this patch (r262246) caused regressions on
> > > > aarch64:
> > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-
> > > > dump
> > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > > > cancelled: can use load/store-lanes"
> > > >
> > > > The problem is that now there are more spaces between "note:" and
> > > > "Built", the attached small patch does that for slp-perm-1.c.
> > >
> > > Sorry about the breakage.
> > >
> > > > Is it the right way of fixing it or do we want to accept any
> > > > amount
> > > > of
> > > > spaces for instance?
> > >
> > > I don't think we want to hardcode the amount of space in the
> > > dumpfile.
> > > The idea of my patch was to make the dump more human-readable (I
> > > hope)
> > > by visualizing the nesting structure of the dump messages, but I
> > > think
> > > we shouldn't "bake" that into the expected strings, as someone
> > > might
> > > want to add an intermediate nesting level.
> > >
> > > Do we really need to look for the "note:" in the scan-tree-dump?
> > > Should that directive be rewritten to:
> > >
> > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use
> > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > vect_load_lanes } } } } */
> > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > vect_load_lanes } } } } */
> > >
> > > which I believe would match any amount of spaces.
> > >
> > > Alternatively a regex accepting any amount of space ought to work,
> > > if
> > > we care that the message begins with "note: ".
> > >
> > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION
> > > or
> > > whatever.  Given that, maybe we should just drop the "note: "
> > > prefix
> > > from these scan-tree-dump expected regexes?  (assuming that works)
> >
> > I guess it was done for the -fopt-info output channel to match what
> > we emit with inform () given those are neither warnings nor errors.
> >
> > But yes, lets elide "note: " from dumpfiles.  Be prepared to fiddle
> > with expected scan-dumps though.
>
> Re-reading, I think I was unclear, but I was proposing removing it from
> the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open to
> the latter).

Both is fine with me.

Richard.

> >
> > > > I'm surprised there is such little impact on the testsuite
> > > > though.
> > >
> > > I see lots of scan-tree-dump* directives in the vect part of the
> > > testsuite, but it seems that only these ones use the "note: "
> > > prefix; I
> > > think everything else was matching against the message whilst
> > > ignoring
> > > the prefix, so it didn't matter when the prefix changed
> > > (I double-checked and these scan-tree-dump directives didn't
> > > trigger on
> > > my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> > > vect_load_lanes } }, where I see
> > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > >
> > > > If OK, I'll update the patch to take the other slp-perm-
> > > > [235678].c
> > > > tests into account.
> > > >
> > > > Thanks,
> > > >
> > > > Christophe
> > >
> > > Sorry again about the breakage.
> > > Dave
> > >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH] Remove "note: " prefix from some scan-tree-dump directives
  2018-07-03 13:53                             ` Richard Biener
@ 2018-07-03 14:10                               ` David Malcolm
  2018-07-03 14:11                                 ` Richard Biener
  2018-07-05  8:42                               ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE Christophe Lyon
  1 sibling, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-03 14:10 UTC (permalink / raw)
  To: Richard Biener, christophe.lyon; +Cc: GCC Patches, David Malcolm

On Tue, 2018-07-03 at 15:53 +0200, Richard Biener wrote:
> On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> >
> > On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com
> > > >
> > > wrote:
> > > >
> > > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenthe
> > > > > r@gm
> > > > > ail.
> > > > > com> wrote:
> > > > > >
> > > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@red
> > > > > > hat.
> > > > > > com>
> > > > > > wrote:
> > > > > > >
> > > > > > > This patch adds a concept of nested "scopes" to
> > > > > > > dumpfile.c's
> > > > > > > dump_*_loc
> > > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in
> > > > > > > tree-
> > > > > > > vectorizer.h,
> > > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > > indentation.
> > > > > > >
> > > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > > >
> > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > > test.c:8:3: note: symbolic number of iterations is
> > > > > > > (unsigned
> > > > > > > int)
> > > > > > > n_9(D)
> > > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > > calls or
> > > > > > > data references that cannot be analyzed
> > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > >
> > > > > > > to:
> > > > > > >
> > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > > (unsigned
> > > > > > > int) n_9(D)
> > > > > > > test.c:8:3: note:   not vectorized: loop contains
> > > > > > > function
> > > > > > > calls
> > > > > > > or data references that cannot be analyzed
> > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > >
> > > > > > > showing that the "symbolic number of iterations" message
> > > > > > > is
> > > > > > > within
> > > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > > "=== vect_analyze_loop_form ===").
> > > > > > >
> > > > > > > This is also enabling work for followups involving
> > > > > > > optimization
> > > > > > > records
> > > > > > > (allowing the records to directly capture the nested
> > > > > > > structure of
> > > > > > > the
> > > > > > > dump messages).
> > > > > > >
> > > > > > > Successfully bootstrapped & regrtested on x86_64-pc-
> > > > > > > linux-
> > > > > > > gnu.
> > > > > > >
> > > > > > > OK for trunk?
> > > > >
> > > > > Hi,
> > > > >
> > > > > I've noticed that this patch (r262246) caused regressions on
> > > > > aarch64:
> > > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-
> > > > > tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built
> > > > > SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >
> > > > > The problem is that now there are more spaces between "note:"
> > > > > and
> > > > > "Built", the attached small patch does that for slp-perm-1.c.
> > > >
> > > > Sorry about the breakage.
> > > >
> > > > > Is it the right way of fixing it or do we want to accept any
> > > > > amount
> > > > > of
> > > > > spaces for instance?
> > > >
> > > > I don't think we want to hardcode the amount of space in the
> > > > dumpfile.
> > > > The idea of my patch was to make the dump more human-readable
> > > > (I
> > > > hope)
> > > > by visualizing the nesting structure of the dump messages, but
> > > > I
> > > > think
> > > > we shouldn't "bake" that into the expected strings, as someone
> > > > might
> > > > want to add an intermediate nesting level.
> > > >
> > > > Do we really need to look for the "note:" in the scan-tree-
> > > > dump?
> > > > Should that directive be rewritten to:
> > > >
> > > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can
> > > > use
> > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > vect_load_lanes } } } } */
> > > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > vect_load_lanes } } } } */
> > > >
> > > > which I believe would match any amount of spaces.
> > > >
> > > > Alternatively a regex accepting any amount of space ought to
> > > > work,
> > > > if
> > > > we care that the message begins with "note: ".
> > > >
> > > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > > regardless of whether it's a MSG_NOTE vs a
> > > > MSG_MISSED_OPTIMIZATION
> > > > or
> > > > whatever.  Given that, maybe we should just drop the "note: "
> > > > prefix
> > > > from these scan-tree-dump expected regexes?  (assuming that
> > > > works)
> > >
> > > I guess it was done for the -fopt-info output channel to match
> > > what
> > > we emit with inform () given those are neither warnings nor
> > > errors.
> > >
> > > But yes, lets elide "note: " from dumpfiles.  Be prepared to
> > > fiddle
> > > with expected scan-dumps though.
> >
> > Re-reading, I think I was unclear, but I was proposing removing it
> > from
> > the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open
> > to
> > the latter).
>
> Both is fine with me.
>
> Richard.
>
> > >
> > > > > I'm surprised there is such little impact on the testsuite
> > > > > though.
> > > >
> > > > I see lots of scan-tree-dump* directives in the vect part of
> > > > the
> > > > testsuite, but it seems that only these ones use the "note: "
> > > > prefix; I
> > > > think everything else was matching against the message whilst
> > > > ignoring
> > > > the prefix, so it didn't matter when the prefix changed
> > > > (I double-checked and these scan-tree-dump directives didn't
> > > > trigger on
> > > > my x86_64 testing of the patch, due to { target {
> > > > vect_perm3_int &&
> > > > vect_load_lanes } }, where I see
> > > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > > >
> > > > > If OK, I'll update the patch to take the other slp-perm-
> > > > > [235678].c
> > > > > tests into account.
> > > > >
> > > > > Thanks,
> > > > >
> > > > > Christophe
> > > >
> > > > Sorry again about the breakage.
> > > > Dave

Here's a minimal patch to remove the "note: " prefix from the
affected directives.

Christophe, does this fix the issues seen on aarch64?

OK for trunk?

gcc/testsuite/ChangeLog:
	* gcc.dg/vect/slp-perm-1.c: Remove "note: " prefix from
	scan-tree-dump directive.
	* gcc.dg/vect/slp-perm-2.c: Likewise.
	* gcc.dg/vect/slp-perm-3.c: Likewise.
	* gcc.dg/vect/slp-perm-5.c: Likewise.
	* gcc.dg/vect/slp-perm-6.c: Likewise.
	* gcc.dg/vect/slp-perm-7.c: Likewise.
	* gcc.dg/vect/slp-perm-8.c: Likewise.
---
 gcc/testsuite/gcc.dg/vect/slp-perm-1.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-2.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-3.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-5.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-6.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-7.c | 2 +-
 gcc/testsuite/gcc.dg/vect/slp-perm-8.c | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
index 6bd16ef..ca7803e 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
@@ -82,7 +82,7 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && {! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
 
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
index 4bab348..82776f3 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
@@ -56,6 +56,6 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
index 568e400..1807275 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
@@ -69,7 +69,7 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
 
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
index 5293913..b86a3dc 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
@@ -106,7 +106,7 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
 
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
index 4eb648a..97a0ebf 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
@@ -105,6 +105,6 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
index baf7f78..346411f 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
@@ -98,6 +98,6 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
index 94d4455..17aa111 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
@@ -62,6 +62,6 @@ int main (int argc, const char* argv[])
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target { vect_perm_byte } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_byte && { ! vect_load_lanes } } } } } */
 /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
-/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
+/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
 /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
 /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] Remove "note: " prefix from some scan-tree-dump directives
  2018-07-03 14:10                               ` [PATCH] Remove "note: " prefix from some scan-tree-dump directives David Malcolm
@ 2018-07-03 14:11                                 ` Richard Biener
  2018-07-09  9:03                                   ` Christophe Lyon
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-07-03 14:11 UTC (permalink / raw)
  To: David Malcolm; +Cc: christophe.lyon, GCC Patches

On Tue, Jul 3, 2018 at 4:10 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Tue, 2018-07-03 at 15:53 +0200, Richard Biener wrote:
> > On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > > > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com
> > > > >
> > > > wrote:
> > > > >
> > > > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenthe
> > > > > > r@gm
> > > > > > ail.
> > > > > > com> wrote:
> > > > > > >
> > > > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@red
> > > > > > > hat.
> > > > > > > com>
> > > > > > > wrote:
> > > > > > > >
> > > > > > > > This patch adds a concept of nested "scopes" to
> > > > > > > > dumpfile.c's
> > > > > > > > dump_*_loc
> > > > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in
> > > > > > > > tree-
> > > > > > > > vectorizer.h,
> > > > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > > > indentation.
> > > > > > > >
> > > > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > > > >
> > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > > > test.c:8:3: note: symbolic number of iterations is
> > > > > > > > (unsigned
> > > > > > > > int)
> > > > > > > > n_9(D)
> > > > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > > > calls or
> > > > > > > > data references that cannot be analyzed
> > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > >
> > > > > > > > to:
> > > > > > > >
> > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > > > (unsigned
> > > > > > > > int) n_9(D)
> > > > > > > > test.c:8:3: note:   not vectorized: loop contains
> > > > > > > > function
> > > > > > > > calls
> > > > > > > > or data references that cannot be analyzed
> > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > >
> > > > > > > > showing that the "symbolic number of iterations" message
> > > > > > > > is
> > > > > > > > within
> > > > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > > > "=== vect_analyze_loop_form ===").
> > > > > > > >
> > > > > > > > This is also enabling work for followups involving
> > > > > > > > optimization
> > > > > > > > records
> > > > > > > > (allowing the records to directly capture the nested
> > > > > > > > structure of
> > > > > > > > the
> > > > > > > > dump messages).
> > > > > > > >
> > > > > > > > Successfully bootstrapped & regrtested on x86_64-pc-
> > > > > > > > linux-
> > > > > > > > gnu.
> > > > > > > >
> > > > > > > > OK for trunk?
> > > > > >
> > > > > > Hi,
> > > > > >
> > > > > > I've noticed that this patch (r262246) caused regressions on
> > > > > > aarch64:
> > > > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-
> > > > > > tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built
> > > > > > SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >
> > > > > > The problem is that now there are more spaces between "note:"
> > > > > > and
> > > > > > "Built", the attached small patch does that for slp-perm-1.c.
> > > > >
> > > > > Sorry about the breakage.
> > > > >
> > > > > > Is it the right way of fixing it or do we want to accept any
> > > > > > amount
> > > > > > of
> > > > > > spaces for instance?
> > > > >
> > > > > I don't think we want to hardcode the amount of space in the
> > > > > dumpfile.
> > > > > The idea of my patch was to make the dump more human-readable
> > > > > (I
> > > > > hope)
> > > > > by visualizing the nesting structure of the dump messages, but
> > > > > I
> > > > > think
> > > > > we shouldn't "bake" that into the expected strings, as someone
> > > > > might
> > > > > want to add an intermediate nesting level.
> > > > >
> > > > > Do we really need to look for the "note:" in the scan-tree-
> > > > > dump?
> > > > > Should that directive be rewritten to:
> > > > >
> > > > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can
> > > > > use
> > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > vect_load_lanes } } } } */
> > > > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > vect_load_lanes } } } } */
> > > > >
> > > > > which I believe would match any amount of spaces.
> > > > >
> > > > > Alternatively a regex accepting any amount of space ought to
> > > > > work,
> > > > > if
> > > > > we care that the message begins with "note: ".
> > > > >
> > > > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > > > regardless of whether it's a MSG_NOTE vs a
> > > > > MSG_MISSED_OPTIMIZATION
> > > > > or
> > > > > whatever.  Given that, maybe we should just drop the "note: "
> > > > > prefix
> > > > > from these scan-tree-dump expected regexes?  (assuming that
> > > > > works)
> > > >
> > > > I guess it was done for the -fopt-info output channel to match
> > > > what
> > > > we emit with inform () given those are neither warnings nor
> > > > errors.
> > > >
> > > > But yes, lets elide "note: " from dumpfiles.  Be prepared to
> > > > fiddle
> > > > with expected scan-dumps though.
> > >
> > > Re-reading, I think I was unclear, but I was proposing removing it
> > > from
> > > the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open
> > > to
> > > the latter).
> >
> > Both is fine with me.
> >
> > Richard.
> >
> > > >
> > > > > > I'm surprised there is such little impact on the testsuite
> > > > > > though.
> > > > >
> > > > > I see lots of scan-tree-dump* directives in the vect part of
> > > > > the
> > > > > testsuite, but it seems that only these ones use the "note: "
> > > > > prefix; I
> > > > > think everything else was matching against the message whilst
> > > > > ignoring
> > > > > the prefix, so it didn't matter when the prefix changed
> > > > > (I double-checked and these scan-tree-dump directives didn't
> > > > > trigger on
> > > > > my x86_64 testing of the patch, due to { target {
> > > > > vect_perm3_int &&
> > > > > vect_load_lanes } }, where I see
> > > > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > > > >
> > > > > > If OK, I'll update the patch to take the other slp-perm-
> > > > > > [235678].c
> > > > > > tests into account.
> > > > > >
> > > > > > Thanks,
> > > > > >
> > > > > > Christophe
> > > > >
> > > > > Sorry again about the breakage.
> > > > > Dave
>
> Here's a minimal patch to remove the "note: " prefix from the
> affected directives.
>
> Christophe, does this fix the issues seen on aarch64?
>
> OK for trunk?

OK.

Richard.

> gcc/testsuite/ChangeLog:
>         * gcc.dg/vect/slp-perm-1.c: Remove "note: " prefix from
>         scan-tree-dump directive.
>         * gcc.dg/vect/slp-perm-2.c: Likewise.
>         * gcc.dg/vect/slp-perm-3.c: Likewise.
>         * gcc.dg/vect/slp-perm-5.c: Likewise.
>         * gcc.dg/vect/slp-perm-6.c: Likewise.
>         * gcc.dg/vect/slp-perm-7.c: Likewise.
>         * gcc.dg/vect/slp-perm-8.c: Likewise.
> ---
>  gcc/testsuite/gcc.dg/vect/slp-perm-1.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-2.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-3.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-5.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-6.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-7.c | 2 +-
>  gcc/testsuite/gcc.dg/vect/slp-perm-8.c | 2 +-
>  7 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> index 6bd16ef..ca7803e 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> @@ -82,7 +82,7 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && {! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
>
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> index 4bab348..82776f3 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> @@ -56,6 +56,6 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> index 568e400..1807275 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> @@ -69,7 +69,7 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
>
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> index 5293913..b86a3dc 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> @@ -106,7 +106,7 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
>
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> index 4eb648a..97a0ebf 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> @@ -105,6 +105,6 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> index baf7f78..346411f 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> @@ -98,6 +98,6 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> index 94d4455..17aa111 100644
> --- a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> @@ -62,6 +62,6 @@ int main (int argc, const char* argv[])
>  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target { vect_perm_byte } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_byte && { ! vect_load_lanes } } } } } */
>  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
> +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
>  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
>  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-03 13:53                             ` Richard Biener
  2018-07-03 14:10                               ` [PATCH] Remove "note: " prefix from some scan-tree-dump directives David Malcolm
@ 2018-07-05  8:42                               ` Christophe Lyon
  2018-07-05  9:03                                 ` Richard Biener
  1 sibling, 1 reply; 80+ messages in thread
From: Christophe Lyon @ 2018-07-05  8:42 UTC (permalink / raw)
  To: Richard Biener; +Cc: David Malcolm, gcc Patches

[-- Attachment #1: Type: text/plain, Size: 8298 bytes --]

On Tue, 3 Jul 2018 at 15:53, Richard Biener <richard.guenther@gmail.com> wrote:
>
> On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com>
> > > wrote:
> > > >
> > > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gm
> > > > > ail.
> > > > > com> wrote:
> > > > > >
> > > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.
> > > > > > com>
> > > > > > wrote:
> > > > > > >
> > > > > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > > > > dump_*_loc
> > > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > > > > vectorizer.h,
> > > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > > indentation.
> > > > > > >
> > > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > > >
> > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > > test.c:8:3: note: symbolic number of iterations is (unsigned
> > > > > > > int)
> > > > > > > n_9(D)
> > > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > > calls or
> > > > > > > data references that cannot be analyzed
> > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > >
> > > > > > > to:
> > > > > > >
> > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > > (unsigned
> > > > > > > int) n_9(D)
> > > > > > > test.c:8:3: note:   not vectorized: loop contains function
> > > > > > > calls
> > > > > > > or data references that cannot be analyzed
> > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > >
> > > > > > > showing that the "symbolic number of iterations" message is
> > > > > > > within
> > > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > > "=== vect_analyze_loop_form ===").
> > > > > > >
> > > > > > > This is also enabling work for followups involving
> > > > > > > optimization
> > > > > > > records
> > > > > > > (allowing the records to directly capture the nested
> > > > > > > structure of
> > > > > > > the
> > > > > > > dump messages).
> > > > > > >
> > > > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-
> > > > > > > gnu.
> > > > > > >
> > > > > > > OK for trunk?
> > > > >
> > > > > Hi,
> > > > >
> > > > > I've noticed that this patch (r262246) caused regressions on
> > > > > aarch64:
> > > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-
> > > > > dump
> > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > > > > cancelled: can use load/store-lanes"
> > > > >
> > > > > The problem is that now there are more spaces between "note:" and
> > > > > "Built", the attached small patch does that for slp-perm-1.c.
> > > >
> > > > Sorry about the breakage.
> > > >
> > > > > Is it the right way of fixing it or do we want to accept any
> > > > > amount
> > > > > of
> > > > > spaces for instance?
> > > >
> > > > I don't think we want to hardcode the amount of space in the
> > > > dumpfile.
> > > > The idea of my patch was to make the dump more human-readable (I
> > > > hope)
> > > > by visualizing the nesting structure of the dump messages, but I
> > > > think
> > > > we shouldn't "bake" that into the expected strings, as someone
> > > > might
> > > > want to add an intermediate nesting level.
> > > >
> > > > Do we really need to look for the "note:" in the scan-tree-dump?
> > > > Should that directive be rewritten to:
> > > >
> > > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use
> > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > vect_load_lanes } } } } */
> > > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > vect_load_lanes } } } } */
> > > >
> > > > which I believe would match any amount of spaces.
> > > >
> > > > Alternatively a regex accepting any amount of space ought to work,
> > > > if
> > > > we care that the message begins with "note: ".
> > > >
> > > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > > regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION
> > > > or
> > > > whatever.  Given that, maybe we should just drop the "note: "
> > > > prefix
> > > > from these scan-tree-dump expected regexes?  (assuming that works)
> > >
> > > I guess it was done for the -fopt-info output channel to match what
> > > we emit with inform () given those are neither warnings nor errors.
> > >
> > > But yes, lets elide "note: " from dumpfiles.  Be prepared to fiddle
> > > with expected scan-dumps though.
> >
> > Re-reading, I think I was unclear, but I was proposing removing it from
> > the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open to
> > the latter).
>
> Both is fine with me.
>

The attached patch removes 'note:' from the scan-tree-dump directives.
Testing showed no regression, but I didn't exercise the
gcc.target/i386 and gnat.dg parts.

OK?

> Richard.
>
> > >
> > > > > I'm surprised there is such little impact on the testsuite
> > > > > though.
> > > >
> > > > I see lots of scan-tree-dump* directives in the vect part of the
> > > > testsuite, but it seems that only these ones use the "note: "
> > > > prefix; I
> > > > think everything else was matching against the message whilst
> > > > ignoring
> > > > the prefix, so it didn't matter when the prefix changed
> > > > (I double-checked and these scan-tree-dump directives didn't
> > > > trigger on
> > > > my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> > > > vect_load_lanes } }, where I see
> > > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > > >
> > > > > If OK, I'll update the patch to take the other slp-perm-
> > > > > [235678].c
> > > > > tests into account.
> > > > >
> > > > > Thanks,
> > > > >
> > > > > Christophe
> > > >
> > > > Sorry again about the breakage.
> > > > Dave
> > > >

[-- Attachment #2: note.chlog.txt --]
[-- Type: text/plain, Size: 1122 bytes --]

2018-07-05  Christophe Lyon  <christophe.lyon@linaro.org>

	* c-c++-common/unroll-1.c: Remove 'note:' in matching string.
	* c-c++-common/unroll-2.c: Likewise.
	* g++.dg/cdce3.C: Likewise.
	* g++.dg/ext/unroll-1.C: Likewise.
	* g++.dg/ext/unroll-2.C: Likewise.
	* g++.dg/ext/unroll-3.C: Likewise.
	* gcc.dg/cdce1.c: Likewise.
	* gcc.dg/cdce2.c: Likewise.
	* gcc.dg/gomp/pr68128-1.c: Likewise.
	* gcc.dg/vect/pr46032.c: Likewise.
	* gcc.dg/vect/vect-cond-10.c: Likewise.
	* gcc.dg/vect/vect-cond-8.c: Likewise.
	* gcc.dg/vect/vect-cond-9.c: Likewise.
	* gcc.dg/vect/vect-mask-load-1.c: Likewise.
	* gcc.dg/vect/vect-mask-loadstore-1.c: Likewise.
	* gcc.target/i386/avx-cvt-2.c: Likewise.
	* gcc.target/i386/avx-cvt-3.c: Likewise.
	* gcc.target/i386/avx2-cvt-2.c: Likewise.
	* gcc.target/i386/avx2-gather-2.c: Likewise.
	* gcc.target/i386/avx2-gather-6.c: Likewise.
	* gcc.target/i386/avx512f-gather-2.c: Likewise.
	* gcc.target/i386/sse2-cvt-2.c: Likewise.
	* gfortran.dg/directive_unroll_1.f90: Likewise.
	* gfortran.dg/directive_unroll_2.f90: Likewise.
	* gnat.dg/unroll2.adb: Likewise.
	* gnat.dg/unroll3.adb: Likewise.

[-- Attachment #3: note.patch.txt --]
[-- Type: text/plain, Size: 18728 bytes --]

diff --git a/gcc/testsuite/c-c++-common/unroll-1.c b/gcc/testsuite/c-c++-common/unroll-1.c
index ccae250..105a82c 100644
--- a/gcc/testsuite/c-c++-common/unroll-1.c
+++ b/gcc/testsuite/c-c++-common/unroll-1.c
@@ -10,12 +10,12 @@ void test (void)
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 8; ++i)
     bar(i);
-  /* { dg-final { scan-tree-dump "11:.*: note: loop with 8 iterations completely unrolled" "cunrolli" } } */
+  /* { dg-final { scan-tree-dump "11:.*: loop with 8 iterations completely unrolled" "cunrolli" } } */
 
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 7; ++i)
     bar(i);
-  /* { dg-final { scan-tree-dump "16:.*: note: loop with 7 iterations completely unrolled" "cunrolli" } } */
+  /* { dg-final { scan-tree-dump "16:.*: loop with 7 iterations completely unrolled" "cunrolli" } } */
 
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 15; ++i)
diff --git a/gcc/testsuite/c-c++-common/unroll-2.c b/gcc/testsuite/c-c++-common/unroll-2.c
index 635b6c2..a67a1d7 100644
--- a/gcc/testsuite/c-c++-common/unroll-2.c
+++ b/gcc/testsuite/c-c++-common/unroll-2.c
@@ -10,12 +10,12 @@ void test (void)
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 8; ++i)
     bar(i);
-  /* { dg-final { scan-tree-dump "11:.*: note: loop with 7 iterations completely unrolled" "cunroll" } } */
+  /* { dg-final { scan-tree-dump "11:.*: loop with 7 iterations completely unrolled" "cunroll" } } */
 
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 7; ++i)
     bar(i);
-  /* { dg-final { scan-tree-dump "16:.*: note: loop with 6 iterations completely unrolled" "cunroll" } } */
+  /* { dg-final { scan-tree-dump "16:.*: loop with 6 iterations completely unrolled" "cunroll" } } */
 
   #pragma GCC unroll 8
   for (unsigned long i = 1; i <= 15; ++i)
diff --git a/gcc/testsuite/g++.dg/cdce3.C b/gcc/testsuite/g++.dg/cdce3.C
index 3937953..4b547b5 100644
--- a/gcc/testsuite/g++.dg/cdce3.C
+++ b/gcc/testsuite/g++.dg/cdce3.C
@@ -4,22 +4,22 @@
 /* { dg-additional-options "-DLARGE_LONG_DOUBLE" { target large_long_double } } */
 /* { dg-additional-options "-DGNU_EXTENSION" { target pow10 } } */
 /* { dg-add-options ieee } */
-/* { dg-final { scan-tree-dump  "cdce3.C:91: note: function call is shrink-wrapped into error conditions\." "cdce" { target pow10 } } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:92: note: function call is shrink-wrapped into error conditions\." "cdce" { target pow10 } } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:94: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:95: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:96: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:97: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:98: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:99: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:100: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:101: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:102: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:103: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:104: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:105: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:106: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
-/* { dg-final { scan-tree-dump  "cdce3.C:107: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:91: .* function call is shrink-wrapped into error conditions\." "cdce" { target pow10 } } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:92: .* function call is shrink-wrapped into error conditions\." "cdce" { target pow10 } } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:94: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:95: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:96: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:97: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:98: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:99: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:100: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:101: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:102: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:103: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:104: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:105: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:106: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce3.C:107: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
 
 #include <stdlib.h>
 #include <math.h>
diff --git a/gcc/testsuite/g++.dg/ext/unroll-1.C b/gcc/testsuite/g++.dg/ext/unroll-1.C
index 2d58a6a..aa11b2e 100644
--- a/gcc/testsuite/g++.dg/ext/unroll-1.C
+++ b/gcc/testsuite/g++.dg/ext/unroll-1.C
@@ -16,4 +16,4 @@ bar (int *a, int *b, int *c)
   foo <int> (a, b, c);
 }
 
-// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 8 iterations completely unrolled" "cunrolli" } }
diff --git a/gcc/testsuite/g++.dg/ext/unroll-2.C b/gcc/testsuite/g++.dg/ext/unroll-2.C
index e68cc31..f9ec892 100644
--- a/gcc/testsuite/g++.dg/ext/unroll-2.C
+++ b/gcc/testsuite/g++.dg/ext/unroll-2.C
@@ -10,4 +10,4 @@ foo (int (&a)[8], int *b, int *c)
     a[i] = b[i] * c[i];
 }
 
-// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 8 iterations completely unrolled" "cunrolli" } }
diff --git a/gcc/testsuite/g++.dg/ext/unroll-3.C b/gcc/testsuite/g++.dg/ext/unroll-3.C
index 6516ee9..dda94c5 100644
--- a/gcc/testsuite/g++.dg/ext/unroll-3.C
+++ b/gcc/testsuite/g++.dg/ext/unroll-3.C
@@ -17,4 +17,4 @@ bar (int (&a)[8], int *b, int *c)
   foo <int> (a, b, c);
 }
 
-// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 8 iterations completely unrolled" "cunrolli" } }
diff --git a/gcc/testsuite/gcc.dg/cdce1.c b/gcc/testsuite/gcc.dg/cdce1.c
index 02b47c0..b23ad63 100644
--- a/gcc/testsuite/gcc.dg/cdce1.c
+++ b/gcc/testsuite/gcc.dg/cdce1.c
@@ -1,7 +1,7 @@
 /* { dg-do  run  } */
 /* { dg-options "-O2 -fmath-errno -fdump-tree-cdce-details  -lm" } */
 /* { dg-require-effective-target int32plus } */
-/* { dg-final { scan-tree-dump  "cdce1.c:16: note: function call is shrink-wrapped into error conditions\."  "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce1.c:16: .* function call is shrink-wrapped into error conditions\."  "cdce" } } */
 /* { dg-require-effective-target large_double } */
 
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/cdce2.c b/gcc/testsuite/gcc.dg/cdce2.c
index 9e6f344..30e7cb1 100644
--- a/gcc/testsuite/gcc.dg/cdce2.c
+++ b/gcc/testsuite/gcc.dg/cdce2.c
@@ -1,7 +1,7 @@
 /* { dg-do  run  } */
 /* { dg-skip-if "doubles are floats" { "avr-*-*" } } */
 /* { dg-options "-O2 -fmath-errno -fdump-tree-cdce-details  -lm" } */
-/* { dg-final { scan-tree-dump  "cdce2.c:15: note: function call is shrink-wrapped into error conditions\." "cdce" } } */
+/* { dg-final { scan-tree-dump  "cdce2.c:15: .* function call is shrink-wrapped into error conditions\." "cdce" } } */
  
 #include <stdlib.h>
 #include <math.h>
diff --git a/gcc/testsuite/gcc.dg/gomp/pr68128-1.c b/gcc/testsuite/gcc.dg/gomp/pr68128-1.c
index 36823c2..01cc605 100644
--- a/gcc/testsuite/gcc.dg/gomp/pr68128-1.c
+++ b/gcc/testsuite/gcc.dg/gomp/pr68128-1.c
@@ -29,4 +29,4 @@ foo (float *u, float v, float w, float x, float y, float z, float t)
     }
 }
 
-/* { dg-final { scan-tree-dump "note: vectorized 1 loops in function" "vect" { target i?86-*-* x86_64-*-* } } } */
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" { target i?86-*-* x86_64-*-* } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/pr46032.c b/gcc/testsuite/gcc.dg/vect/pr46032.c
index e1a5834..bad8745 100644
--- a/gcc/testsuite/gcc.dg/vect/pr46032.c
+++ b/gcc/testsuite/gcc.dg/vect/pr46032.c
@@ -44,6 +44,6 @@ main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loop" 1 "vect" { xfail { vect_no_align && { ! vect_hw_misalign } } } } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" { xfail { vect_no_align && { ! vect_hw_misalign } } } } } */
 /* { dg-final { scan-tree-dump-not "versioning for alias required" "vect" } } */
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
index 1a18800..b2f97d7 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
@@ -163,4 +163,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops" 8 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 8 "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-8.c b/gcc/testsuite/gcc.dg/vect/vect-cond-8.c
index 224251d..d888442 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-8.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-8.c
@@ -119,4 +119,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops" 5 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 5 "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
index c03ed96..63eee1b 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
@@ -198,4 +198,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops" 10 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 10 "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-mask-load-1.c b/gcc/testsuite/gcc.dg/vect/vect-mask-load-1.c
index 3a38b64..992cbda 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-mask-load-1.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-mask-load-1.c
@@ -46,4 +46,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops" 1 "vect" { target avx_runtime } } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target avx_runtime } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-mask-loadstore-1.c b/gcc/testsuite/gcc.dg/vect/vect-mask-loadstore-1.c
index 56b130e..7d9dc5a 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-mask-loadstore-1.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-mask-loadstore-1.c
@@ -44,4 +44,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops" 1 "vect" { target avx_runtime } } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target avx_runtime } } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx-cvt-2.c b/gcc/testsuite/gcc.target/i386/avx-cvt-2.c
index 7380833..1fbcf6e 100644
--- a/gcc/testsuite/gcc.target/i386/avx-cvt-2.c
+++ b/gcc/testsuite/gcc.target/i386/avx-cvt-2.c
@@ -3,7 +3,7 @@
 
 #include "avx-cvt-1.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 6 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 6 "vect" } } */
 /* { dg-final { scan-assembler "vcvttpd2dq(y\[^\n\r\]*%xmm|\[^\n\r\]*xmm\[^\n\r\]*YMMWORD PTR)" } } */
 /* { dg-final { scan-assembler "vcvtdq2ps\[^\n\r\]*ymm" } } */
 /* { dg-final { scan-assembler "vcvtps2pd\[^\n\r\]*(%xmm\[^\n\r\]*%ymm|ymm\[^\n\r\]*xmm)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx-cvt-3.c b/gcc/testsuite/gcc.target/i386/avx-cvt-3.c
index de2e482..33af620 100644
--- a/gcc/testsuite/gcc.target/i386/avx-cvt-3.c
+++ b/gcc/testsuite/gcc.target/i386/avx-cvt-3.c
@@ -3,7 +3,7 @@
 
 #include "avx-cvt-1.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 6 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 6 "vect" } } */
 /* { dg-final { scan-assembler "vcvttpd2dq(x\[^\n\r\]*%xmm|\[^\n\r\]*xmm\[^\n\r\]*XMMWORD PTR)" } } */
 /* { dg-final { scan-assembler "vcvtdq2ps\[^\n\r\]*xmm" } } */
 /* { dg-final { scan-assembler "vcvtps2pd\[^\n\r\]*(%xmm\[^\n\r\]*%xmm|xmm\[^\n\r\]*xmm)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx2-cvt-2.c b/gcc/testsuite/gcc.target/i386/avx2-cvt-2.c
index 317c2c0..d37809d 100644
--- a/gcc/testsuite/gcc.target/i386/avx2-cvt-2.c
+++ b/gcc/testsuite/gcc.target/i386/avx2-cvt-2.c
@@ -3,7 +3,7 @@
 
 #include "avx2-cvt-1.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 6 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 6 "vect" } } */
 /* { dg-final { scan-assembler "vcvttpd2dq(y\[^\n\r\]*%xmm|\[^\n\r\]*xmm\[^\n\r\]*YMMWORD PTR)" } } */
 /* { dg-final { scan-assembler "vcvtdq2ps\[^\n\r\]*ymm" } } */
 /* { dg-final { scan-assembler "vcvtps2pd\[^\n\r\]*(%xmm\[^\n\r\]*%ymm|ymm\[^\n\r\]*xmm)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx2-gather-2.c b/gcc/testsuite/gcc.target/i386/avx2-gather-2.c
index e080323..1a704af 100644
--- a/gcc/testsuite/gcc.target/i386/avx2-gather-2.c
+++ b/gcc/testsuite/gcc.target/i386/avx2-gather-2.c
@@ -3,4 +3,4 @@
 
 #include "avx2-gather-1.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 16 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 16 "vect" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx2-gather-6.c b/gcc/testsuite/gcc.target/i386/avx2-gather-6.c
index 0ffb04f..b911958 100644
--- a/gcc/testsuite/gcc.target/i386/avx2-gather-6.c
+++ b/gcc/testsuite/gcc.target/i386/avx2-gather-6.c
@@ -3,4 +3,4 @@
 
 #include "avx2-gather-5.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 1 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 1 "vect" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx512f-gather-2.c b/gcc/testsuite/gcc.target/i386/avx512f-gather-2.c
index e52a0ef..a26aa65 100644
--- a/gcc/testsuite/gcc.target/i386/avx512f-gather-2.c
+++ b/gcc/testsuite/gcc.target/i386/avx512f-gather-2.c
@@ -7,4 +7,4 @@
 /* { dg-final { scan-assembler-not "gather\[^\n\]*xmm\[^\n\]*ymm" } } */
 /* { dg-final { scan-assembler-not "gather\[^\n\]*ymm\[^\n\]*xmm" } } */
 /* { dg-final { scan-assembler-not "gather\[^\n\]*xmm\[^\n\]*xmm" } } */
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 16 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 16 "vect" } } */
diff --git a/gcc/testsuite/gcc.target/i386/sse2-cvt-2.c b/gcc/testsuite/gcc.target/i386/sse2-cvt-2.c
index 97bd1fd..0ecafb2 100644
--- a/gcc/testsuite/gcc.target/i386/sse2-cvt-2.c
+++ b/gcc/testsuite/gcc.target/i386/sse2-cvt-2.c
@@ -3,7 +3,7 @@
 
 #include "sse2-cvt-1.c"
 
-/* { dg-final { scan-tree-dump-times "note: vectorized 1 loops in function" 6 "vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 6 "vect" } } */
 /* { dg-final { scan-assembler "cvttpd2dq" } } */
 /* { dg-final { scan-assembler "cvtdq2ps" } } */
 /* { dg-final { scan-assembler "cvtps2pd" } } */
diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_1.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_1.f90
index 00fe7dc..85b3671 100644
--- a/gcc/testsuite/gfortran.dg/directive_unroll_1.f90
+++ b/gcc/testsuite/gfortran.dg/directive_unroll_1.f90
@@ -12,7 +12,7 @@ subroutine test1(a)
   DO i=1, 8, 1
     call dummy(a(i))
   ENDDO
-! { dg-final { scan-tree-dump "12:.*: note: loop with 8 iterations completely unrolled" "cunrolli" } } */
+! { dg-final { scan-tree-dump "12:.*: loop with 8 iterations completely unrolled" "cunrolli" } } */
 end subroutine test1
 
 subroutine test2(a, n)
diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_2.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_2.f90
index bc93f91..6dff8fa 100644
--- a/gcc/testsuite/gfortran.dg/directive_unroll_2.f90
+++ b/gcc/testsuite/gfortran.dg/directive_unroll_2.f90
@@ -12,7 +12,7 @@ subroutine test1(a)
   DO i=1, 8, 1
     call dummy(a(i))
   ENDDO
-! { dg-final { scan-tree-dump "12:.*: note: loop with 7 iterations completely unrolled" "cunroll" } } */
+! { dg-final { scan-tree-dump "12:.*: loop with 7 iterations completely unrolled" "cunroll" } } */
 end subroutine test1
 
 subroutine test2(a, n)
diff --git a/gcc/testsuite/gnat.dg/unroll2.adb b/gcc/testsuite/gnat.dg/unroll2.adb
index e4473cc..1d3a757 100644
--- a/gcc/testsuite/gnat.dg/unroll2.adb
+++ b/gcc/testsuite/gnat.dg/unroll2.adb
@@ -23,4 +23,4 @@ package body Unroll2 is
 
 end Unroll2;
 
--- { dg-final { scan-tree-dump-times "note: loop with 3 iterations completely unrolled" 2 "cunrolli" } }
+-- { dg-final { scan-tree-dump-times "loop with 3 iterations completely unrolled" 2 "cunrolli" } }
diff --git a/gcc/testsuite/gnat.dg/unroll3.adb b/gcc/testsuite/gnat.dg/unroll3.adb
index ba4e122..3bd06e7 100644
--- a/gcc/testsuite/gnat.dg/unroll3.adb
+++ b/gcc/testsuite/gnat.dg/unroll3.adb
@@ -23,4 +23,4 @@ package body Unroll3 is
 
 end Unroll3;
 
--- { dg-final { scan-tree-dump-times "note: loop with 3 iterations completely unrolled" 2 "cunroll" } }
+-- { dg-final { scan-tree-dump-times "loop with 3 iterations completely unrolled" 2 "cunroll" } }

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE
  2018-07-05  8:42                               ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE Christophe Lyon
@ 2018-07-05  9:03                                 ` Richard Biener
  0 siblings, 0 replies; 80+ messages in thread
From: Richard Biener @ 2018-07-05  9:03 UTC (permalink / raw)
  To: christophe.lyon; +Cc: David Malcolm, GCC Patches

On Thu, Jul 5, 2018 at 10:42 AM Christophe Lyon
<christophe.lyon@linaro.org> wrote:
>
> On Tue, 3 Jul 2018 at 15:53, Richard Biener <richard.guenther@gmail.com> wrote:
> >
> > On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com> wrote:
> > >
> > > On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > > > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com>
> > > > wrote:
> > > > >
> > > > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenther@gm
> > > > > > ail.
> > > > > > com> wrote:
> > > > > > >
> > > > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@redhat.
> > > > > > > com>
> > > > > > > wrote:
> > > > > > > >
> > > > > > > > This patch adds a concept of nested "scopes" to dumpfile.c's
> > > > > > > > dump_*_loc
> > > > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in tree-
> > > > > > > > vectorizer.h,
> > > > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > > > indentation.
> > > > > > > >
> > > > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > > > >
> > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > > > test.c:8:3: note: symbolic number of iterations is (unsigned
> > > > > > > > int)
> > > > > > > > n_9(D)
> > > > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > > > calls or
> > > > > > > > data references that cannot be analyzed
> > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > >
> > > > > > > > to:
> > > > > > > >
> > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > > > (unsigned
> > > > > > > > int) n_9(D)
> > > > > > > > test.c:8:3: note:   not vectorized: loop contains function
> > > > > > > > calls
> > > > > > > > or data references that cannot be analyzed
> > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > >
> > > > > > > > showing that the "symbolic number of iterations" message is
> > > > > > > > within
> > > > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > > > "=== vect_analyze_loop_form ===").
> > > > > > > >
> > > > > > > > This is also enabling work for followups involving
> > > > > > > > optimization
> > > > > > > > records
> > > > > > > > (allowing the records to directly capture the nested
> > > > > > > > structure of
> > > > > > > > the
> > > > > > > > dump messages).
> > > > > > > >
> > > > > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-
> > > > > > > > gnu.
> > > > > > > >
> > > > > > > > OK for trunk?
> > > > > >
> > > > > > Hi,
> > > > > >
> > > > > > I've noticed that this patch (r262246) caused regressions on
> > > > > > aarch64:
> > > > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-tree-
> > > > > > dump
> > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built SLP
> > > > > > cancelled: can use load/store-lanes"
> > > > > >
> > > > > > The problem is that now there are more spaces between "note:" and
> > > > > > "Built", the attached small patch does that for slp-perm-1.c.
> > > > >
> > > > > Sorry about the breakage.
> > > > >
> > > > > > Is it the right way of fixing it or do we want to accept any
> > > > > > amount
> > > > > > of
> > > > > > spaces for instance?
> > > > >
> > > > > I don't think we want to hardcode the amount of space in the
> > > > > dumpfile.
> > > > > The idea of my patch was to make the dump more human-readable (I
> > > > > hope)
> > > > > by visualizing the nesting structure of the dump messages, but I
> > > > > think
> > > > > we shouldn't "bake" that into the expected strings, as someone
> > > > > might
> > > > > want to add an intermediate nesting level.
> > > > >
> > > > > Do we really need to look for the "note:" in the scan-tree-dump?
> > > > > Should that directive be rewritten to:
> > > > >
> > > > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use
> > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > vect_load_lanes } } } } */
> > > > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > vect_load_lanes } } } } */
> > > > >
> > > > > which I believe would match any amount of spaces.
> > > > >
> > > > > Alternatively a regex accepting any amount of space ought to work,
> > > > > if
> > > > > we care that the message begins with "note: ".
> > > > >
> > > > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > > > regardless of whether it's a MSG_NOTE vs a MSG_MISSED_OPTIMIZATION
> > > > > or
> > > > > whatever.  Given that, maybe we should just drop the "note: "
> > > > > prefix
> > > > > from these scan-tree-dump expected regexes?  (assuming that works)
> > > >
> > > > I guess it was done for the -fopt-info output channel to match what
> > > > we emit with inform () given those are neither warnings nor errors.
> > > >
> > > > But yes, lets elide "note: " from dumpfiles.  Be prepared to fiddle
> > > > with expected scan-dumps though.
> > >
> > > Re-reading, I think I was unclear, but I was proposing removing it from
> > > the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open to
> > > the latter).
> >
> > Both is fine with me.
> >
>
> The attached patch removes 'note:' from the scan-tree-dump directives.
> Testing showed no regression, but I didn't exercise the
> gcc.target/i386 and gnat.dg parts.
>
> OK?

OK.

RIchard.

> > Richard.
> >
> > > >
> > > > > > I'm surprised there is such little impact on the testsuite
> > > > > > though.
> > > > >
> > > > > I see lots of scan-tree-dump* directives in the vect part of the
> > > > > testsuite, but it seems that only these ones use the "note: "
> > > > > prefix; I
> > > > > think everything else was matching against the message whilst
> > > > > ignoring
> > > > > the prefix, so it didn't matter when the prefix changed
> > > > > (I double-checked and these scan-tree-dump directives didn't
> > > > > trigger on
> > > > > my x86_64 testing of the patch, due to { target { vect_perm3_int &&
> > > > > vect_load_lanes } }, where I see
> > > > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > > > >
> > > > > > If OK, I'll update the patch to take the other slp-perm-
> > > > > > [235678].c
> > > > > > tests into account.
> > > > > >
> > > > > > Thanks,
> > > > > >
> > > > > > Christophe
> > > > >
> > > > > Sorry again about the breakage.
> > > > > Dave
> > > > >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH] Remove "note: " prefix from some scan-tree-dump directives
  2018-07-03 14:11                                 ` Richard Biener
@ 2018-07-09  9:03                                   ` Christophe Lyon
  0 siblings, 0 replies; 80+ messages in thread
From: Christophe Lyon @ 2018-07-09  9:03 UTC (permalink / raw)
  To: Richard Biener; +Cc: David Malcolm, gcc Patches

On Tue, 3 Jul 2018 at 16:11, Richard Biener <richard.guenther@gmail.com> wrote:
>
> On Tue, Jul 3, 2018 at 4:10 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > On Tue, 2018-07-03 at 15:53 +0200, Richard Biener wrote:
> > > On Tue, Jul 3, 2018 at 3:52 PM David Malcolm <dmalcolm@redhat.com>
> > > wrote:
> > > >
> > > > On Tue, 2018-07-03 at 09:37 +0200, Richard Biener wrote:
> > > > > On Mon, Jul 2, 2018 at 7:00 PM David Malcolm <dmalcolm@redhat.com
> > > > > >
> > > > > wrote:
> > > > > >
> > > > > > On Mon, 2018-07-02 at 14:23 +0200, Christophe Lyon wrote:
> > > > > > > On Fri, 29 Jun 2018 at 10:09, Richard Biener <richard.guenthe
> > > > > > > r@gm
> > > > > > > ail.
> > > > > > > com> wrote:
> > > > > > > >
> > > > > > > > On Tue, Jun 26, 2018 at 5:43 PM David Malcolm <dmalcolm@red
> > > > > > > > hat.
> > > > > > > > com>
> > > > > > > > wrote:
> > > > > > > > >
> > > > > > > > > This patch adds a concept of nested "scopes" to
> > > > > > > > > dumpfile.c's
> > > > > > > > > dump_*_loc
> > > > > > > > > calls, and wires it up to the DUMP_VECT_SCOPE macro in
> > > > > > > > > tree-
> > > > > > > > > vectorizer.h,
> > > > > > > > > so that the nested structure is shown in -fopt-info by
> > > > > > > > > indentation.
> > > > > > > > >
> > > > > > > > > For example, this converts -fopt-info-all e.g. from:
> > > > > > > > >
> > > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > > test.c:8:3: note: === analyze_loop_nest ===
> > > > > > > > > test.c:8:3: note: === vect_analyze_loop_form ===
> > > > > > > > > test.c:8:3: note: === get_loop_niters ===
> > > > > > > > > test.c:8:3: note: symbolic number of iterations is
> > > > > > > > > (unsigned
> > > > > > > > > int)
> > > > > > > > > n_9(D)
> > > > > > > > > test.c:8:3: note: not vectorized: loop contains function
> > > > > > > > > calls or
> > > > > > > > > data references that cannot be analyzed
> > > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > > >
> > > > > > > > > to:
> > > > > > > > >
> > > > > > > > > test.c:8:3: note: === analyzing loop ===
> > > > > > > > > test.c:8:3: note:  === analyze_loop_nest ===
> > > > > > > > > test.c:8:3: note:   === vect_analyze_loop_form ===
> > > > > > > > > test.c:8:3: note:    === get_loop_niters ===
> > > > > > > > > test.c:8:3: note:   symbolic number of iterations is
> > > > > > > > > (unsigned
> > > > > > > > > int) n_9(D)
> > > > > > > > > test.c:8:3: note:   not vectorized: loop contains
> > > > > > > > > function
> > > > > > > > > calls
> > > > > > > > > or data references that cannot be analyzed
> > > > > > > > > test.c:8:3: note: vectorized 0 loops in function
> > > > > > > > >
> > > > > > > > > showing that the "symbolic number of iterations" message
> > > > > > > > > is
> > > > > > > > > within
> > > > > > > > > the "=== analyze_loop_nest ===" (and not within the
> > > > > > > > > "=== vect_analyze_loop_form ===").
> > > > > > > > >
> > > > > > > > > This is also enabling work for followups involving
> > > > > > > > > optimization
> > > > > > > > > records
> > > > > > > > > (allowing the records to directly capture the nested
> > > > > > > > > structure of
> > > > > > > > > the
> > > > > > > > > dump messages).
> > > > > > > > >
> > > > > > > > > Successfully bootstrapped & regrtested on x86_64-pc-
> > > > > > > > > linux-
> > > > > > > > > gnu.
> > > > > > > > >
> > > > > > > > > OK for trunk?
> > > > > > >
> > > > > > > Hi,
> > > > > > >
> > > > > > > I've noticed that this patch (r262246) caused regressions on
> > > > > > > aarch64:
> > > > > > >     gcc.dg/vect/slp-perm-1.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-1.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-2.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-2.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-3.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-3.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-5.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-5.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-6.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-6.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-7.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-7.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-8.c -flto -ffat-lto-objects  scan-
> > > > > > > tree-
> > > > > > > dump
> > > > > > > vect "note: Built SLP cancelled: can use load/store-lanes"
> > > > > > >     gcc.dg/vect/slp-perm-8.c scan-tree-dump vect "note: Built
> > > > > > > SLP
> > > > > > > cancelled: can use load/store-lanes"
> > > > > > >
> > > > > > > The problem is that now there are more spaces between "note:"
> > > > > > > and
> > > > > > > "Built", the attached small patch does that for slp-perm-1.c.
> > > > > >
> > > > > > Sorry about the breakage.
> > > > > >
> > > > > > > Is it the right way of fixing it or do we want to accept any
> > > > > > > amount
> > > > > > > of
> > > > > > > spaces for instance?
> > > > > >
> > > > > > I don't think we want to hardcode the amount of space in the
> > > > > > dumpfile.
> > > > > > The idea of my patch was to make the dump more human-readable
> > > > > > (I
> > > > > > hope)
> > > > > > by visualizing the nesting structure of the dump messages, but
> > > > > > I
> > > > > > think
> > > > > > we shouldn't "bake" that into the expected strings, as someone
> > > > > > might
> > > > > > want to add an intermediate nesting level.
> > > > > >
> > > > > > Do we really need to look for the "note:" in the scan-tree-
> > > > > > dump?
> > > > > > Should that directive be rewritten to:
> > > > > >
> > > > > > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can
> > > > > > use
> > > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > > vect_load_lanes } } } } */
> > > > > > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use
> > > > > > load/store-lanes" "vect" { target { vect_perm3_int &&
> > > > > > vect_load_lanes } } } } */
> > > > > >
> > > > > > which I believe would match any amount of spaces.
> > > > > >
> > > > > > Alternatively a regex accepting any amount of space ought to
> > > > > > work,
> > > > > > if
> > > > > > we care that the message begins with "note: ".
> > > > > >
> > > > > > The "note: " comes from dumpfile.c's dump_loc, and is emitted
> > > > > > regardless of whether it's a MSG_NOTE vs a
> > > > > > MSG_MISSED_OPTIMIZATION
> > > > > > or
> > > > > > whatever.  Given that, maybe we should just drop the "note: "
> > > > > > prefix
> > > > > > from these scan-tree-dump expected regexes?  (assuming that
> > > > > > works)
> > > > >
> > > > > I guess it was done for the -fopt-info output channel to match
> > > > > what
> > > > > we emit with inform () given those are neither warnings nor
> > > > > errors.
> > > > >
> > > > > But yes, lets elide "note: " from dumpfiles.  Be prepared to
> > > > > fiddle
> > > > > with expected scan-dumps though.
> > > >
> > > > Re-reading, I think I was unclear, but I was proposing removing it
> > > > from
> > > > the scan-tree-dump regexes, *not* from dumpfile.c (though I'm open
> > > > to
> > > > the latter).
> > >
> > > Both is fine with me.
> > >
> > > Richard.
> > >
> > > > >
> > > > > > > I'm surprised there is such little impact on the testsuite
> > > > > > > though.
> > > > > >
> > > > > > I see lots of scan-tree-dump* directives in the vect part of
> > > > > > the
> > > > > > testsuite, but it seems that only these ones use the "note: "
> > > > > > prefix; I
> > > > > > think everything else was matching against the message whilst
> > > > > > ignoring
> > > > > > the prefix, so it didn't matter when the prefix changed
> > > > > > (I double-checked and these scan-tree-dump directives didn't
> > > > > > trigger on
> > > > > > my x86_64 testing of the patch, due to { target {
> > > > > > vect_perm3_int &&
> > > > > > vect_load_lanes } }, where I see
> > > > > > check_effective_target_vect_load_lanes: returning 0 in the log)
> > > > > >
> > > > > > > If OK, I'll update the patch to take the other slp-perm-
> > > > > > > [235678].c
> > > > > > > tests into account.
> > > > > > >
> > > > > > > Thanks,
> > > > > > >
> > > > > > > Christophe
> > > > > >
> > > > > > Sorry again about the breakage.
> > > > > > Dave
> >
> > Here's a minimal patch to remove the "note: " prefix from the
> > affected directives.
> >
> > Christophe, does this fix the issues seen on aarch64?
> >
> > OK for trunk?
>
> OK.
>
Hi David,

Sorry I missed your email: of course it works, thanks!


> Richard.
>
> > gcc/testsuite/ChangeLog:
> >         * gcc.dg/vect/slp-perm-1.c: Remove "note: " prefix from
> >         scan-tree-dump directive.
> >         * gcc.dg/vect/slp-perm-2.c: Likewise.
> >         * gcc.dg/vect/slp-perm-3.c: Likewise.
> >         * gcc.dg/vect/slp-perm-5.c: Likewise.
> >         * gcc.dg/vect/slp-perm-6.c: Likewise.
> >         * gcc.dg/vect/slp-perm-7.c: Likewise.
> >         * gcc.dg/vect/slp-perm-8.c: Likewise.
> > ---
> >  gcc/testsuite/gcc.dg/vect/slp-perm-1.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-2.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-3.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-5.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-6.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-7.c | 2 +-
> >  gcc/testsuite/gcc.dg/vect/slp-perm-8.c | 2 +-
> >  7 files changed, 7 insertions(+), 7 deletions(-)
> >
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> > index 6bd16ef..ca7803e 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-1.c
> > @@ -82,7 +82,7 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && {! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> >
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> > index 4bab348..82776f3 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-2.c
> > @@ -56,6 +56,6 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> > index 568e400..1807275 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-3.c
> > @@ -69,7 +69,7 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm && {! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> >
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> > index 5293913..b86a3dc 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-5.c
> > @@ -106,7 +106,7 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> >
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> > index 4eb648a..97a0ebf 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-6.c
> > @@ -105,6 +105,6 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 2 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> > index baf7f78..346411f 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-7.c
> > @@ -98,6 +98,6 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  { target vect_perm } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_int && { ! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_int && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> > diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> > index 94d4455..17aa111 100644
> > --- a/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> > +++ b/gcc/testsuite/gcc.dg/vect/slp-perm-8.c
> > @@ -62,6 +62,6 @@ int main (int argc, const char* argv[])
> >  /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { target { vect_perm_byte } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 1 "vect" { target { vect_perm3_byte && { ! vect_load_lanes } } } } } */
> >  /* { dg-final { scan-tree-dump-times "vectorizing stmts using SLP" 0 "vect" { target vect_load_lanes } } } */
> > -/* { dg-final { scan-tree-dump "note: Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
> > +/* { dg-final { scan-tree-dump "Built SLP cancelled: can use load/store-lanes" "vect" { target { vect_perm3_byte && vect_load_lanes } } } } */
> >  /* { dg-final { scan-tree-dump "LOAD_LANES" "vect" { target vect_load_lanes } } } */
> >  /* { dg-final { scan-tree-dump "STORE_LANES" "vect" { target vect_load_lanes } } } */
> > --
> > 1.8.5.3
> >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/2] Add "optinfo" framework
  2018-07-02 20:51                           ` [PATCH 1/2] " David Malcolm
@ 2018-07-09 13:01                             ` Richard Biener
  2018-07-10 11:01                               ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-07-09 13:01 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch implements a way to consolidate dump_* calls into
> optinfo objects, as enabling work towards being able to write out
> optimization records to a file, or emit them as diagnostic "remarks".
>
> The patch adds the support for building optinfo instances from dump_*
> calls, but leaves implementing any *users* of them to followup patches.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

Looks good overall, but ...

To "fix" the GC issue you'd need to capture all possibly interesting
information from tree/gimple while it is still in flight.  This _may_ be
necessary anyway since I remember writing code like

  fprintf (dump_file, "old: ");
  print_gimple_stmt (..., stmt);
  gimple_set_rhs1 (stmt, op);
  fprintf (dump_file, "new: ");
  print_gmple_stmt (..., stmt);
  fprintf (dump_file, "\n");

capturing interesting information means we know all targeted
optinfo channels, right?  And the optinfo consumers
need to handle "streams" of input and may not look back.

I've yet have to look at the 2nd patch but can you comment on
this?  How difficult is it to re-wire how the data flows to make
stmt re-use like the above possible?

Thanks,
Richard.

> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add optinfo.o.
>         * coretypes.h (class symtab_node): New forward decl.
>         (struct cgraph_node): New forward decl.
>         (class varpool_node): New forward decl.
>         * dump-context.h: New file.
>         * dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
>         "tree-pass.h", "optinfo-internal.h".
>         (refresh_dumps_are_enabled): Use optinfo_enabled_p.
>         (set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
>         (set_alt_dump_file): Likewise.
>         (dump_context::~dump_context): New dtor.
>         (dump_gimple_stmt): Move implementation to...
>         (dump_context::dump_gimple_stmt): ...this new member function.
>         Add the stmt to any pending optinfo, creating one if need be.
>         (dump_gimple_stmt_loc): Move implementation to...
>         (dump_context::dump_gimple_stmt_loc): ...this new member function.
>         Convert param "loc" from location_t to const dump_location_t &.
>         Start a new optinfo and add the stmt to it.
>         (dump_generic_expr): Move implementation to...
>         (dump_context::dump_generic_expr): ...this new member function.
>         Add the tree to any pending optinfo, creating one if need be.
>         (dump_generic_expr_loc): Move implementation to...
>         (dump_context::dump_generic_expr_loc): ...this new member
>         function.  Add the tree to any pending optinfo, creating one if
>         need be.
>         (dump_printf): Move implementation to...
>         (dump_context::dump_printf_va): ...this new member function.  Add
>         the text to any pending optinfo, creating one if need be.
>         (dump_printf_loc): Move implementation to...
>         (dump_context::dump_printf_loc_va): ...this new member function.
>         Convert param "loc" from location_t to const dump_location_t &.
>         Start a new optinfo and add the stmt to it.
>         (dump_dec): Move implementation to...
>         (dump_context::dump_dec): ...this new member function.  Add the
>         value to any pending optinfo, creating one if need be.
>         (dump_context::dump_symtab_node): New member function.
>         (dump_context::get_scope_depth): New member function.
>         (dump_context::begin_scope): New member function.
>         (dump_context::end_scope): New member function.
>         (dump_context::ensure_pending_optinfo): New member function.
>         (dump_context::begin_next_optinfo): New member function.
>         (dump_context::end_any_optinfo): New member function.
>         (dump_context::s_current): New global.
>         (dump_context::s_default): New global.
>         (dump_scope_depth): Delete global.
>         (dumpfile_ensure_any_optinfo_are_flushed): New function.
>         (dump_symtab_node): New function.
>         (get_dump_scope_depth): Reimplement in terms of dump_context.
>         (dump_begin_scope): Likewise.
>         (dump_end_scope): Likewise.
>         (selftest::temp_dump_context::temp_dump_context): New ctor.
>         (selftest::temp_dump_context::~temp_dump_context): New dtor.
>         (selftest::assert_is_text): New support function.
>         (selftest::assert_is_tree): New support function.
>         (selftest::assert_is_gimple): New support function.
>         (selftest::test_capture_of_dump_calls): New test.
>         (selftest::dumpfile_c_tests): Call it.
>         * dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block,
>         dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc,
>         dump_gimple_stmt, dump_dec): Gather these related decls and add a
>         descriptive comment.
>         (dump_function, print_combine_total_stats, enable_rtl_dump_file,
>         dump_node, dump_bb): Move these unrelated decls.
>         (class dump_manager): Add leading comment.
>         * ggc-page.c (ggc_collect): Call
>         dumpfile_ensure_any_optinfo_are_flushed.
>         * optinfo-internal.h: New file.
>         * optinfo.cc: New file.
>         * optinfo.h: New file.
>         * selftest-run-tests.c (selftest::run_tests): Call
>         selftest::optinfo_cc_tests.
>         * selftest.h (selftest::optinfo_cc_tests): New decl.
> ---
>  gcc/Makefile.in          |   1 +
>  gcc/coretypes.h          |   7 +
>  gcc/dump-context.h       | 128 ++++++++++++
>  gcc/dumpfile.c           | 498 +++++++++++++++++++++++++++++++++++++++++++----
>  gcc/dumpfile.h           |  82 +++++---
>  gcc/ggc-page.c           |   2 +
>  gcc/optinfo-internal.h   | 148 ++++++++++++++
>  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++
>  gcc/optinfo.h            | 175 +++++++++++++++++
>  gcc/selftest-run-tests.c |   1 +
>  gcc/selftest.h           |   1 +
>  11 files changed, 1233 insertions(+), 61 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo-internal.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 66c8b6e..7d36a77 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1426,6 +1426,7 @@ OBJS = \
>         optabs-libfuncs.o \
>         optabs-query.o \
>         optabs-tree.o \
> +       optinfo.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..ed0e825 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>
> +/* Subclasses of symtab_node, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..753f714
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,128 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   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_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                            const dump_location_t &loc,
> +                            dump_flags_t extra_dump_flags,
> +                            gimple *gs, int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +                         dump_flags_t extra_dump_flags,
> +                         tree t);
> +
> +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> +                             const dump_location_t &loc,
> +                             dump_flags_t extra_dump_flags,
> +                             tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                      va_list ap) ATTRIBUTE_PRINTF (3, 0);
> +
> +  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
> +                          const char *format, va_list ap)
> +    ATTRIBUTE_PRINTF (4, 0);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool forcibly_enable_optinfo_p () const
> +  {
> +    return m_forcibly_enable_optinfo;
> +  }
> +
> +  void end_any_optinfo ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool m_forcibly_enable_optinfo;
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII-style class for use in selftests for temporarily using a different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool forcibly_enable_optinfo);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 5f69f9b..6e089ef 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -33,6 +33,11 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h" /* for dump_user_location_t ctor.  */
>  #include "rtl.h" /* for dump_user_location_t ctor.  */
>  #include "selftest.h"
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "cgraph.h"
> +#include "tree-pass.h" /* for "current_pass".  */
> +#include "optinfo-internal.h" /* for selftests.  */
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -64,7 +69,7 @@ bool dumps_are_enabled = false;
>  static void
>  refresh_dumps_are_enabled ()
>  {
> -  dumps_are_enabled = (dump_file || alt_dump_file);
> +  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
>  }
>
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
> @@ -73,6 +78,7 @@ refresh_dumps_are_enabled ()
>  void
>  set_dump_file (FILE *new_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -83,6 +89,7 @@ set_dump_file (FILE *new_dump_file)
>  static void
>  set_alt_dump_file (FILE *new_alt_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    alt_dump_file = new_alt_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -458,25 +465,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>      }
>  }
>
> +/* Implementation of dump_context member functions.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> +                               dump_flags_t extra_dump_flags,
> +                               gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
>  }
>
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
>
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs, int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -490,20 +516,35 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_stmt (gs, extra_dump_flags);
> +    }
>  }
>
>  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
>     DUMP_KIND is enabled.  */
>
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                  tree t)
> +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> +                                dump_flags_t extra_dump_flags,
> +                                tree t)
>  {
>    if (dump_file && (dump_kind & pflags))
>        print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, extra_dump_flags);
> +    }
>  }
>
>
> @@ -511,8 +552,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>     location.  */
>
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                      dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> +                                    const dump_location_t &loc,
> +                                    dump_flags_t extra_dump_flags,
> +                                    tree t)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -526,53 +569,82 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, extra_dump_flags);
> +    }
>  }
>
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                             va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> -/* Similar to dump_printf, except source location is also printed.  */
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
>
>  void
> -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                const char *format, ...)
> +dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
> +                                 const char *format, va_list ap)
>  {
>    location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> @@ -580,7 +652,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>
>  template<unsigned int N, typename C>
>  void
> -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> @@ -589,6 +661,203 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
> +    }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +void
> +dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  if (dump_file && (dump_kind & pflags))
> +    fprintf (dump_file, "%s", node->dump_name ());
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    fprintf (alt_dump_file, "%s", node->dump_name ());
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
> +    }
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Emit a "scope" optinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +void
> +dump_context::begin_scope (const char *name, const dump_location_t &loc)
> +{
> +  /* Specialcase, to avoid going through dump_printf_loc,
> +     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
> +
> +  if (dump_file)
> +    {
> +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> +      fprintf (dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context; emitting
> +   it to any destinations as appropriate - though none have currently been
> +   implemented.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   member functions.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                  tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
> +}
> +
> +/* Similar to dump_generic_expr, except additionally print the source
> +   location.  */
> +
> +void
> +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                      dump_flags_t extra_dump_flags, tree t)
> +{
> +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
> +                                             t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump streams.  */
> +
> +void
> +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);
>  template void dump_dec (dump_flags_t, const poly_offset_int &);
>  template void dump_dec (dump_flags_t, const poly_widest_int &);
>
> -/* The current dump scope-nesting depth.  */
> +/* Emit and delete the currently pending optinfo, if there is one,
> +   without the caller needing to know about class dump_context.  */
> +
> +void
> +dumpfile_ensure_any_optinfo_are_flushed ()
> +{
> +  dump_context::get().end_any_optinfo ();
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
>
> -static int dump_scope_depth;
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
>
>  /* Get the current dump scope-nesting depth.
> -   For use by dump_*_loc (for showing nesting via indentation).  */
> +   For use by -fopt-info (for showing nesting via indentation).  */
>
>  unsigned int
>  get_dump_scope_depth ()
>  {
> -  return dump_scope_depth;
> +  return dump_context::get ().get_scope_depth ();
>  }
>
>  /* Push a nested dump scope.
>     Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
>     destination, if any.
> +   Emit a "scope" opinfo if optinfos are enabled.
>     Increment the scope depth.  */
>
>  void
>  dump_begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> -  dump_scope_depth++;
> +  dump_context::get ().begin_scope (name, loc);
>  }
>
>  /* Pop a nested dump scope.  */
> @@ -627,7 +909,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc)
>  void
>  dump_end_scope ()
>  {
> -  dump_scope_depth--;
> +  dump_context::get ().end_scope ();
>  }
>
>  /* Start a dump for PHASE. Store user-supplied dump flags in
> @@ -1180,6 +1462,24 @@ enable_rtl_dump_file (void)
>
>  #if CHECKING_P
>
> +/* temp_dump_context's ctor.  Temporarily override the dump_context
> +   (to forcibly enable optinfo-generation).  */
> +
> +temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
> +: m_context (),
> +  m_saved (&dump_context ().get ())
> +{
> +  dump_context::s_current = &m_context;
> +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +}
> +
>  namespace selftest {
>
>  /* Verify that the dump_location_t constructors capture the source location
> @@ -1216,12 +1516,136 @@ test_impl_location ()
>  #endif
>  }
>
> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> +
> +static void
> +assert_is_text (const optinfo_item *item, const char *expected_text)
> +{
> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
> +  const optinfo_item_text * text_item
> +    = static_cast <const optinfo_item_text *> (item);
> +  ASSERT_STREQ (text_item->get_text (), expected_text);
> +}
> +
> +/* Verify that ITEM is a tree item, with the expected values.  */
> +
> +static void
> +assert_is_tree (const optinfo_item *item, tree expected_tree,
> +               dump_flags_t expected_dump_flags)
> +{
> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
> +  const optinfo_item_tree * tree_item
> +    = static_cast <const optinfo_item_tree *> (item);
> +  ASSERT_EQ (tree_item->get_node (), expected_tree);
> +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
> +}
> +
> +/* Verify that ITEM is a gimple item, with the expected values.  */
> +
> +static void
> +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
> +                 dump_flags_t expected_dump_flags)
> +{
> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
> +  const optinfo_item_gimple * gimple_item
> +    = static_cast <const optinfo_item_gimple *> (item);
> +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
> +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
> +}
> +
> +/* Verify that calls to the dump_* API are captured and consolidated into
> +   optimization records. */
> +
> +static void
> +test_capture_of_dump_calls ()
> +{
> +  dump_location_t loc;
> +
> +  /* Tree, via dump_generic_expr.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 2);
> +    assert_is_text (info->get_item (0), "test of tree: ");
> +    assert_is_tree (info->get_item (1), integer_zero_node, TDF_SLIM);
> +  }
> +
> +  /* Tree, via dump_generic_expr_loc.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 1);
> +    assert_is_tree (info->get_item (0), integer_one_node, TDF_SLIM);
> +  }
> +
> +  /* Gimple.  */
> +  {
> +    greturn *stmt = gimple_build_return (NULL);
> +
> +    temp_dump_context tmp (true);
> +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->num_items (), 1);
> +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
> +
> +    /* Verify that optinfo instances are flushed if a GC is about to
> +       happen (and thus don't need to be GTY-marked).
> +       We don't want them in the PCH file, but we don't want the
> +       items to have their data collected from under them.  */
> +    selftest::forcibly_ggc_collect ();
> +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);
> +  }
> +
> +  /* poly_int.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_dec (MSG_NOTE, poly_int64 (42));
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->num_items (), 1);
> +    assert_is_text (info->get_item (0), "42");
> +  }
> +
> +  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
> +     above.  */
> +  {
> +    /* MSG_OPTIMIZED_LOCATIONS.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_SUCCESS);
> +    }
> +
> +    /* MSG_MISSED_OPTIMIZATION.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_FAILURE);
> +    }
> +  }
> +}
> +
>  /* Run all of the selftests within this file.  */
>
>  void
>  dumpfile_c_tests ()
>  {
>    test_impl_location ();
> +  test_capture_of_dump_calls ();
>  }
>
>  } // namespace selftest
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 0e588a6..899bb89 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,30 +420,6 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> -extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
> -extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
> -                            const char *, ...) ATTRIBUTE_PRINTF_3;
> -extern void dump_function (int phase, tree fn);
> -extern void dump_basic_block (dump_flags_t, basic_block, int);
> -extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
> -                                  dump_flags_t, tree);
> -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> -extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
> -                                 dump_flags_t, gimple *, int);
> -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
> -extern void print_combine_total_stats (void);
> -extern bool enable_rtl_dump_file (void);
> -
> -template<unsigned int N, typename C>
> -void dump_dec (dump_flags_t, const poly_int<N, C> &);
> -
> -/* In tree-dump.c  */
> -extern void dump_node (const_tree, dump_flags_t, FILE *);
> -
> -/* In combine.c  */
> -extern void dump_combine_total_stats (FILE *);
> -/* In cfghooks.c  */
> -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
>
>  /* Global variables used to communicate with passes.  */
>  extern FILE *dump_file;
> @@ -461,6 +437,49 @@ dump_enabled_p (void)
>    return dumps_are_enabled;
>  }
>
> +/* The following API calls (which *don't* take a "FILE *")
> +   write the output to zero or more locations:
> +   (a) the active dump_file, if any
> +   (b) the -fopt-info destination, if any
> +   (c) to the "optinfo" destinations, if any:
> +
> +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> +                                   |
> +                                   +--> (b) alt_dump_file
> +                                   |
> +                                   `--> (c) optinfo
> +                                            `---> optinfo destinations
> +
> +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> +   instance: all subsequent dump_* calls are consolidated into
> +   that optinfo, until the next dump_*_loc call (or a change in
> +   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
> +
> +   A group of dump_* calls should be guarded by:
> +
> +     if (dump_enabled_p ())
> +
> +   to minimize the work done for the common case where dumps
> +   are disabled.  */
> +
> +extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
> +extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
> +                            const char *, ...) ATTRIBUTE_PRINTF_3;
> +extern void dump_function (int phase, tree fn);
> +extern void dump_basic_block (dump_flags_t, basic_block, int);
> +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> +extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
> +                                  dump_flags_t, tree);
> +extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
> +                                 dump_flags_t, gimple *, int);
> +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
> +extern void dump_symtab_node (dump_flags_t, symtab_node *);
> +
> +template<unsigned int N, typename C>
> +void dump_dec (dump_flags_t, const poly_int<N, C> &);
> +
> +extern void dumpfile_ensure_any_optinfo_are_flushed ();
> +
>  /* Managing nested scopes, so that dumps can express the call chain
>     leading to a dump message.  */
>
> @@ -500,8 +519,23 @@ class auto_dump_scope
>  #define AUTO_DUMP_SCOPE(NAME, LOC) \
>    auto_dump_scope scope (NAME, LOC)
>
> +extern void dump_function (int phase, tree fn);
> +extern void print_combine_total_stats (void);
> +extern bool enable_rtl_dump_file (void);
> +
> +/* In tree-dump.c  */
> +extern void dump_node (const_tree, dump_flags_t, FILE *);
> +
> +/* In combine.c  */
> +extern void dump_combine_total_stats (FILE *);
> +/* In cfghooks.c  */
> +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> +
>  namespace gcc {
>
> +/* A class for managing all of the various dump files used by the
> +   optimization passes.  */
> +
>  class dump_manager
>  {
>  public:
> diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
> index 51783e5..f3a2119 100644
> --- a/gcc/ggc-page.c
> +++ b/gcc/ggc-page.c
> @@ -2177,6 +2177,8 @@ ggc_collect (void)
>    if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)
>      return;
>
> +  dumpfile_ensure_any_optinfo_are_flushed ();
> +
>    timevar_push (TV_GC);
>    if (!quiet_flag)
>      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024);
> diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
> new file mode 100644
> index 0000000..8144174
> --- /dev/null
> +++ b/gcc/optinfo-internal.h
> @@ -0,0 +1,148 @@
> +/* Implementation details of optinfo.
> +   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_OPTINFO_INTERNAL_H
> +#define GCC_OPTINFO_INTERNAL_H
> +
> +/* Multiple dispatch: there are various kinds of items within an optinfo,
> +   and various destinations to send optinfo to.
> +
> +   Handling this for now by exposing all of the item subclasses,
> +   and having the destinations handle them with "switch" statements.  */
> +
> +/* An enum for discriminating between optinfo_item subclasses.  */
> +
> +enum optinfo_item_kind
> +{
> +  OPTINFO_ITEM_KIND_TEXT,
> +  OPTINFO_ITEM_KIND_TREE,
> +  OPTINFO_ITEM_KIND_GIMPLE,
> +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> +};
> +
> +/* Abstract base class for items within an optinfo.  */
> +
> +class optinfo_item
> +{
> + public:
> +  virtual ~optinfo_item () {}
> +
> +  virtual enum optinfo_item_kind get_kind () const = 0;
> +};
> +
> +/* optinfo_item subclasses.  */
> +
> +/* Item within an optinfo: text, either owned by the item
> +   (for optinfo_printf), or borrowed (for string literals).  */
> +
> +class optinfo_item_text : public optinfo_item
> +{
> + public:
> +  optinfo_item_text (char *text, bool owned)
> +  : m_text (text), m_owned (owned)
> +  {}
> +  ~optinfo_item_text ()
> +  {
> +    if (m_owned)
> +      free (m_text);
> +  }
> +
> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> +  {
> +    return OPTINFO_ITEM_KIND_TEXT;
> +  }
> +
> +  const char *get_text () const { return m_text; }
> +
> +  void trim_trailing_whitespace ();
> +
> + private:
> +  char *m_text;
> +  bool m_owned;
> +};
> +
> +/* Item within an optinfo: a tree, with dump flags.
> +   Note that this is not GTY-marked; see the description of
> +   class optinfo for a discussion of the interaction with the
> +   garbage-collector.  */
> +
> +class optinfo_item_tree : public optinfo_item
> +{
> + public:
> +  optinfo_item_tree (tree node, dump_flags_t dump_flags)
> +    : m_node (node), m_dump_flags (dump_flags)
> +  {}
> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> +  {
> +    return OPTINFO_ITEM_KIND_TREE;
> +  }
> +
> +  tree get_node () const { return m_node; }
> +  dump_flags_t get_flags () const { return m_dump_flags; }
> +
> + private:
> +  tree m_node;
> +  dump_flags_t m_dump_flags;
> +};
> +
> +/* Item within an optinfo: a gimple statement.
> +   Note that this is not GTY-marked; see the description of
> +   class optinfo for a discussion of the interaction with the
> +   garbage-collector.  */
> +
> +class optinfo_item_gimple : public optinfo_item
> +{
> + public:
> +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
> +    : m_stmt (stmt), m_dump_flags (dump_flags) {}
> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> +  {
> +    return OPTINFO_ITEM_KIND_GIMPLE;
> +  }
> +
> +  gimple *get_stmt () const { return m_stmt; }
> +  dump_flags_t get_flags () const { return m_dump_flags; }
> +
> + private:
> +  gimple *m_stmt;
> +  dump_flags_t m_dump_flags;
> +};
> +
> +/* Item within an optinfo: a symbol table node.
> +   Note that this is not GTY-marked; see the description of
> +   class optinfo for a discussion of the interaction with the
> +   garbage-collector.  */
> +
> +class optinfo_item_symtab_node : public optinfo_item
> +{
> + public:
> +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> +  {
> +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
> +  }
> +
> +  symtab_node *get_node () const { return m_node; }
> +
> + private:
> +  symtab_node *m_node;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> new file mode 100644
> index 0000000..1da7d37
> --- /dev/null
> +++ b/gcc/optinfo.cc
> @@ -0,0 +1,251 @@
> +/* Optimization information.
> +   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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +
> +#include "optinfo.h"
> +#include "optinfo-internal.h"
> +#include "dump-context.h"
> +#include "selftest.h"
> +
> +/* Remove any trailing whitespace characters from this text item.
> +   Primarily for use in stripping trailing newline characters when
> +   emitting remarks (since the diagnostic subsystem doesn't expect
> +   trailing newlines in messages).  */
> +
> +void
> +optinfo_item_text::trim_trailing_whitespace ()
> +{
> +  size_t len = strlen (m_text);
> +  if (len == 0)
> +    return;
> +
> +  size_t new_len = len;
> +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
> +    new_len--;
> +
> +  if (new_len == len)
> +    return;
> +
> +  if (m_owned)
> +    m_text[new_len] = '\0';
> +  else
> +    {
> +      m_text = xstrndup (m_text, new_len);
> +      m_owned = true;
> +    }
> +}
> +
> +/* Get a string from KIND.  */
> +
> +const char *
> +optinfo_kind_to_string (enum optinfo_kind kind)
> +{
> +  switch (kind)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case OPTINFO_KIND_SUCCESS:
> +      return "success";
> +    case OPTINFO_KIND_FAILURE:
> +      return "failure";
> +    case OPTINFO_KIND_NOTE:
> +      return "note";
> +    case OPTINFO_KIND_SCOPE:
> +      return "scope";
> +    }
> +}
> +
> +/* optinfo's dtor.  */
> +
> +optinfo::~optinfo ()
> +{
> +  /* Cleanup.  */
> +  unsigned i;
> +  optinfo_item *item;
> +  FOR_EACH_VEC_ELT (m_items, i, item)
> +    delete item;
> +}
> +
> +/* Emit the optinfo to all of the active destinations.  */
> +
> +void
> +optinfo::emit ()
> +{
> +  /* Eliminate any trailing whitespace.  */
> +  while (m_items.length () > 0)
> +    {
> +      optinfo_item *last_item = m_items[m_items.length () - 1];
> +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
> +       break;
> +
> +      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
> +      last_text->trim_trailing_whitespace ();
> +
> +      if (strlen (last_text->get_text ()) > 0)
> +       break;
> +
> +      m_items.pop ();
> +      delete last_item;
> +    }
> +
> +  /* currently this is a no-op.  */
> +}
> +
> +/* Update the optinfo's kind based on DUMP_KIND.  */
> +
> +void
> +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> +{
> +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> +    m_kind = OPTINFO_KIND_SUCCESS;
> +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> +    m_kind = OPTINFO_KIND_FAILURE;
> +  else if (dump_kind & MSG_NOTE)
> +    m_kind = OPTINFO_KIND_NOTE;
> +}
> +
> +/* Append a string literal to this optinfo.  */
> +
> +void
> +optinfo::add_string (const char *str)
> +{
> +  optinfo_item *item
> +    = new optinfo_item_text (const_cast <char *> (str), false);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf (const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  add_printf_va (format, ap);
> +  va_end (ap);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf_va (const char *format, va_list ap)
> +{
> +  char *formatted_text = xvasprintf (format, ap);
> +  optinfo_item *item
> +    = new optinfo_item_text (formatted_text, true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a gimple statement to this optinfo.  */
> +
> +void
> +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
> +{
> +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
> +}
> +
> +/* Append a tree node to this optinfo.  */
> +
> +void
> +optinfo::add_tree (tree node, dump_flags_t dump_flags)
> +{
> +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
> +}
> +
> +/* Append a symbol table node to this optinfo.  */
> +
> +void
> +optinfo::add_symtab_node (symtab_node *node)
> +{
> +  m_items.safe_push (new optinfo_item_symtab_node (node));
> +}
> +
> +/* Append the decimal represenation of a wide_int_ref to this
> +   optinfo.  */
> +
> +void
> +optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> +{
> +  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> +  print_dec (wi, buf, sgn);
> +  optinfo_item *item
> +    = new optinfo_item_text (xstrdup (buf), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +bool optinfo_enabled_p ()
> +{
> +  /* Currently no destinations are implemented, just a hook for
> +     selftests.  */
> +  return dump_context::get ().forcibly_enable_optinfo_p ();
> +}
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +bool optinfo_wants_inlining_info_p ()
> +{
> +  return false;
> +}
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Verify that optinfo_item_text::trim_trailing_whitespace turns
> +   INPUT into EXPECTED.  */
> +
> +static void
> +test_trim_trailing_whitespace (const char *input, const char *expected)
> +{
> +  optinfo_item_text item (const_cast <char *> (input), false);
> +  item.trim_trailing_whitespace ();
> +  ASSERT_STREQ (item.get_text (), expected);
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +optinfo_cc_tests ()
> +{
> +  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
> +  test_trim_trailing_whitespace ("", "");
> +  test_trim_trailing_whitespace ("\n", "");
> +  test_trim_trailing_whitespace ("foo", "foo");
> +  test_trim_trailing_whitespace ("foo\n", "foo");
> +  test_trim_trailing_whitespace ("foo\n\n", "foo");
> +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
> +}
> +
> +} // namespace selftest
> +
> +#endif /* CHECKING_P */
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> new file mode 100644
> index 0000000..0d49823
> --- /dev/null
> +++ b/gcc/optinfo.h
> @@ -0,0 +1,175 @@
> +/* Optimization information.
> +   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_OPTINFO_H
> +#define GCC_OPTINFO_H
> +
> +/* An "optinfo" is a bundle of information describing part of an
> +   optimization, which can be emitted to zero or more of several
> +   destinations, such as:
> +
> +   * as a "remark" through the diagnostics subsystem
> +
> +   * saved to a file as an "optimization record"
> +
> +   Currently no such destinations are implemented.
> +
> +   They are generated in response to calls to the "dump_*" API in
> +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> +   into a pending optinfo instance, with a "dump_*_loc" starting a new
> +   optinfo instance.
> +
> +   The data sent to the dump calls are captured within the pending optinfo
> +   instance as a sequence of optinfo_items.  For example, given:
> +
> +      if (dump_enabled_p ())
> +        {
> +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                           "not vectorized: live stmt not supported: ");
> +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
> +        }
> +
> +   the "dump_printf_loc" call begins a new optinfo containing two items:
> +   (1) a text item containing "not vectorized: live stmt not supported: "
> +   (2) a gimple item for "stmt"
> +
> +   Dump destinations are thus able to access rich metadata about the
> +   items when the optinfo is emitted to them, rather than just having plain
> +   text.  For example, when saving the above optinfo to a file as an
> +   "optimization record", the record could capture the source location of
> +   "stmt" above, rather than just its textual form.
> +
> +   The currently pending optinfo is emitted and deleted:
> +   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
> +   * when the dump files are changed (at the end of a pass), or
> +   * when a garbage collection is about to happen.  This safety measure
> +     ensures that no GC happens during the lifetime of an optinfo instance,
> +     and thus that any optinfo_items within the optinfo instances can refer
> +     to GC-allocated objects without needing to be GTY-marked: they will never
> +     refer to collected garbage.  optinfo and optinfo_item are not GTY-marked
> +     as it would make no sense for them to be in PCH files.
> +
> +   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
> +   instances), so all usage should be guarded by
> +
> +     if (optinfo_enabled_p ())
> +
> +   which is off by default.  */
> +
> +
> +/* Forward decls.  */
> +struct opt_pass;
> +class optinfo_item; /* optinfo-internal.h.  */
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +extern bool optinfo_enabled_p ();
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +extern bool optinfo_wants_inlining_info_p ();
> +
> +/* The various kinds of optinfo.  */
> +
> +enum optinfo_kind
> +{
> +  OPTINFO_KIND_SUCCESS,
> +  OPTINFO_KIND_FAILURE,
> +  OPTINFO_KIND_NOTE,
> +  OPTINFO_KIND_SCOPE
> +};
> +
> +extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
> +
> +/* A bundle of information describing part of an optimization.  */
> +
> +class optinfo
> +{
> +  friend class dump_context;
> +
> + public:
> +  optinfo (const dump_location_t &loc,
> +          enum optinfo_kind kind,
> +          opt_pass *pass)
> +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> +  {}
> +  ~optinfo ();
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_loc.get_user_location (); }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_loc.get_impl_location (); }
> +
> +  enum optinfo_kind get_kind () const { return m_kind; }
> +  opt_pass *get_pass () const { return m_pass; }
> +  unsigned int num_items () const { return m_items.length (); }
> +  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
> +
> +  location_t get_location_t () const { return m_loc.get_location_t (); }
> +  profile_count get_count () const { return m_loc.get_count (); }
> +
> + private:
> +  void emit ();
> +
> +  /* Pre-canned ways of manipulating the optinfo, for use by friend class
> +     dump_context.  */
> +  void handle_dump_file_kind (dump_flags_t);
> +  void add_string (const char *str);
> +  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
> +  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
> +  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
> +  void add_tree (tree node, dump_flags_t dump_flags);
> +  void add_symtab_node (symtab_node *node);
> +  void add_dec (const wide_int_ref &wi, signop sgn);
> +
> +  template<unsigned int N, typename C>
> +  void add_poly_int (const poly_int<N, C> &value)
> +  {
> +    /* Compare with dump_dec (MSG_NOTE, ).  */
> +
> +    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> +    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> +
> +    if (value.is_constant ())
> +      add_dec (value.coeffs[0], sgn);
> +    else
> +      {
> +       add_string ("[");
> +       for (unsigned int i = 0; i < N; ++i)
> +         {
> +           add_dec (value.coeffs[i], sgn);
> +           add_string (i == N - 1 ? "]" : ",");
> +         }
> +      }
> +  }
> +
> + private:
> +  dump_location_t m_loc;
> +  enum optinfo_kind m_kind;
> +  opt_pass *m_pass;
> +  auto_vec <optinfo_item *> m_items;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_H */
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index 7f4d6f3..989c50a 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -72,6 +72,7 @@ selftest::run_tests ()
>    typed_splay_tree_c_tests ();
>    unique_ptr_tests_cc_tests ();
>    opt_proposer_c_tests ();
> +  optinfo_cc_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index 54fc488..48881c9 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -228,6 +228,7 @@ extern void gimple_c_tests ();
>  extern void hash_map_tests_c_tests ();
>  extern void hash_set_tests_c_tests ();
>  extern void input_c_tests ();
> +extern void optinfo_cc_tests ();
>  extern void predict_c_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/2] optinfo: add diagnostic remarks
  2018-07-02 20:51                           ` [PATCH 2/2] optinfo: add diagnostic remarks David Malcolm
@ 2018-07-09 13:05                             ` Richard Biener
  2018-07-11 10:53                               ` [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-07-09 13:05 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch adds the first destination for optinfo instances to be
> emitted to: as "remarks" through the diagnostics subsystem.
>
> Examples can be seen at
>   https://dmalcolm.fedorapeople.org/gcc/2018-06-18/test.cc.remarks.html
>
> Remarks look a lot like the output of -fopt-info, with the following
> differences:
>
> * announcing "In function 'blah':" etc when the function changes.
>
> * printing the corresponding source lines (unless
>   "-fno-diagnostics-show-caret"), as per other diagnostics, and
>
> * colorizing the various parts of the message if stderr is at a tty.
>
> * showing extra metadata:
>   * the pass that emitted the remark,
>   * the execution count of the code in question
>   * which file/line/function of GCC emitted the remark
>
> * possibly allowing for remarks to be used in DejaGnu tests to better
>   associate testing of an optimization with the source line in
>   question, rather than relying on a scan of the dumpfile (which is
>   per-source file and thus rather "coarse-grained").*
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> (see the notes in the cover letter about the state of this patch;
> posting for motivation of the optinfo stuff).

As an overall comment I do like this as it is an improvement
over -fopt-info which is currently the user-facing way of
showing remarks about what code is optimized and what
not and why.

But for this very reason I don't like introducing a separate
set of options.  This means the remark stuff should
replace -fopt-info (and re-use that flag as far as possible).
This eventually means some -fXYZ might reduce the
verbosity level (source line / hotness, etc.) to the current
state - but I don't think we have made any guarantees
about the current format.  Given there'll be the JSON stuff
for machine-consumption I am also not worried about
existing machines parsing the stuff.

Anyway, I think the issue outlined in the comment to 1/n
needs to be addressed first.

Thanks for working on this!
Richard.

> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add optinfo-emit-diagnostics.o.
>         * common.opt (fremarks): New option.
>         (fdiagnostics-show-remark-hotness): New option.
>         (fdiagnostics-show-remark-origin): New option.
>         (fdiagnostics-show-remark-pass): New option.
>         * diagnostic-color.c (color_dict): Add "remark", as bold green.
>         * diagnostic-core.h (remark): New decl.
>         * diagnostic.c (diagnostic_action_after_output): Handle DK_REMARK.
>         (remark): New function.
>         * diagnostic.def (DK_REMARK): New diagnostic kind.
>         * doc/invoke.texi (Remarks): New section.
>         (-fremarks): New option.
>         (-fno-diagnostics-show-remark-hotness): New option.
>         (-fno-diagnostics-show-remark-origin): New option.
>         (-fno-diagnostics-show-remark-pass): New option.
>         * dumpfile.c (dump_context::get_scope_depth): Update comment.
>         (dump_context::end_any_optinfo): Likewise.
>         * dumpfile.h: Update comment.
>         * opt-functions.awk (function): Handle "Remark" by adding
>         CL_REMARK.
>         * optinfo-emit-diagnostics.cc: New file.
>         * optinfo-emit-diagnostics.h: New file.
>         * optinfo.cc: Include "optinfo-emit-diagnostics.h".
>         (optinfo::emit): Call emit_optinfo_as_diagnostic_remark.
>         (optinfo_enabled_p): Use flag_remarks.
>         * optinfo.h: Update comment.
>         * opts.c (print_specific_help): Handle CL_REMARK.
>         (common_handle_option): Likewise.
>         * opts.h (CL_REMARK): New macro.
>         (CL_MAX_OPTION_CLASS): Update for CL_REMARK.
>         (CL_JOINED, CL_SEPARATE, CL_UNDOCUMENTED, CL_NO_DWARF_RECORD)
>         (CL_PCH_IGNORE): Likewise.
>         * profile-count.c (profile_quality_as_string): New function.
>         * profile-count.h (profile_quality_as_string): New decl.
>         (profile_count::quality): New accessor.
>         * selftest-run-tests.c (selftest::run_tests): Call
>         selftest::optinfo_emit_diagnostics_cc_tests.
>         * selftest.h (selftest::optinfo_emit_diagnostics_cc_tests): New
>         decl.
>
> gcc/fortran/ChangeLog:
>         * gfc-diagnostic.def (DK_REMARK): New diagnostic kind.
>
> gcc/testsuite/ChangeLog:
>         * gcc.dg/plugin/plugin.exp (plugin_test_list): Add
>         remarks_plugin.c.
>         * gcc.dg/plugin/remarks-1.c: New test.
>         * gcc.dg/plugin/remarks_plugin.c: New test plugin.
>         * lib/gcc-dg.exp (dg-remark): New function.
> ---
>  gcc/Makefile.in                              |   1 +
>  gcc/common.opt                               |  17 ++
>  gcc/diagnostic-color.c                       |   2 +
>  gcc/diagnostic-core.h                        |   2 +
>  gcc/diagnostic.c                             |  17 ++
>  gcc/diagnostic.def                           |   1 +
>  gcc/doc/invoke.texi                          |  67 ++++++
>  gcc/dumpfile.c                               |   5 +-
>  gcc/dumpfile.h                               |   2 +
>  gcc/fortran/gfc-diagnostic.def               |   1 +
>  gcc/opt-functions.awk                        |   1 +
>  gcc/optinfo-emit-diagnostics.cc              | 317 +++++++++++++++++++++++++++
>  gcc/optinfo-emit-diagnostics.h               |  26 +++
>  gcc/optinfo.cc                               |   9 +-
>  gcc/optinfo.h                                |   4 +-
>  gcc/opts.c                                   |   4 +
>  gcc/opts.h                                   |  13 +-
>  gcc/profile-count.c                          |  28 +++
>  gcc/profile-count.h                          |   5 +
>  gcc/selftest-run-tests.c                     |   1 +
>  gcc/selftest.h                               |   1 +
>  gcc/testsuite/gcc.dg/plugin/plugin.exp       |   2 +
>  gcc/testsuite/gcc.dg/plugin/remarks-1.c      |  30 +++
>  gcc/testsuite/gcc.dg/plugin/remarks_plugin.c | 152 +++++++++++++
>  gcc/testsuite/lib/gcc-dg.exp                 |   9 +
>  25 files changed, 701 insertions(+), 16 deletions(-)
>  create mode 100644 gcc/optinfo-emit-diagnostics.cc
>  create mode 100644 gcc/optinfo-emit-diagnostics.h
>  create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 7d36a77..232cae4 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1427,6 +1427,7 @@ OBJS = \
>         optabs-query.o \
>         optabs-tree.o \
>         optinfo.o \
> +       optinfo-emit-diagnostics.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 5a50bc27..908432e 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -510,6 +510,11 @@ Driver Negative(Qn)
>  R
>  Driver Joined Separate
>
> +fremarks
> +Common Remark Var(flag_remarks)
> +Emit diagnostic remarks about optimizations
> +FIXME: should this be -fdiagnostics-foo or somesuch?
> +
>  S
>  Driver
>
> @@ -1281,6 +1286,18 @@ fdiagnostics-show-option
>  Common Var(flag_diagnostics_show_option) Init(1)
>  Amend appropriate diagnostic messages with the command line option that controls them.
>
> +fdiagnostics-show-remark-hotness
> +Common Var(flag_diagnostics_show_remark_hotness) Init(1)
> +When emitting optimization remarks, show the execution count of the code being optimized.
> +
> +fdiagnostics-show-remark-origin
> +Common Var(flag_diagnostics_show_remark_origin) Init(1)
> +When emitting optimization remarks, show which line of GCC code produced the remark.
> +
> +fdiagnostics-show-remark-pass
> +Common Var(flag_diagnostics_show_remark_pass) Init(1)
> +When emitting optimization remarks, show which optimization pass produced the remark.
> +
>  fdisable-
>  Common Joined RejectNegative Var(common_deferred_options) Defer
>  -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 disables an optimization pass.
> diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
> index 3ee21bc..bcc3767 100644
> --- a/gcc/diagnostic-color.c
> +++ b/gcc/diagnostic-color.c
> @@ -84,6 +84,8 @@ static struct color_cap color_dict[] =
>    { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
>    { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
>                7, false },
> +  { "remark", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN),
> +              6, false },
>    { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
>    { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
>    { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
> diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
> index aa5807e..63166b8 100644
> --- a/gcc/diagnostic-core.h
> +++ b/gcc/diagnostic-core.h
> @@ -69,6 +69,8 @@ extern bool warning_at (location_t, int, const char *, ...)
>      ATTRIBUTE_GCC_DIAG(3,4);
>  extern bool warning_at (rich_location *, int, const char *, ...)
>      ATTRIBUTE_GCC_DIAG(3,4);
> +extern bool remark (location_t, int, const char *, ...)
> +    ATTRIBUTE_GCC_DIAG(3,4);
>  extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
>  extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
>                      const char *, ...)
> diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
> index e22c17b..97ed88b 100644
> --- a/gcc/diagnostic.c
> +++ b/gcc/diagnostic.c
> @@ -492,6 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
>      {
>      case DK_DEBUG:
>      case DK_NOTE:
> +    case DK_REMARK:
>      case DK_ANACHRONISM:
>      case DK_WARNING:
>        break;
> @@ -1274,6 +1275,22 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
>    return ret;
>  }
>
> +/* Emit an optimization remark at LOCATION.  This is used by the optinfo
> +   framework.
> +   Return true if the remark was printed, false if it was inhibited.  */
> +// FIXME: we don't yet have a more fine-grained way of inhibiting remarks
> +
> +bool
> +remark (location_t location, int opt, const char *gmsgid, ...)
> +{
> +  va_list ap;
> +  va_start (ap, gmsgid);
> +  rich_location richloc (line_table, location);
> +  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_REMARK);
> +  va_end (ap);
> +  return ret;
> +}
> +
>  /* A "pedantic" warning at LOCATION: issues a warning unless
>     -pedantic-errors was given on the command line, in which case it
>     issues an error.  Use this for diagnostics required by the relevant
> diff --git a/gcc/diagnostic.def b/gcc/diagnostic.def
> index ce3dc56..b58095d 100644
> --- a/gcc/diagnostic.def
> +++ b/gcc/diagnostic.def
> @@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented: ", "error")
>  DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
> +DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark: ", "remark")
>  DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
>  /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
>  prefix does not matter.  */
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 248e603..1a3c1a6 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -141,6 +141,7 @@ only one of these two forms, whichever one is not the default.
>  * Debugging Options::   Producing debuggable code.
>  * Optimize Options::    How much optimization?
>  * Instrumentation Options:: Enabling profiling and extra run-time error checking.
> +* Remarks::             Details on how your code is being optimized.
>  * Preprocessor Options:: Controlling header files and macro definitions.
>                           Also, getting dependency information for Make.
>  * Assembler Options::   Passing options to the assembler.
> @@ -471,6 +472,11 @@ Objective-C and Objective-C++ Dialects}.
>  -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
>  -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}}
>
> +@item Remarks
> +@xref{Remarks,,Options to Control Remarks from the Compiler}.
> +@gccoptlist{-fremarks -fno-diagnostics-show-remark-hotness @gol
> +-fno-diagnostics-show-remark-origin -fno-diagnostics-show-remark-pass}
> +
>  @item Preprocessor Options
>  @xref{Preprocessor Options,,Options Controlling the Preprocessor}.
>  @gccoptlist{-A@var{question}=@var{answer} @gol
> @@ -12040,6 +12046,67 @@ The NOP instructions are inserted at---and maybe before, depending on
>  @end table
>
>
> +@node Remarks
> +@section Options to Control Remarks from the Compiler
> +@cindex remarks
> +@cindex options, remarks
> +
> +These options are aimed at advanced users who may be interested
> +in seeing additional diagnostics from the compiler, giving information
> +on the decisions it is making on the code.
> +
> +The precise messages and their format are subject to change.
> +
> +@table @gcctabopt
> +@item -fremarks
> +@opindex fremarks
> +Emit diagnostic remarks about optimizations.
> +
> +Enabling this option leads to GCC emitting diagnostics detailing the
> +optimization decisions it is making.
> +
> +For example, this message:
> +
> +@smallexample
> +test.c:13:3: remark:   Symbolic number of iterations is '(unsigned int) n_8(D)' [pass=vect] [count(precise)=76800000] [../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]
> +@end smallexample
> +
> +describes an internal detail of the ``vect'' optimization pass, acting at
> +the given source location within ``test.c'', where the remark was emitted
> +by the function ``vect_analyze_loop_form'' at line 1387 of GCC's source
> +file ``tree-vect-loop.c''.
> +
> +@item -fno-diagnostics-show-remark-hotness
> +@opindex fno-diagnostics-show-remark-hotness
> +@opindex fdiagnostics-show-remark-hotness
> +By default, if diagnostic remarks are enabled, they include information
> +on the execution count, or ``hotness'', of the code being optimized:
> +the ``[count(precise)=76800000]'' in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@item -fno-diagnostics-show-remark-origin
> +@opindex fno-diagnostics-show-remark-origin
> +@opindex fdiagnostics-show-remark-origin
> +By default, if diagnostic remarks are enabled, they include information
> +on where in GCC's own source code (or a plugin's source code) the remark
> +is being emitted from; the
> +``[../../src/gcc/tree-vect-loop.c:1387:vect_analyze_loop_form]''
> +in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@item -fno-diagnostics-show-remark-pass
> +@opindex fno-diagnostics-show-remark-pass
> +@opindex fdiagnostics-show-remark-pass
> +By default, if diagnostic remarks are enabled, they include information
> +on which optimization pass emitted the remark: the ``[pass=vect]''
> +in the example above.
> +
> +This option suppresses this part of the remark.
> +
> +@end table
> +
>  @node Preprocessor Options
>  @section Options Controlling the Preprocessor
>  @cindex preprocessor options
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 6e089ef..955fd57 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -690,7 +690,7 @@ dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
>  }
>
>  /* Get the current dump scope-nesting depth.
> -   For use by -fopt-info (for showing nesting via indentation).  */
> +   For use by remarks and -fopt-info (for showing nesting via indentation).  */
>
>  unsigned int
>  dump_context::get_scope_depth () const
> @@ -766,8 +766,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc)
>  }
>
>  /* End any optinfo that has been accumulated within this context; emitting
> -   it to any destinations as appropriate - though none have currently been
> -   implemented.  */
> +   it to any destinations as appropriate, such as a diagnostic "remark".  */
>
>  void
>  dump_context::end_any_optinfo ()
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 899bb89..514efe3 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -442,6 +442,7 @@ dump_enabled_p (void)
>     (a) the active dump_file, if any
>     (b) the -fopt-info destination, if any
>     (c) to the "optinfo" destinations, if any:
> +       (c.1) as diagnostic "remarks"
>
>     dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
>                                     |
> @@ -449,6 +450,7 @@ dump_enabled_p (void)
>                                     |
>                                     `--> (c) optinfo
>                                              `---> optinfo destinations
> +                                                  (c.1) diagnostic "remarks"
>
>     For optinfos, the dump_*_loc mark the beginning of an optinfo
>     instance: all subsequent dump_* calls are consolidated into
> diff --git a/gcc/fortran/gfc-diagnostic.def b/gcc/fortran/gfc-diagnostic.def
> index 565fa83..a10b6aa 100644
> --- a/gcc/fortran/gfc-diagnostic.def
> +++ b/gcc/fortran/gfc-diagnostic.def
> @@ -37,6 +37,7 @@ DEFINE_DIAGNOSTIC_KIND (DK_SORRY, "sorry, unimplemented", "error")
>  DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "Warning", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism", "warning")
>  DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note", "note")
> +DEFINE_DIAGNOSTIC_KIND (DK_REMARK, "remark ", "remark")
>  DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug", "note")
>  /* These two would be re-classified as DK_WARNING or DK_ERROR, so the
>  prefix does not matter.  */
> diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk
> index 2c371e5..48ecac5 100644
> --- a/gcc/opt-functions.awk
> +++ b/gcc/opt-functions.awk
> @@ -105,6 +105,7 @@ function switch_flags (flags)
>           test_flag("Undocumented", flags,  " | CL_UNDOCUMENTED") \
>           test_flag("NoDWARFRecord", flags,  " | CL_NO_DWARF_RECORD") \
>           test_flag("Warning", flags,  " | CL_WARNING") \
> +         test_flag("Remark", flags,  " | CL_REMARK") \
>           test_flag("(Optimization|PerFunction)", flags,  " | CL_OPTIMIZATION")
>         sub( "^0 \\| ", "", result )
>         return result
> diff --git a/gcc/optinfo-emit-diagnostics.cc b/gcc/optinfo-emit-diagnostics.cc
> new file mode 100644
> index 0000000..5320379
> --- /dev/null
> +++ b/gcc/optinfo-emit-diagnostics.cc
> @@ -0,0 +1,317 @@
> +/* Emit optimization information as "remark" diagnostics.
> +   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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "tree-pass.h"
> +#include "gimple.h"
> +#include "pretty-print.h"
> +#include "tree-pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "diagnostic.h"
> +#include "diagnostic-color.h"
> +#include "options.h"
> +#include "cgraph.h"
> +
> +#include "optinfo.h"
> +#include "optinfo-emit-diagnostics.h"
> +#include "optinfo-internal.h"
> +#include "dump-context.h"
> +#include "selftest.h"
> +#include "context.h"
> +#include "pass_manager.h"
> +
> +/* Print the items within OPTINFO to PP (as part of a remark).  */
> +
> +static void
> +print_optinfo_items (pretty_printer *pp, const optinfo *optinfo)
> +{
> +  bool show_color = pp_show_color (pp);
> +
> +  for (unsigned i = 0; i < optinfo->num_items (); i++)
> +    {
> +      const optinfo_item *item = optinfo->get_item (i);
> +      switch (item->get_kind ())
> +       {
> +       default:
> +         gcc_unreachable ();
> +       case OPTINFO_ITEM_KIND_TEXT:
> +         {
> +           const optinfo_item_text *as_text
> +             = (const optinfo_item_text *)item;
> +           pp_string (pp, as_text->get_text ());
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_TREE:
> +         {
> +           const optinfo_item_tree *as_tree
> +             = (const optinfo_item_tree *)item;
> +           pp_begin_quote (pp, show_color);
> +           dump_generic_node (pp, as_tree->get_node (), 0, TDF_DETAILS,
> +                              false);
> +           pp_end_quote (pp, show_color);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_GIMPLE:
> +         {
> +           const optinfo_item_gimple *as_gimple
> +             = (const optinfo_item_gimple *)item;
> +           gimple *stmt = as_gimple->get_stmt ();
> +           pp_begin_quote (pp, show_color);
> +           pp_gimple_stmt_1 (pp, stmt, 0, TDF_SLIM);
> +           pp_end_quote (pp, show_color);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_SYMTAB_NODE:
> +         {
> +           const optinfo_item_symtab_node *as_symtab_node
> +             = (const optinfo_item_symtab_node *)item;
> +           symtab_node *node = as_symtab_node->get_node ();
> +           pp_begin_quote (pp, show_color);
> +           pp_string (pp, node->dump_name ());
> +           pp_end_quote (pp, show_color);
> +         }
> +         break;
> +       }
> +    }
> +}
> +
> +/* Print PASS to PP (as part of a remark).  */
> +
> +static void
> +print_pass (pretty_printer *pp, opt_pass *pass)
> +{
> +  if (pass == NULL)
> +    return;
> +
> +  bool show_color = pp_show_color (pp);
> +
> +  pp_string (pp, " [");
> +  pp_string (pp, colorize_start (show_color,
> +                                diagnostic_get_color_for_kind (DK_REMARK)));
> +  pp_string (pp, "pass=");
> +  pp_string (pp, pass->name);
> +  pp_string (pp, colorize_stop (show_color));
> +  pp_string (pp, "]");
> +}
> +
> +/* Print COUNT to PP (as part of a remark).  */
> +
> +static void
> +print_count (pretty_printer *pp, profile_count count)
> +{
> +  if (!count.initialized_p ())
> +    return;
> +
> +  bool show_color = pp_show_color (pp);
> +
> +  pp_string (pp, " [");
> +  pp_string (pp,
> +            colorize_start (show_color,
> +                            diagnostic_get_color_for_kind (DK_NOTE)));
> +  pp_string (pp, "count(");
> +  pp_string (pp, profile_quality_as_string (count.quality ()));
> +  pp_string (pp, ")=");
> +  pp_scalar (pp, "%li", count.to_gcov_type ());
> +  pp_string (pp, colorize_stop (show_color));
> +  pp_string (pp, "]");
> +}
> +
> +/* Print IMPL_LOC to PP (as part of a remark).  */
> +
> +static void
> +print_impl_location (pretty_printer *pp, const dump_impl_location_t &impl_loc)
> +{
> +  bool show_color = pp_show_color (pp);
> +
> +  pp_string (pp, " [");
> +  pp_string (pp,
> +            colorize_start (show_color,
> +                            diagnostic_get_color_for_kind (DK_REMARK)));
> +  pp_printf (pp, "%s:%i", impl_loc.m_file, impl_loc.m_line);
> +  if (impl_loc.m_function)
> +    pp_printf (pp, ":%s", impl_loc.m_function);
> +  pp_string (pp, colorize_stop (show_color));
> +  pp_string (pp, "]");
> +}
> +
> +/* Print OPTINFO to PP.  */
> +
> +static void
> +print_optinfo_as_remark (pretty_printer *pp, const optinfo *optinfo)
> +{
> +  /* Start with scope-based indentation.  */
> +  for (unsigned i = get_dump_scope_depth (); i > 0; i--)
> +    pp_space (pp);
> +
> +  /* Print the items into PP.  */
> +  print_optinfo_items (pp, optinfo);
> +
> +  /* Add metadata: which pass?  */
> +  if (flag_diagnostics_show_remark_pass)
> +    print_pass (pp, optinfo->get_pass ());
> +
> +  /* Add metadata: hotness.  */
> +  if (flag_diagnostics_show_remark_hotness)
> +    print_count (pp, optinfo->get_count ());
> +
> +  /* Add metadata: where was this emitted from.  */
> +  if (flag_diagnostics_show_remark_origin)
> +    print_impl_location (pp, optinfo->get_impl_location ());
> +}
> +
> +/* Subclass of pretty_printer for building up the text of a remark.  */
> +
> +class remark_printer : public pretty_printer
> +{
> +public:
> +  remark_printer (bool show_color_);
> +};
> +
> +/* remark_printer's ctor.  */
> +
> +remark_printer::remark_printer (bool show_color_)
> +{
> +  pp_needs_newline (this) = true;
> +  pp_translate_identifiers (this) = false;
> +  pp_show_color (this) = show_color_;
> +}
> +
> +/* If diagnostic "remarks" are enabled, then emit OPTINFO as a remark.  */
> +
> +void
> +emit_optinfo_as_diagnostic_remark (const optinfo *optinfo)
> +{
> +  if (!flag_remarks)
> +    return;
> +
> +  remark_printer pp (pp_show_color (global_dc->printer));
> +
> +  print_optinfo_as_remark (&pp, optinfo);
> +
> +  const char *msg = pp_formatted_text (&pp);
> +  location_t loc = optinfo->get_location_t ();
> +
> +  remark (loc, 0, "%s", msg);
> +}
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Verify that print_optinfo_items works.  */
> +
> +static void
> +test_print_optinfo_items ()
> +{
> +  temp_dump_context tmp (true);
> +  dump_location_t loc;
> +  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +  optinfo *info = tmp.get_pending_optinfo ();
> +  ASSERT_TRUE (info != NULL);
> +
> +  /* Avoid introducing locale-specific differences in the results
> +     by hardcoding open_quote and close_quote.  */
> +  auto_fix_quotes fix_quotes;
> +
> +  remark_printer pp (false);
> +  print_optinfo_items (&pp, info);
> +  const char *msg = pp_formatted_text (&pp);
> +  ASSERT_STREQ (msg, "test of tree: `0'");
> +}
> +
> +/* Verify that print_pass works.  */
> +
> +static void
> +test_print_pass ()
> +{
> +  /* NULL pass.  */
> +  {
> +    remark_printer pp (false);
> +    print_pass (&pp, NULL);
> +    const char *msg = pp_formatted_text (&pp);
> +    ASSERT_STREQ (msg, "");
> +  }
> +
> +  /* Non-NULL pass.  */
> +  {
> +    remark_printer pp (false);
> +    opt_pass *pass = make_pass_ipa_increase_alignment (NULL);
> +    print_pass (&pp, pass);
> +    const char *msg = pp_formatted_text (&pp);
> +    ASSERT_STREQ (msg, " [pass=increase_alignment]");
> +    delete pass;
> +  }
> +}
> +
> +/* Verify that print_count works.  */
> +
> +static void
> +test_print_count ()
> +{
> +  remark_printer pp (false);
> +  print_count (&pp, profile_count ());
> +  const char *msg = pp_formatted_text (&pp);
> +  ASSERT_STREQ (msg, " [count(uninitialized)=0]");
> +}
> +
> +/* Verify that print_impl_location works.  */
> +
> +static void
> +test_print_impl_location ()
> +{
> +  /* Non-NULL function.  */
> +  {
> +    remark_printer pp (false);
> +    dump_impl_location_t loc ("foo.c", 42, "funcname");
> +    print_impl_location (&pp, loc);
> +    const char *msg = pp_formatted_text (&pp);
> +    ASSERT_STREQ (msg, " [foo.c:42:funcname]");
> +  }
> +
> +  /* NULL function.  */
> +  {
> +    remark_printer pp (false);
> +    dump_impl_location_t loc ("foo.c", 42, NULL);
> +    print_impl_location (&pp, loc);
> +    const char *msg = pp_formatted_text (&pp);
> +    ASSERT_STREQ (msg, " [foo.c:42]");
> +  }
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +optinfo_emit_diagnostics_cc_tests ()
> +{
> +  test_print_optinfo_items ();
> +  test_print_pass ();
> +  test_print_count ();
> +  test_print_impl_location ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* CHECKING_P */
> diff --git a/gcc/optinfo-emit-diagnostics.h b/gcc/optinfo-emit-diagnostics.h
> new file mode 100644
> index 0000000..820cefd
> --- /dev/null
> +++ b/gcc/optinfo-emit-diagnostics.h
> @@ -0,0 +1,26 @@
> +/* Emit optimization information as "remark" diagnostics.
> +   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_OPTINFO_EMIT_DIAGNOSTICS_H
> +#define GCC_OPTINFO_EMIT_DIAGNOSTICS_H
> +
> +extern void emit_optinfo_as_diagnostic_remark (const optinfo *);
> +
> +#endif /* #ifndef GCC_OPTINFO_EMIT_DIAGNOSTICS_H */
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> index 1da7d37..bc86696 100644
> --- a/gcc/optinfo.cc
> +++ b/gcc/optinfo.cc
> @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
>
>  #include "optinfo.h"
>  #include "optinfo-internal.h"
> +#include "optinfo-emit-diagnostics.h"
>  #include "dump-context.h"
>  #include "selftest.h"
>
> @@ -112,7 +113,8 @@ optinfo::emit ()
>        delete last_item;
>      }
>
> -  /* currently this is a no-op.  */
> +  /* -fremarks.  */
> +  emit_optinfo_as_diagnostic_remark (this);
>  }
>
>  /* Update the optinfo's kind based on DUMP_KIND.  */
> @@ -203,9 +205,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn)
>
>  bool optinfo_enabled_p ()
>  {
> -  /* Currently no destinations are implemented, just a hook for
> -     selftests.  */
> -  return dump_context::get ().forcibly_enable_optinfo_p ();
> +  return (dump_context::get ().forcibly_enable_optinfo_p ()
> +         || flag_remarks);
>  }
>
>  /* Return true if any of the active optinfo destinations make use
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> index 0d49823..8a8f812 100644
> --- a/gcc/optinfo.h
> +++ b/gcc/optinfo.h
> @@ -27,9 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>
>     * as a "remark" through the diagnostics subsystem
>
> -   * saved to a file as an "optimization record"
> -
> -   Currently no such destinations are implemented.
> +   * saved to a file as an "optimization record" (not yet implemented)
>
>     They are generated in response to calls to the "dump_*" API in
>     dumpfile.h; repeated calls to the "dump_*" API are consolidated
> diff --git a/gcc/opts.c b/gcc/opts.c
> index ed102c0..5cc8801 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1435,6 +1435,9 @@ print_specific_help (unsigned int include_flags,
>         case CL_WARNING:
>           description = _("The following options control compiler warning messages");
>           break;
> +       case CL_REMARK:
> +         description = _("The following options control compiler remarks");
> +         break;
>         case CL_OPTIMIZATION:
>           description = _("The following options control optimizations");
>           break;
> @@ -1875,6 +1878,7 @@ common_handle_option (struct gcc_options *opts,
>               { "optimizers", CL_OPTIMIZATION },
>               { "target", CL_TARGET },
>               { "warnings", CL_WARNING },
> +             { "remarks", CL_REMARK },
>               { "undocumented", CL_UNDOCUMENTED },
>               { "params", CL_PARAMS },
>               { "joined", CL_JOINED },
> diff --git a/gcc/opts.h b/gcc/opts.h
> index 3c4065ea..d9df788 100644
> --- a/gcc/opts.h
> +++ b/gcc/opts.h
> @@ -137,20 +137,21 @@ extern const unsigned int cl_lang_count;
>  #define CL_DRIVER              (1U << 19) /* Driver option.  */
>  #define CL_TARGET              (1U << 20) /* Target-specific option.  */
>  #define CL_COMMON              (1U << 21) /* Language-independent.  */
> +#define CL_REMARK              (1U << 22) /* Enables an (optional) remark.  */
>
>  #define CL_MIN_OPTION_CLASS    CL_PARAMS
> -#define CL_MAX_OPTION_CLASS    CL_COMMON
> +#define CL_MAX_OPTION_CLASS    CL_REMARK
>
>  /* From here on the bits describe attributes of the options.
>     Before this point the bits have described the class of the option.
>     This distinction is important because --help will not list options
>     which only have these higher bits set.  */
>
> -#define CL_JOINED              (1U << 22) /* If takes joined argument.  */
> -#define CL_SEPARATE            (1U << 23) /* If takes a separate argument.  */
> -#define CL_UNDOCUMENTED                (1U << 24) /* Do not output with --help.  */
> -#define CL_NO_DWARF_RECORD     (1U << 25) /* Do not add to producer string.  */
> -#define CL_PCH_IGNORE          (1U << 26) /* Do compare state for pch.  */
> +#define CL_JOINED              (1U << 23) /* If takes joined argument.  */
> +#define CL_SEPARATE            (1U << 24) /* If takes a separate argument.  */
> +#define CL_UNDOCUMENTED                (1U << 25) /* Do not output with --help.  */
> +#define CL_NO_DWARF_RECORD     (1U << 26) /* Do not add to producer string.  */
> +#define CL_PCH_IGNORE          (1U << 27) /* Do compare state for pch.  */
>
>  /* Flags for an enumerated option argument.  */
>  #define CL_ENUM_CANONICAL      (1 << 0) /* Canonical for this value.  */
> diff --git a/gcc/profile-count.c b/gcc/profile-count.c
> index 3d411cf..6a17f5e 100644
> --- a/gcc/profile-count.c
> +++ b/gcc/profile-count.c
> @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
>  #include "wide-int.h"
>  #include "sreal.h"
>
> +/* Get a string describing QUALITY.  */
> +
> +const char *
> +profile_quality_as_string (enum profile_quality quality)
> +{
> +  switch (quality)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case profile_uninitialized:
> +      return "uninitialized";
> +    case profile_guessed_local:
> +      return "guessed_local";
> +    case profile_guessed_global0:
> +      return "guessed_global0";
> +    case profile_guessed_global0adjusted:
> +      return "guessed_global0adjusted";
> +    case profile_guessed:
> +      return "guessed";
> +    case profile_afdo:
> +      return "afdo";
> +    case profile_adjusted:
> +      return "adjusted";
> +    case profile_precise:
> +      return "precise";
> +    }
> +}
> +
>  /* Dump THIS to F.  */
>
>  void
> diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> index c83fa3b..f4d0c340 100644
> --- a/gcc/profile-count.h
> +++ b/gcc/profile-count.h
> @@ -59,6 +59,8 @@ enum profile_quality {
>    profile_precise
>  };
>
> +extern const char *profile_quality_as_string (enum profile_quality);
> +
>  /* The base value for branch probability notes and edge probabilities.  */
>  #define REG_BR_PROB_BASE  10000
>
> @@ -721,6 +723,9 @@ public:
>        return m_quality == profile_precise;
>      }
>
> +  /* Get the quality of the count.  */
> +  enum profile_quality quality () const { return m_quality; }
> +
>    /* When merging basic blocks, the two different profile counts are unified.
>       Return true if this can be done without losing info about profile.
>       The only case we care about here is when first BB contains something
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index 989c50a..c0acf19 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -73,6 +73,7 @@ selftest::run_tests ()
>    unique_ptr_tests_cc_tests ();
>    opt_proposer_c_tests ();
>    optinfo_cc_tests ();
> +  optinfo_emit_diagnostics_cc_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index 48881c9..1594d1d 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -229,6 +229,7 @@ extern void hash_map_tests_c_tests ();
>  extern void hash_set_tests_c_tests ();
>  extern void input_c_tests ();
>  extern void optinfo_cc_tests ();
> +extern void optinfo_emit_diagnostics_cc_tests ();
>  extern void predict_c_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
> diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> index 5a19fc9..1f0a079 100644
> --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
> +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> @@ -96,6 +96,8 @@ set plugin_test_list [list \
>           must-tail-call-2.c } \
>      { expensive_selftests_plugin.c \
>           expensive-selftests-1.c } \
> +    { remarks_plugin.c \
> +         remarks-1.c } \
>  ]
>
>  foreach plugin_test $plugin_test_list {
> diff --git a/gcc/testsuite/gcc.dg/plugin/remarks-1.c b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
> new file mode 100644
> index 0000000..9139b9d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/remarks-1.c
> @@ -0,0 +1,30 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fremarks" } */
> +
> +extern void test_string_literal (void);
> +extern void test_tree (void);
> +extern void test_gimple (int);
> +extern void test_cgraph_node (void);
> +extern void test_printf (void);
> +extern void test_wide_int (void);
> +extern void test_poly_int (void);
> +extern void test_scopes (void);
> +
> +void test_remarks (void)
> +{
> +  test_string_literal (); /* { dg-remark "test of remark for test_string_literal" } */
> +  test_tree (); /* { dg-remark "test of tree: '0'" } */
> +  test_gimple (42); /* { dg-remark "test of gimple: 'test_gimple \\(42\\);'" } */
> +  test_cgraph_node (); /* { dg-remark "test of callgraph node: 'test_cgraph_node/.*'" } */
> +  test_printf (); /* { dg-remark "test of optinfo printf: 42" } */
> +  test_wide_int (); /* { dg-remark "test of wide int: 0" } */
> +  test_poly_int (); /* { dg-remark "test of poly int: 42" } */
> +
> +  test_scopes (); /* { dg-line test_scopes_line } */
> +  /* { dg-remark "=== outer scope ===" "" { target *-*-* } test_scopes_line } */
> +  /* { dg-remark " at outer scope" "" { target *-*-* } test_scopes_line } */
> +  /* { dg-remark " === middle scope ===" "" { target *-*-* } test_scopes_line } */
> +  /* { dg-remark "  at middle scope" "" { target *-*-* } test_scopes_line } */
> +  /* { dg-remark "  === innermost scope ===" "" { target *-*-* } test_scopes_line } */
> +  /* { dg-remark "   at innermost scope" "" { target *-*-* } test_scopes_line } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
> new file mode 100644
> index 0000000..332bba6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/remarks_plugin.c
> @@ -0,0 +1,152 @@
> +/* Test of remark-emission by optinfo.  */
> +
> +#include "gcc-plugin.h"
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tree.h"
> +#include "tree-pass.h"
> +#include "intl.h"
> +#include "plugin-version.h"
> +#include "diagnostic.h"
> +#include "context.h"
> +#include "optinfo.h"
> +#include "gimple.h"
> +#include "gimple-iterator.h"
> +#include "cgraph.h"
> +
> +int plugin_is_GPL_compatible;
> +
> +const pass_data pass_data_test_remarks =
> +{
> +  GIMPLE_PASS, /* type */
> +  "test_remarks", /* name */
> +  OPTGROUP_NONE, /* optinfo_flags */
> +  TV_NONE, /* tv_id */
> +  PROP_ssa, /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  0, /* todo_flags_finish */
> +};
> +
> +class pass_test_remarks : public gimple_opt_pass
> +{
> +public:
> +  pass_test_remarks(gcc::context *ctxt)
> +    : gimple_opt_pass(pass_data_test_remarks, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  bool gate (function *) { return true; }
> +  virtual unsigned int execute (function *);
> +
> +}; // class pass_test_remarks
> +
> +unsigned int
> +pass_test_remarks::execute (function *fun)
> +{
> +  basic_block bb;
> +
> +  if (!dump_enabled_p ())
> +    return 0;
> +
> +  FOR_ALL_BB_FN (bb, fun)
> +    for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
> +        !gsi_end_p (gsi); gsi_next (&gsi))
> +      {
> +       gimple *stmt = gsi_stmt (gsi);
> +       gcall *call = dyn_cast <gcall *> (stmt);
> +       if (!call)
> +         continue;
> +       tree callee_decl = gimple_call_fndecl (call);
> +       if (!callee_decl)
> +         continue;
> +       tree callee_name = DECL_NAME (callee_decl);
> +       if (!callee_name)
> +         continue;
> +       const char *callee = IDENTIFIER_POINTER (callee_name);
> +
> +       /* Various optinfo tests, done at callsites,
> +          controlled by the callee name.  */
> +       if (strcmp (callee, "test_string_literal") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of remark for ");
> +           dump_printf (MSG_NOTE, callee);
> +         }
> +       else if (strcmp (callee, "test_tree") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of tree: ");
> +           dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +         }
> +       else if (strcmp (callee, "test_gimple") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of gimple: ");
> +           dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 0);
> +         }
> +       else if (strcmp (callee, "test_cgraph_node") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of callgraph node: ");
> +           dump_symtab_node (MSG_NOTE, cgraph_node::get (callee_decl));
> +         }
> +       else if (strcmp (callee, "test_printf") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of optinfo printf: %d", 42);
> +         }
> +       else if (strcmp (callee, "test_wide_int") == 0)
> +         {
> +           HOST_WIDE_INT val = 0;
> +           dump_printf_loc (MSG_NOTE, stmt,
> +                            "test of wide int: " HOST_WIDE_INT_PRINT_DEC,
> +                            val);
> +         }
> +       else if (strcmp (callee, "test_poly_int") == 0)
> +         {
> +           dump_printf_loc (MSG_NOTE, stmt, "test of poly int: ");
> +           dump_dec (MSG_NOTE, poly_int64 (42));
> +         }
> +       else if (strcmp (callee, "test_scopes") == 0)
> +         {
> +           AUTO_DUMP_SCOPE ("outer scope", stmt);
> +           {
> +             dump_printf_loc (MSG_NOTE, stmt, "at outer scope");
> +             AUTO_DUMP_SCOPE ("middle scope", stmt);
> +             {
> +               dump_printf_loc (MSG_NOTE, stmt, "at middle scope");
> +               AUTO_DUMP_SCOPE ("innermost scope", stmt);
> +               dump_printf_loc (MSG_NOTE, stmt, "at innermost scope");
> +             }
> +           }
> +         }
> +      }
> +
> +  return 0;
> +}
> +
> +static gimple_opt_pass *
> +make_pass_test_remarks (gcc::context *ctxt)
> +{
> +  return new pass_test_remarks (ctxt);
> +}
> +
> +int
> +plugin_init (struct plugin_name_args *plugin_info,
> +            struct plugin_gcc_version *version)
> +{
> +  struct register_pass_info pass_info;
> +  const char *plugin_name = plugin_info->base_name;
> +  int argc = plugin_info->argc;
> +  struct plugin_argument *argv = plugin_info->argv;
> +
> +  if (!plugin_default_version_check (version, &gcc_version))
> +    return 1;
> +
> +  pass_info.pass = make_pass_test_remarks (g);
> +  pass_info.reference_pass_name = "ssa";
> +  pass_info.ref_pass_instance_number = 1;
> +  pass_info.pos_op = PASS_POS_INSERT_AFTER;
> +  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
> +                    &pass_info);
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
> index a15c5d5..906ee3b 100644
> --- a/gcc/testsuite/lib/gcc-dg.exp
> +++ b/gcc/testsuite/lib/gcc-dg.exp
> @@ -1154,6 +1154,15 @@ proc dg-locus { args } {
>      verbose "process-message:\n${dg-messages}" 2
>  }
>
> +# Handle remarks.
> +
> +proc dg-remark { args } {
> +    # Make this variable available here and to the saved proc.
> +    upvar dg-messages dg-messages
> +
> +    process-message saved-dg-error "remark: " "$args"
> +}
> +
>  # Check the existence of a gdb in the path, and return true if there
>  # is one.
>  #
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/2] Add "optinfo" framework
  2018-07-09 13:01                             ` Richard Biener
@ 2018-07-10 11:01                               ` David Malcolm
  2018-07-10 11:25                                 ` Richard Biener
  0 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-10 11:01 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Mon, 2018-07-09 at 15:00 +0200, Richard Biener wrote:
> On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This patch implements a way to consolidate dump_* calls into
> > optinfo objects, as enabling work towards being able to write out
> > optimization records to a file, or emit them as diagnostic
> > "remarks".
> > 
> > The patch adds the support for building optinfo instances from
> > dump_*
> > calls, but leaves implementing any *users* of them to followup
> > patches.
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > 
> > OK for trunk?
> 
> Looks good overall, but ...
> 
> To "fix" the GC issue you'd need to capture all possibly interesting
> information from tree/gimple while it is still in flight.  This _may_
> be
> necessary anyway since I remember writing code like
> 
>   fprintf (dump_file, "old: ");
>   print_gimple_stmt (..., stmt);
>   gimple_set_rhs1 (stmt, op);
>   fprintf (dump_file, "new: ");
>   print_gmple_stmt (..., stmt);
>   fprintf (dump_file, "\n");
> 
> capturing interesting information means we know all targeted
> optinfo channels, right?  And the optinfo consumers
> need to handle "streams" of input and may not look back.

> I've yet have to look at the 2nd patch but can you comment on
> this?  How difficult is it to re-wire how the data flows to make
> stmt re-use like the above possible?

I *think* it's doable: rather than capture, say, a gimple *, the
optinfo_item would capture the result of pp_gimple_stmt_1, plus some
metadata.  In fact, it would probably allow for removing the
optinfo_item subclasses, making optinfo_item concrete, containing
something like:

  /* Textual form.  */
  char *m_text;
  bool m_ownership_of_text;

  /* Metadata for optimization records.  */
  enum optinfo_item_kind m_kind;
  location_t m_location;

or somesuch.

I'll have a go at implementing this.

Thanks
Dave

> Thanks,
> Richard.
> 
> > gcc/ChangeLog:
> >         * Makefile.in (OBJS): Add optinfo.o.
> >         * coretypes.h (class symtab_node): New forward decl.
> >         (struct cgraph_node): New forward decl.
> >         (class varpool_node): New forward decl.
> >         * dump-context.h: New file.
> >         * dumpfile.c: Include "optinfo.h", "dump-context.h",
> > "cgraph.h",
> >         "tree-pass.h", "optinfo-internal.h".
> >         (refresh_dumps_are_enabled): Use optinfo_enabled_p.
> >         (set_dump_file): Call
> > dumpfile_ensure_any_optinfo_are_flushed.
> >         (set_alt_dump_file): Likewise.
> >         (dump_context::~dump_context): New dtor.
> >         (dump_gimple_stmt): Move implementation to...
> >         (dump_context::dump_gimple_stmt): ...this new member
> > function.
> >         Add the stmt to any pending optinfo, creating one if need
> > be.
> >         (dump_gimple_stmt_loc): Move implementation to...
> >         (dump_context::dump_gimple_stmt_loc): ...this new member
> > function.
> >         Convert param "loc" from location_t to const
> > dump_location_t &.
> >         Start a new optinfo and add the stmt to it.
> >         (dump_generic_expr): Move implementation to...
> >         (dump_context::dump_generic_expr): ...this new member
> > function.
> >         Add the tree to any pending optinfo, creating one if need
> > be.
> >         (dump_generic_expr_loc): Move implementation to...
> >         (dump_context::dump_generic_expr_loc): ...this new member
> >         function.  Add the tree to any pending optinfo, creating
> > one if
> >         need be.
> >         (dump_printf): Move implementation to...
> >         (dump_context::dump_printf_va): ...this new member
> > function.  Add
> >         the text to any pending optinfo, creating one if need be.
> >         (dump_printf_loc): Move implementation to...
> >         (dump_context::dump_printf_loc_va): ...this new member
> > function.
> >         Convert param "loc" from location_t to const
> > dump_location_t &.
> >         Start a new optinfo and add the stmt to it.
> >         (dump_dec): Move implementation to...
> >         (dump_context::dump_dec): ...this new member function.  Add
> > the
> >         value to any pending optinfo, creating one if need be.
> >         (dump_context::dump_symtab_node): New member function.
> >         (dump_context::get_scope_depth): New member function.
> >         (dump_context::begin_scope): New member function.
> >         (dump_context::end_scope): New member function.
> >         (dump_context::ensure_pending_optinfo): New member
> > function.
> >         (dump_context::begin_next_optinfo): New member function.
> >         (dump_context::end_any_optinfo): New member function.
> >         (dump_context::s_current): New global.
> >         (dump_context::s_default): New global.
> >         (dump_scope_depth): Delete global.
> >         (dumpfile_ensure_any_optinfo_are_flushed): New function.
> >         (dump_symtab_node): New function.
> >         (get_dump_scope_depth): Reimplement in terms of
> > dump_context.
> >         (dump_begin_scope): Likewise.
> >         (dump_end_scope): Likewise.
> >         (selftest::temp_dump_context::temp_dump_context): New ctor.
> >         (selftest::temp_dump_context::~temp_dump_context): New
> > dtor.
> >         (selftest::assert_is_text): New support function.
> >         (selftest::assert_is_tree): New support function.
> >         (selftest::assert_is_gimple): New support function.
> >         (selftest::test_capture_of_dump_calls): New test.
> >         (selftest::dumpfile_c_tests): Call it.
> >         * dumpfile.h (dump_printf, dump_printf_loc,
> > dump_basic_block,
> >         dump_generic_expr_loc, dump_generic_expr,
> > dump_gimple_stmt_loc,
> >         dump_gimple_stmt, dump_dec): Gather these related decls and
> > add a
> >         descriptive comment.
> >         (dump_function, print_combine_total_stats,
> > enable_rtl_dump_file,
> >         dump_node, dump_bb): Move these unrelated decls.
> >         (class dump_manager): Add leading comment.
> >         * ggc-page.c (ggc_collect): Call
> >         dumpfile_ensure_any_optinfo_are_flushed.
> >         * optinfo-internal.h: New file.
> >         * optinfo.cc: New file.
> >         * optinfo.h: New file.
> >         * selftest-run-tests.c (selftest::run_tests): Call
> >         selftest::optinfo_cc_tests.
> >         * selftest.h (selftest::optinfo_cc_tests): New decl.
> > ---
> >  gcc/Makefile.in          |   1 +
> >  gcc/coretypes.h          |   7 +
> >  gcc/dump-context.h       | 128 ++++++++++++
> >  gcc/dumpfile.c           | 498
> > +++++++++++++++++++++++++++++++++++++++++++----
> >  gcc/dumpfile.h           |  82 +++++---
> >  gcc/ggc-page.c           |   2 +
> >  gcc/optinfo-internal.h   | 148 ++++++++++++++
> >  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++
> >  gcc/optinfo.h            | 175 +++++++++++++++++
> >  gcc/selftest-run-tests.c |   1 +
> >  gcc/selftest.h           |   1 +
> >  11 files changed, 1233 insertions(+), 61 deletions(-)
> >  create mode 100644 gcc/dump-context.h
> >  create mode 100644 gcc/optinfo-internal.h
> >  create mode 100644 gcc/optinfo.cc
> >  create mode 100644 gcc/optinfo.h
> > 
> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > index 66c8b6e..7d36a77 100644
> > --- a/gcc/Makefile.in
> > +++ b/gcc/Makefile.in
> > @@ -1426,6 +1426,7 @@ OBJS = \
> >         optabs-libfuncs.o \
> >         optabs-query.o \
> >         optabs-tree.o \
> > +       optinfo.o \
> >         options-save.o \
> >         opts-global.o \
> >         passes.o \
> > diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> > index 283b4eb..ed0e825 100644
> > --- a/gcc/coretypes.h
> > +++ b/gcc/coretypes.h
> > @@ -134,6 +134,13 @@ struct gomp_single;
> >  struct gomp_target;
> >  struct gomp_teams;
> > 
> > +/* Subclasses of symtab_node, using indentation to show the class
> > +   hierarchy.  */
> > +
> > +class symtab_node;
> > +  struct cgraph_node;
> > +  class varpool_node;
> > +
> >  union section;
> >  typedef union section section;
> >  struct gcc_options;
> > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > new file mode 100644
> > index 0000000..753f714
> > --- /dev/null
> > +++ b/gcc/dump-context.h
> > @@ -0,0 +1,128 @@
> > +/* Support code for handling the various dump_* calls in
> > dumpfile.h
> > +   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_DUMP_CONTEXT_H
> > +#define GCC_DUMP_CONTEXT_H 1
> > +
> > +/* A class for handling the various dump_* calls.
> > +
> > +   In particular, this class has responsibility for consolidating
> > +   the "dump_*" calls into optinfo instances (delimited by
> > "dump_*_loc"
> > +   calls), and emitting them.
> > +
> > +   Putting this in a class (rather than as global state) allows
> > +   for selftesting of this code.  */
> > +
> > +class dump_context
> > +{
> > +  friend class temp_dump_context;
> > + public:
> > +  static dump_context &get () { return *s_current; }
> > +
> > +  ~dump_context ();
> > +
> > +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > +                        gimple *gs, int spc);
> > +
> > +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > +                            const dump_location_t &loc,
> > +                            dump_flags_t extra_dump_flags,
> > +                            gimple *gs, int spc);
> > +
> > +  void dump_generic_expr (dump_flags_t dump_kind,
> > +                         dump_flags_t extra_dump_flags,
> > +                         tree t);
> > +
> > +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> > +                             const dump_location_t &loc,
> > +                             dump_flags_t extra_dump_flags,
> > +                             tree t);
> > +
> > +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> > +                      va_list ap) ATTRIBUTE_PRINTF (3, 0);
> > +
> > +  void dump_printf_loc_va (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > +                          const char *format, va_list ap)
> > +    ATTRIBUTE_PRINTF (4, 0);
> > +
> > +  template<unsigned int N, typename C>
> > +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> > &value);
> > +
> > +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node
> > *node);
> > +
> > +  /* Managing nested scopes.  */
> > +  unsigned int get_scope_depth () const;
> > +  void begin_scope (const char *name, const dump_location_t &loc);
> > +  void end_scope ();
> > +
> > +  /* For use in selftests; if true then optinfo_enabled_p is
> > true.  */
> > +  bool forcibly_enable_optinfo_p () const
> > +  {
> > +    return m_forcibly_enable_optinfo;
> > +  }
> > +
> > +  void end_any_optinfo ();
> > +
> > + private:
> > +  optinfo &ensure_pending_optinfo ();
> > +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> > +
> > +  /* For use in selftests; if true then optinfo_enabled_p is
> > true.  */
> > +  bool m_forcibly_enable_optinfo;
> > +
> > +  /* The current nesting depth of dump scopes, for showing nesting
> > +     via indentation).  */
> > +  unsigned int m_scope_depth;
> > +
> > +  /* The optinfo currently being accumulated since the last
> > dump_*_loc call,
> > +     if any.  */
> > +  optinfo *m_pending;
> > +
> > +  /* The currently active dump_context, for use by the dump_* API
> > calls.  */
> > +  static dump_context *s_current;
> > +
> > +  /* The default active context.  */
> > +  static dump_context s_default;
> > +};
> > +
> > +#if CHECKING_P
> > +
> > +/* An RAII-style class for use in selftests for temporarily using
> > a different
> > +   dump_context.  */
> > +
> > +class temp_dump_context
> > +{
> > + public:
> > +  temp_dump_context (bool forcibly_enable_optinfo);
> > +  ~temp_dump_context ();
> > +
> > +  /* Support for selftests.  */
> > +  optinfo *get_pending_optinfo () const { return
> > m_context.m_pending; }
> > +
> > + private:
> > +  dump_context m_context;
> > +  dump_context *m_saved;
> > +  bool m_saved_flag_remarks;
> > +};
> > +
> > +#endif /* CHECKING_P */
> > +
> > +#endif /* GCC_DUMP_CONTEXT_H */
> > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > index 5f69f9b..6e089ef 100644
> > --- a/gcc/dumpfile.c
> > +++ b/gcc/dumpfile.c
> > @@ -33,6 +33,11 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "gimple.h" /* for dump_user_location_t ctor.  */
> >  #include "rtl.h" /* for dump_user_location_t ctor.  */
> >  #include "selftest.h"
> > +#include "optinfo.h"
> > +#include "dump-context.h"
> > +#include "cgraph.h"
> > +#include "tree-pass.h" /* for "current_pass".  */
> > +#include "optinfo-internal.h" /* for selftests.  */
> > 
> >  /* If non-NULL, return one past-the-end of the matching SUBPART of
> >     the WHOLE string.  */
> > @@ -64,7 +69,7 @@ bool dumps_are_enabled = false;
> >  static void
> >  refresh_dumps_are_enabled ()
> >  {
> > -  dumps_are_enabled = (dump_file || alt_dump_file);
> > +  dumps_are_enabled = (dump_file || alt_dump_file ||
> > optinfo_enabled_p ());
> >  }
> > 
> >  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> > "dumps_are_enabled"
> > @@ -73,6 +78,7 @@ refresh_dumps_are_enabled ()
> >  void
> >  set_dump_file (FILE *new_dump_file)
> >  {
> > +  dumpfile_ensure_any_optinfo_are_flushed ();
> >    dump_file = new_dump_file;
> >    refresh_dumps_are_enabled ();
> >  }
> > @@ -83,6 +89,7 @@ set_dump_file (FILE *new_dump_file)
> >  static void
> >  set_alt_dump_file (FILE *new_alt_dump_file)
> >  {
> > +  dumpfile_ensure_any_optinfo_are_flushed ();
> >    alt_dump_file = new_alt_dump_file;
> >    refresh_dumps_are_enabled ();
> >  }
> > @@ -458,25 +465,44 @@ dump_loc (dump_flags_t dump_kind, FILE
> > *dfile, source_location loc)
> >      }
> >  }
> > 
> > +/* Implementation of dump_context member functions.  */
> > +
> > +/* dump_context's dtor.  */
> > +
> > +dump_context::~dump_context ()
> > +{
> > +  delete m_pending;
> > +}
> > +
> >  /* Dump gimple statement GS with SPC indentation spaces and
> >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > enabled.  */
> > 
> >  void
> > -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > -                 gimple *gs, int spc)
> > +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> > +                               dump_flags_t extra_dump_flags,
> > +                               gimple *gs, int spc)
> >  {
> >    if (dump_file && (dump_kind & pflags))
> >      print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > 
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = ensure_pending_optinfo ();
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_stmt (gs, extra_dump_flags);
> > +    }
> >  }
> > 
> >  /* Similar to dump_gimple_stmt, except additionally print source
> > location.  */
> > 
> >  void
> > -dump_gimple_stmt_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > -                     dump_flags_t extra_dump_flags, gimple *gs,
> > int spc)
> > +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > +                                   const dump_location_t &loc,
> > +                                   dump_flags_t extra_dump_flags,
> > +                                   gimple *gs, int spc)
> >  {
> >    location_t srcloc = loc.get_location_t ();
> >    if (dump_file && (dump_kind & pflags))
> > @@ -490,20 +516,35 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > const dump_location_t &loc,
> >        dump_loc (dump_kind, alt_dump_file, srcloc);
> >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> >      }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_stmt (gs, extra_dump_flags);
> > +    }
> >  }
> > 
> >  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > if
> >     DUMP_KIND is enabled.  */
> > 
> >  void
> > -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > -                  tree t)
> > +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> > +                                dump_flags_t extra_dump_flags,
> > +                                tree t)
> >  {
> >    if (dump_file && (dump_kind & pflags))
> >        print_generic_expr (dump_file, t, dump_flags |
> > extra_dump_flags);
> > 
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >        print_generic_expr (alt_dump_file, t, dump_flags |
> > extra_dump_flags);
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = ensure_pending_optinfo ();
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_tree (t, extra_dump_flags);
> > +    }
> >  }
> > 
> > 
> > @@ -511,8 +552,10 @@ dump_generic_expr (dump_flags_t dump_kind,
> > dump_flags_t extra_dump_flags,
> >     location.  */
> > 
> >  void
> > -dump_generic_expr_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > -                      dump_flags_t extra_dump_flags, tree t)
> > +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> > +                                    const dump_location_t &loc,
> > +                                    dump_flags_t extra_dump_flags,
> > +                                    tree t)
> >  {
> >    location_t srcloc = loc.get_location_t ();
> >    if (dump_file && (dump_kind & pflags))
> > @@ -526,53 +569,82 @@ dump_generic_expr_loc (dump_flags_t
> > dump_kind, const dump_location_t &loc,
> >        dump_loc (dump_kind, alt_dump_file, srcloc);
> >        print_generic_expr (alt_dump_file, t, dump_flags |
> > extra_dump_flags);
> >      }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_tree (t, extra_dump_flags);
> > +    }
> >  }
> > 
> >  /* Output a formatted message using FORMAT on appropriate dump
> > streams.  */
> > 
> >  void
> > -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> > +dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > *format,
> > +                             va_list ap)
> >  {
> >    if (dump_file && (dump_kind & pflags))
> >      {
> > -      va_list ap;
> > -      va_start (ap, format);
> > -      vfprintf (dump_file, format, ap);
> > -      va_end (ap);
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      vfprintf (dump_file, format, aq);
> > +      va_end (aq);
> >      }
> > 
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      {
> > -      va_list ap;
> > -      va_start (ap, format);
> > -      vfprintf (alt_dump_file, format, ap);
> > -      va_end (ap);
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      vfprintf (alt_dump_file, format, aq);
> > +      va_end (aq);
> > +    }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = ensure_pending_optinfo ();
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      info.add_printf_va (format, aq);
> > +      va_end (aq);
> >      }
> >  }
> > 
> > -/* Similar to dump_printf, except source location is also
> > printed.  */
> > +/* Similar to dump_printf, except source location is also printed,
> > and
> > +   dump location captured.  */
> > 
> >  void
> > -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t
> > &loc,
> > -                const char *format, ...)
> > +dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > +                                 const char *format, va_list ap)
> >  {
> >    location_t srcloc = loc.get_location_t ();
> > +
> >    if (dump_file && (dump_kind & pflags))
> >      {
> > -      va_list ap;
> >        dump_loc (dump_kind, dump_file, srcloc);
> > -      va_start (ap, format);
> > -      vfprintf (dump_file, format, ap);
> > -      va_end (ap);
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      vfprintf (dump_file, format, aq);
> > +      va_end (aq);
> >      }
> > 
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      {
> > -      va_list ap;
> >        dump_loc (dump_kind, alt_dump_file, srcloc);
> > -      va_start (ap, format);
> > -      vfprintf (alt_dump_file, format, ap);
> > -      va_end (ap);
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      vfprintf (alt_dump_file, format, aq);
> > +      va_end (aq);
> > +    }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      va_list aq;
> > +      va_copy (aq, ap);
> > +      info.add_printf_va (format, aq);
> > +      va_end (aq);
> >      }
> >  }
> > 
> > @@ -580,7 +652,7 @@ dump_printf_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > 
> >  template<unsigned int N, typename C>
> >  void
> > -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> > +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > C> &value)
> >  {
> >    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> >    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > UNSIGNED;
> > @@ -589,6 +661,203 @@ dump_dec (dump_flags_t dump_kind, const
> > poly_int<N, C> &value)
> > 
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      print_dec (value, alt_dump_file, sgn);
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = ensure_pending_optinfo ();
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_poly_int<N,C> (value);
> > +    }
> > +}
> > +
> > +/* Output the name of NODE on appropriate dump streams.  */
> > +
> > +void
> > +dump_context::dump_symtab_node (dump_flags_t dump_kind,
> > symtab_node *node)
> > +{
> > +  if (dump_file && (dump_kind & pflags))
> > +    fprintf (dump_file, "%s", node->dump_name ());
> > +
> > +  if (alt_dump_file && (dump_kind & alt_flags))
> > +    fprintf (alt_dump_file, "%s", node->dump_name ());
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      optinfo &info = ensure_pending_optinfo ();
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_symtab_node (node);
> > +    }
> > +}
> > +
> > +/* Get the current dump scope-nesting depth.
> > +   For use by -fopt-info (for showing nesting via
> > indentation).  */
> > +
> > +unsigned int
> > +dump_context::get_scope_depth () const
> > +{
> > +  return m_scope_depth;
> > +}
> > +
> > +/* Push a nested dump scope.
> > +   Print "=== NAME ===\n" to the dumpfile, if any, and to the
> > -fopt-info
> > +   destination, if any.
> > +   Emit a "scope" optinfo if optinfos are enabled.
> > +   Increment the scope depth.  */
> > +
> > +void
> > +dump_context::begin_scope (const char *name, const dump_location_t
> > &loc)
> > +{
> > +  /* Specialcase, to avoid going through dump_printf_loc,
> > +     so that we can create a optinfo of kind
> > OPTINFO_KIND_SCOPE.  */
> > +
> > +  if (dump_file)
> > +    {
> > +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > +      fprintf (dump_file, "=== %s ===\n", name);
> > +    }
> > +
> > +  if (alt_dump_file)
> > +    {
> > +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > +      fprintf (alt_dump_file, "=== %s ===\n", name);
> > +    }
> > +
> > +  if (optinfo_enabled_p ())
> > +    {
> > +      end_any_optinfo ();
> > +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> > +      info.add_printf ("=== %s ===", name);
> > +      info.emit ();
> > +    }
> > +
> > +  m_scope_depth++;
> > +}
> > +
> > +/* Pop a nested dump scope.  */
> > +
> > +void
> > +dump_context::end_scope ()
> > +{
> > +  end_any_optinfo ();
> > +  m_scope_depth--;
> > +}
> > +
> > +/* Return the optinfo currently being accumulated, creating one if
> > +   necessary.  */
> > +
> > +optinfo &
> > +dump_context::ensure_pending_optinfo ()
> > +{
> > +  if (!m_pending)
> > +    return begin_next_optinfo (dump_location_t
> > (dump_user_location_t ()));
> > +  return *m_pending;
> > +}
> > +
> > +/* Start a new optinfo and return it, ending any optinfo that was
> > already
> > +   accumulated.  */
> > +
> > +optinfo &
> > +dump_context::begin_next_optinfo (const dump_location_t &loc)
> > +{
> > +  end_any_optinfo ();
> > +  gcc_assert (m_pending == NULL);
> > +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> > +  return *m_pending;
> > +}
> > +
> > +/* End any optinfo that has been accumulated within this context;
> > emitting
> > +   it to any destinations as appropriate - though none have
> > currently been
> > +   implemented.  */
> > +
> > +void
> > +dump_context::end_any_optinfo ()
> > +{
> > +  if (m_pending)
> > +    m_pending->emit ();
> > +  delete m_pending;
> > +  m_pending = NULL;
> > +}
> > +
> > +/* The current singleton dump_context, and its default.  */
> > +
> > +dump_context *dump_context::s_current = &dump_context::s_default;
> > +dump_context dump_context::s_default;
> > +
> > +/* Implementation of dump_* API calls, calling into dump_context
> > +   member functions.  */
> > +
> > +/* Dump gimple statement GS with SPC indentation spaces and
> > +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > enabled.  */
> > +
> > +void
> > +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > +                 gimple *gs, int spc)
> > +{
> > +  dump_context::get ().dump_gimple_stmt (dump_kind,
> > extra_dump_flags, gs, spc);
> > +}
> > +
> > +/* Similar to dump_gimple_stmt, except additionally print source
> > location.  */
> > +
> > +void
> > +dump_gimple_stmt_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > +                     dump_flags_t extra_dump_flags, gimple *gs,
> > int spc)
> > +{
> > +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc,
> > extra_dump_flags,
> > +                                            gs, spc);
> > +}
> > +
> > +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > if
> > +   DUMP_KIND is enabled.  */
> > +
> > +void
> > +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > +                  tree t)
> > +{
> > +  dump_context::get ().dump_generic_expr (dump_kind,
> > extra_dump_flags, t);
> > +}
> > +
> > +/* Similar to dump_generic_expr, except additionally print the
> > source
> > +   location.  */
> > +
> > +void
> > +dump_generic_expr_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc,
> > +                      dump_flags_t extra_dump_flags, tree t)
> > +{
> > +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc,
> > extra_dump_flags,
> > +                                             t);
> > +}
> > +
> > +/* Output a formatted message using FORMAT on appropriate dump
> > streams.  */
> > +
> > +void
> > +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> > +{
> > +  va_list ap;
> > +  va_start (ap, format);
> > +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> > +  va_end (ap);
> > +}
> > +
> > +/* Similar to dump_printf, except source location is also printed,
> > and
> > +   dump location captured.  */
> > +
> > +void
> > +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t
> > &loc,
> > +                const char *format, ...)
> > +{
> > +  va_list ap;
> > +  va_start (ap, format);
> > +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > ap);
> > +  va_end (ap);
> > +}
> > +
> > +/* Output VALUE in decimal to appropriate dump streams.  */
> > +
> > +template<unsigned int N, typename C>
> > +void
> > +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> > +{
> > +  dump_context::get ().dump_dec (dump_kind, value);
> >  }
> > 
> >  template void dump_dec (dump_flags_t, const poly_uint16 &);
> > @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const
> > poly_uint64 &);
> >  template void dump_dec (dump_flags_t, const poly_offset_int &);
> >  template void dump_dec (dump_flags_t, const poly_widest_int &);
> > 
> > -/* The current dump scope-nesting depth.  */
> > +/* Emit and delete the currently pending optinfo, if there is one,
> > +   without the caller needing to know about class
> > dump_context.  */
> > +
> > +void
> > +dumpfile_ensure_any_optinfo_are_flushed ()
> > +{
> > +  dump_context::get().end_any_optinfo ();
> > +}
> > +
> > +/* Output the name of NODE on appropriate dump streams.  */
> > 
> > -static int dump_scope_depth;
> > +void
> > +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> > +{
> > +  dump_context::get ().dump_symtab_node (dump_kind, node);
> > +}
> > 
> >  /* Get the current dump scope-nesting depth.
> > -   For use by dump_*_loc (for showing nesting via
> > indentation).  */
> > +   For use by -fopt-info (for showing nesting via
> > indentation).  */
> > 
> >  unsigned int
> >  get_dump_scope_depth ()
> >  {
> > -  return dump_scope_depth;
> > +  return dump_context::get ().get_scope_depth ();
> >  }
> > 
> >  /* Push a nested dump scope.
> >     Print "=== NAME ===\n" to the dumpfile, if any, and to the
> > -fopt-info
> >     destination, if any.
> > +   Emit a "scope" opinfo if optinfos are enabled.
> >     Increment the scope depth.  */
> > 
> >  void
> >  dump_begin_scope (const char *name, const dump_location_t &loc)
> >  {
> > -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> > -  dump_scope_depth++;
> > +  dump_context::get ().begin_scope (name, loc);
> >  }
> > 
> >  /* Pop a nested dump scope.  */
> > @@ -627,7 +909,7 @@ dump_begin_scope (const char *name, const
> > dump_location_t &loc)
> >  void
> >  dump_end_scope ()
> >  {
> > -  dump_scope_depth--;
> > +  dump_context::get ().end_scope ();
> >  }
> > 
> >  /* Start a dump for PHASE. Store user-supplied dump flags in
> > @@ -1180,6 +1462,24 @@ enable_rtl_dump_file (void)
> > 
> >  #if CHECKING_P
> > 
> > +/* temp_dump_context's ctor.  Temporarily override the
> > dump_context
> > +   (to forcibly enable optinfo-generation).  */
> > +
> > +temp_dump_context::temp_dump_context (bool
> > forcibly_enable_optinfo)
> > +: m_context (),
> > +  m_saved (&dump_context ().get ())
> > +{
> > +  dump_context::s_current = &m_context;
> > +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> > +}
> > +
> > +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> > +
> > +temp_dump_context::~temp_dump_context ()
> > +{
> > +  dump_context::s_current = m_saved;
> > +}
> > +
> >  namespace selftest {
> > 
> >  /* Verify that the dump_location_t constructors capture the source
> > location
> > @@ -1216,12 +1516,136 @@ test_impl_location ()
> >  #endif
> >  }
> > 
> > +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> > +
> > +static void
> > +assert_is_text (const optinfo_item *item, const char
> > *expected_text)
> > +{
> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
> > +  const optinfo_item_text * text_item
> > +    = static_cast <const optinfo_item_text *> (item);
> > +  ASSERT_STREQ (text_item->get_text (), expected_text);
> > +}
> > +
> > +/* Verify that ITEM is a tree item, with the expected values.  */
> > +
> > +static void
> > +assert_is_tree (const optinfo_item *item, tree expected_tree,
> > +               dump_flags_t expected_dump_flags)
> > +{
> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
> > +  const optinfo_item_tree * tree_item
> > +    = static_cast <const optinfo_item_tree *> (item);
> > +  ASSERT_EQ (tree_item->get_node (), expected_tree);
> > +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
> > +}
> > +
> > +/* Verify that ITEM is a gimple item, with the expected
> > values.  */
> > +
> > +static void
> > +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
> > +                 dump_flags_t expected_dump_flags)
> > +{
> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
> > +  const optinfo_item_gimple * gimple_item
> > +    = static_cast <const optinfo_item_gimple *> (item);
> > +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
> > +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
> > +}
> > +
> > +/* Verify that calls to the dump_* API are captured and
> > consolidated into
> > +   optimization records. */
> > +
> > +static void
> > +test_capture_of_dump_calls ()
> > +{
> > +  dump_location_t loc;
> > +
> > +  /* Tree, via dump_generic_expr.  */
> > +  {
> > +    temp_dump_context tmp (true);
> > +    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > +    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > +
> > +    optinfo *info = tmp.get_pending_optinfo ();
> > +    ASSERT_TRUE (info != NULL);
> > +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > +    ASSERT_EQ (info->num_items (), 2);
> > +    assert_is_text (info->get_item (0), "test of tree: ");
> > +    assert_is_tree (info->get_item (1), integer_zero_node,
> > TDF_SLIM);
> > +  }
> > +
> > +  /* Tree, via dump_generic_expr_loc.  */
> > +  {
> > +    temp_dump_context tmp (true);
> > +    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > integer_one_node);
> > +
> > +    optinfo *info = tmp.get_pending_optinfo ();
> > +    ASSERT_TRUE (info != NULL);
> > +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > +    ASSERT_EQ (info->num_items (), 1);
> > +    assert_is_tree (info->get_item (0), integer_one_node,
> > TDF_SLIM);
> > +  }
> > +
> > +  /* Gimple.  */
> > +  {
> > +    greturn *stmt = gimple_build_return (NULL);
> > +
> > +    temp_dump_context tmp (true);
> > +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
> > +
> > +    optinfo *info = tmp.get_pending_optinfo ();
> > +    ASSERT_TRUE (info != NULL);
> > +    ASSERT_EQ (info->num_items (), 1);
> > +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
> > +
> > +    /* Verify that optinfo instances are flushed if a GC is about
> > to
> > +       happen (and thus don't need to be GTY-marked).
> > +       We don't want them in the PCH file, but we don't want the
> > +       items to have their data collected from under them.  */
> > +    selftest::forcibly_ggc_collect ();
> > +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);
> > +  }
> > +
> > +  /* poly_int.  */
> > +  {
> > +    temp_dump_context tmp (true);
> > +    dump_dec (MSG_NOTE, poly_int64 (42));
> > +
> > +    optinfo *info = tmp.get_pending_optinfo ();
> > +    ASSERT_TRUE (info != NULL);
> > +    ASSERT_EQ (info->num_items (), 1);
> > +    assert_is_text (info->get_item (0), "42");
> > +  }
> > +
> > +  /* Verify that MSG_* affects optinfo->get_kind (); we tested
> > MSG_NOTE
> > +     above.  */
> > +  {
> > +    /* MSG_OPTIMIZED_LOCATIONS.  */
> > +    {
> > +      temp_dump_context tmp (true);
> > +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> > +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > +                OPTINFO_KIND_SUCCESS);
> > +    }
> > +
> > +    /* MSG_MISSED_OPTIMIZATION.  */
> > +    {
> > +      temp_dump_context tmp (true);
> > +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> > +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > +                OPTINFO_KIND_FAILURE);
> > +    }
> > +  }
> > +}
> > +
> >  /* Run all of the selftests within this file.  */
> > 
> >  void
> >  dumpfile_c_tests ()
> >  {
> >    test_impl_location ();
> > +  test_capture_of_dump_calls ();
> >  }
> > 
> >  } // namespace selftest
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 0e588a6..899bb89 100644
> > --- a/gcc/dumpfile.h
> > +++ b/gcc/dumpfile.h
> > @@ -420,30 +420,6 @@ extern FILE *dump_begin (int, dump_flags_t *);
> >  extern void dump_end (int, FILE *);
> >  extern int opt_info_switch_p (const char *);
> >  extern const char *dump_flag_name (int);
> > -extern void dump_printf (dump_flags_t, const char *, ...)
> > ATTRIBUTE_PRINTF_2;
> > -extern void dump_printf_loc (dump_flags_t, const dump_location_t
> > &,
> > -                            const char *, ...) ATTRIBUTE_PRINTF_3;
> > -extern void dump_function (int phase, tree fn);
> > -extern void dump_basic_block (dump_flags_t, basic_block, int);
> > -extern void dump_generic_expr_loc (dump_flags_t, const
> > dump_location_t &,
> > -                                  dump_flags_t, tree);
> > -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> > -extern void dump_gimple_stmt_loc (dump_flags_t, const
> > dump_location_t &,
> > -                                 dump_flags_t, gimple *, int);
> > -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple
> > *, int);
> > -extern void print_combine_total_stats (void);
> > -extern bool enable_rtl_dump_file (void);
> > -
> > -template<unsigned int N, typename C>
> > -void dump_dec (dump_flags_t, const poly_int<N, C> &);
> > -
> > -/* In tree-dump.c  */
> > -extern void dump_node (const_tree, dump_flags_t, FILE *);
> > -
> > -/* In combine.c  */
> > -extern void dump_combine_total_stats (FILE *);
> > -/* In cfghooks.c  */
> > -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> > 
> >  /* Global variables used to communicate with passes.  */
> >  extern FILE *dump_file;
> > @@ -461,6 +437,49 @@ dump_enabled_p (void)
> >    return dumps_are_enabled;
> >  }
> > 
> > +/* The following API calls (which *don't* take a "FILE *")
> > +   write the output to zero or more locations:
> > +   (a) the active dump_file, if any
> > +   (b) the -fopt-info destination, if any
> > +   (c) to the "optinfo" destinations, if any:
> > +
> > +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> > +                                   |
> > +                                   +--> (b) alt_dump_file
> > +                                   |
> > +                                   `--> (c) optinfo
> > +                                            `---> optinfo
> > destinations
> > +
> > +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> > +   instance: all subsequent dump_* calls are consolidated into
> > +   that optinfo, until the next dump_*_loc call (or a change in
> > +   dump scope, or a call to
> > dumpfile_ensure_any_optinfo_are_flushed).
> > +
> > +   A group of dump_* calls should be guarded by:
> > +
> > +     if (dump_enabled_p ())
> > +
> > +   to minimize the work done for the common case where dumps
> > +   are disabled.  */
> > +
> > +extern void dump_printf (dump_flags_t, const char *, ...)
> > ATTRIBUTE_PRINTF_2;
> > +extern void dump_printf_loc (dump_flags_t, const dump_location_t
> > &,
> > +                            const char *, ...) ATTRIBUTE_PRINTF_3;
> > +extern void dump_function (int phase, tree fn);
> > +extern void dump_basic_block (dump_flags_t, basic_block, int);
> > +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> > +extern void dump_generic_expr_loc (dump_flags_t, const
> > dump_location_t &,
> > +                                  dump_flags_t, tree);
> > +extern void dump_gimple_stmt_loc (dump_flags_t, const
> > dump_location_t &,
> > +                                 dump_flags_t, gimple *, int);
> > +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple
> > *, int);
> > +extern void dump_symtab_node (dump_flags_t, symtab_node *);
> > +
> > +template<unsigned int N, typename C>
> > +void dump_dec (dump_flags_t, const poly_int<N, C> &);
> > +
> > +extern void dumpfile_ensure_any_optinfo_are_flushed ();
> > +
> >  /* Managing nested scopes, so that dumps can express the call
> > chain
> >     leading to a dump message.  */
> > 
> > @@ -500,8 +519,23 @@ class auto_dump_scope
> >  #define AUTO_DUMP_SCOPE(NAME, LOC) \
> >    auto_dump_scope scope (NAME, LOC)
> > 
> > +extern void dump_function (int phase, tree fn);
> > +extern void print_combine_total_stats (void);
> > +extern bool enable_rtl_dump_file (void);
> > +
> > +/* In tree-dump.c  */
> > +extern void dump_node (const_tree, dump_flags_t, FILE *);
> > +
> > +/* In combine.c  */
> > +extern void dump_combine_total_stats (FILE *);
> > +/* In cfghooks.c  */
> > +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> > +
> >  namespace gcc {
> > 
> > +/* A class for managing all of the various dump files used by the
> > +   optimization passes.  */
> > +
> >  class dump_manager
> >  {
> >  public:
> > diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
> > index 51783e5..f3a2119 100644
> > --- a/gcc/ggc-page.c
> > +++ b/gcc/ggc-page.c
> > @@ -2177,6 +2177,8 @@ ggc_collect (void)
> >    if (G.allocated < allocated_last_gc + min_expand &&
> > !ggc_force_collect)
> >      return;
> > 
> > +  dumpfile_ensure_any_optinfo_are_flushed ();
> > +
> >    timevar_push (TV_GC);
> >    if (!quiet_flag)
> >      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated
> > / 1024);
> > diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
> > new file mode 100644
> > index 0000000..8144174
> > --- /dev/null
> > +++ b/gcc/optinfo-internal.h
> > @@ -0,0 +1,148 @@
> > +/* Implementation details of optinfo.
> > +   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_OPTINFO_INTERNAL_H
> > +#define GCC_OPTINFO_INTERNAL_H
> > +
> > +/* Multiple dispatch: there are various kinds of items within an
> > optinfo,
> > +   and various destinations to send optinfo to.
> > +
> > +   Handling this for now by exposing all of the item subclasses,
> > +   and having the destinations handle them with "switch"
> > statements.  */
> > +
> > +/* An enum for discriminating between optinfo_item subclasses.  */
> > +
> > +enum optinfo_item_kind
> > +{
> > +  OPTINFO_ITEM_KIND_TEXT,
> > +  OPTINFO_ITEM_KIND_TREE,
> > +  OPTINFO_ITEM_KIND_GIMPLE,
> > +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> > +};
> > +
> > +/* Abstract base class for items within an optinfo.  */
> > +
> > +class optinfo_item
> > +{
> > + public:
> > +  virtual ~optinfo_item () {}
> > +
> > +  virtual enum optinfo_item_kind get_kind () const = 0;
> > +};
> > +
> > +/* optinfo_item subclasses.  */
> > +
> > +/* Item within an optinfo: text, either owned by the item
> > +   (for optinfo_printf), or borrowed (for string literals).  */
> > +
> > +class optinfo_item_text : public optinfo_item
> > +{
> > + public:
> > +  optinfo_item_text (char *text, bool owned)
> > +  : m_text (text), m_owned (owned)
> > +  {}
> > +  ~optinfo_item_text ()
> > +  {
> > +    if (m_owned)
> > +      free (m_text);
> > +  }
> > +
> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > +  {
> > +    return OPTINFO_ITEM_KIND_TEXT;
> > +  }
> > +
> > +  const char *get_text () const { return m_text; }
> > +
> > +  void trim_trailing_whitespace ();
> > +
> > + private:
> > +  char *m_text;
> > +  bool m_owned;
> > +};
> > +
> > +/* Item within an optinfo: a tree, with dump flags.
> > +   Note that this is not GTY-marked; see the description of
> > +   class optinfo for a discussion of the interaction with the
> > +   garbage-collector.  */
> > +
> > +class optinfo_item_tree : public optinfo_item
> > +{
> > + public:
> > +  optinfo_item_tree (tree node, dump_flags_t dump_flags)
> > +    : m_node (node), m_dump_flags (dump_flags)
> > +  {}
> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > +  {
> > +    return OPTINFO_ITEM_KIND_TREE;
> > +  }
> > +
> > +  tree get_node () const { return m_node; }
> > +  dump_flags_t get_flags () const { return m_dump_flags; }
> > +
> > + private:
> > +  tree m_node;
> > +  dump_flags_t m_dump_flags;
> > +};
> > +
> > +/* Item within an optinfo: a gimple statement.
> > +   Note that this is not GTY-marked; see the description of
> > +   class optinfo for a discussion of the interaction with the
> > +   garbage-collector.  */
> > +
> > +class optinfo_item_gimple : public optinfo_item
> > +{
> > + public:
> > +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
> > +    : m_stmt (stmt), m_dump_flags (dump_flags) {}
> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > +  {
> > +    return OPTINFO_ITEM_KIND_GIMPLE;
> > +  }
> > +
> > +  gimple *get_stmt () const { return m_stmt; }
> > +  dump_flags_t get_flags () const { return m_dump_flags; }
> > +
> > + private:
> > +  gimple *m_stmt;
> > +  dump_flags_t m_dump_flags;
> > +};
> > +
> > +/* Item within an optinfo: a symbol table node.
> > +   Note that this is not GTY-marked; see the description of
> > +   class optinfo for a discussion of the interaction with the
> > +   garbage-collector.  */
> > +
> > +class optinfo_item_symtab_node : public optinfo_item
> > +{
> > + public:
> > +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > +  {
> > +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
> > +  }
> > +
> > +  symtab_node *get_node () const { return m_node; }
> > +
> > + private:
> > +  symtab_node *m_node;
> > +};
> > +
> > +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
> > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > new file mode 100644
> > index 0000000..1da7d37
> > --- /dev/null
> > +++ b/gcc/optinfo.cc
> > @@ -0,0 +1,251 @@
> > +/* Optimization information.
> > +   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/>.  */
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +
> > +#include "backend.h"
> > +#include "tree.h"
> > +#include "gimple.h"
> > +
> > +#include "optinfo.h"
> > +#include "optinfo-internal.h"
> > +#include "dump-context.h"
> > +#include "selftest.h"
> > +
> > +/* Remove any trailing whitespace characters from this text item.
> > +   Primarily for use in stripping trailing newline characters when
> > +   emitting remarks (since the diagnostic subsystem doesn't expect
> > +   trailing newlines in messages).  */
> > +
> > +void
> > +optinfo_item_text::trim_trailing_whitespace ()
> > +{
> > +  size_t len = strlen (m_text);
> > +  if (len == 0)
> > +    return;
> > +
> > +  size_t new_len = len;
> > +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
> > +    new_len--;
> > +
> > +  if (new_len == len)
> > +    return;
> > +
> > +  if (m_owned)
> > +    m_text[new_len] = '\0';
> > +  else
> > +    {
> > +      m_text = xstrndup (m_text, new_len);
> > +      m_owned = true;
> > +    }
> > +}
> > +
> > +/* Get a string from KIND.  */
> > +
> > +const char *
> > +optinfo_kind_to_string (enum optinfo_kind kind)
> > +{
> > +  switch (kind)
> > +    {
> > +    default:
> > +      gcc_unreachable ();
> > +    case OPTINFO_KIND_SUCCESS:
> > +      return "success";
> > +    case OPTINFO_KIND_FAILURE:
> > +      return "failure";
> > +    case OPTINFO_KIND_NOTE:
> > +      return "note";
> > +    case OPTINFO_KIND_SCOPE:
> > +      return "scope";
> > +    }
> > +}
> > +
> > +/* optinfo's dtor.  */
> > +
> > +optinfo::~optinfo ()
> > +{
> > +  /* Cleanup.  */
> > +  unsigned i;
> > +  optinfo_item *item;
> > +  FOR_EACH_VEC_ELT (m_items, i, item)
> > +    delete item;
> > +}
> > +
> > +/* Emit the optinfo to all of the active destinations.  */
> > +
> > +void
> > +optinfo::emit ()
> > +{
> > +  /* Eliminate any trailing whitespace.  */
> > +  while (m_items.length () > 0)
> > +    {
> > +      optinfo_item *last_item = m_items[m_items.length () - 1];
> > +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
> > +       break;
> > +
> > +      optinfo_item_text *last_text = (optinfo_item_text
> > *)last_item;
> > +      last_text->trim_trailing_whitespace ();
> > +
> > +      if (strlen (last_text->get_text ()) > 0)
> > +       break;
> > +
> > +      m_items.pop ();
> > +      delete last_item;
> > +    }
> > +
> > +  /* currently this is a no-op.  */
> > +}
> > +
> > +/* Update the optinfo's kind based on DUMP_KIND.  */
> > +
> > +void
> > +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> > +{
> > +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> > +    m_kind = OPTINFO_KIND_SUCCESS;
> > +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> > +    m_kind = OPTINFO_KIND_FAILURE;
> > +  else if (dump_kind & MSG_NOTE)
> > +    m_kind = OPTINFO_KIND_NOTE;
> > +}
> > +
> > +/* Append a string literal to this optinfo.  */
> > +
> > +void
> > +optinfo::add_string (const char *str)
> > +{
> > +  optinfo_item *item
> > +    = new optinfo_item_text (const_cast <char *> (str), false);
> > +  m_items.safe_push (item);
> > +}
> > +
> > +/* Append printf-formatted text to this optinfo.  */
> > +
> > +void
> > +optinfo::add_printf (const char *format, ...)
> > +{
> > +  va_list ap;
> > +  va_start (ap, format);
> > +  add_printf_va (format, ap);
> > +  va_end (ap);
> > +}
> > +
> > +/* Append printf-formatted text to this optinfo.  */
> > +
> > +void
> > +optinfo::add_printf_va (const char *format, va_list ap)
> > +{
> > +  char *formatted_text = xvasprintf (format, ap);
> > +  optinfo_item *item
> > +    = new optinfo_item_text (formatted_text, true);
> > +  m_items.safe_push (item);
> > +}
> > +
> > +/* Append a gimple statement to this optinfo.  */
> > +
> > +void
> > +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
> > +{
> > +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
> > +}
> > +
> > +/* Append a tree node to this optinfo.  */
> > +
> > +void
> > +optinfo::add_tree (tree node, dump_flags_t dump_flags)
> > +{
> > +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
> > +}
> > +
> > +/* Append a symbol table node to this optinfo.  */
> > +
> > +void
> > +optinfo::add_symtab_node (symtab_node *node)
> > +{
> > +  m_items.safe_push (new optinfo_item_symtab_node (node));
> > +}
> > +
> > +/* Append the decimal represenation of a wide_int_ref to this
> > +   optinfo.  */
> > +
> > +void
> > +optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> > +{
> > +  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> > +  print_dec (wi, buf, sgn);
> > +  optinfo_item *item
> > +    = new optinfo_item_text (xstrdup (buf), true);
> > +  m_items.safe_push (item);
> > +}
> > +
> > +/* Should optinfo instances be created?
> > +   All creation of optinfos should be guarded by this predicate.
> > +   Return true if any optinfo destinations are active.  */
> > +
> > +bool optinfo_enabled_p ()
> > +{
> > +  /* Currently no destinations are implemented, just a hook for
> > +     selftests.  */
> > +  return dump_context::get ().forcibly_enable_optinfo_p ();
> > +}
> > +
> > +/* Return true if any of the active optinfo destinations make use
> > +   of inlining information.
> > +   (if true, then the information is preserved).  */
> > +
> > +bool optinfo_wants_inlining_info_p ()
> > +{
> > +  return false;
> > +}
> > +
> > +#if CHECKING_P
> > +
> > +namespace selftest {
> > +
> > +/* Verify that optinfo_item_text::trim_trailing_whitespace turns
> > +   INPUT into EXPECTED.  */
> > +
> > +static void
> > +test_trim_trailing_whitespace (const char *input, const char
> > *expected)
> > +{
> > +  optinfo_item_text item (const_cast <char *> (input), false);
> > +  item.trim_trailing_whitespace ();
> > +  ASSERT_STREQ (item.get_text (), expected);
> > +}
> > +
> > +/* Run all of the selftests within this file.  */
> > +
> > +void
> > +optinfo_cc_tests ()
> > +{
> > +  /* Verify that optinfo_item_text::trim_trailing_whitespace
> > works.  */
> > +  test_trim_trailing_whitespace ("", "");
> > +  test_trim_trailing_whitespace ("\n", "");
> > +  test_trim_trailing_whitespace ("foo", "foo");
> > +  test_trim_trailing_whitespace ("foo\n", "foo");
> > +  test_trim_trailing_whitespace ("foo\n\n", "foo");
> > +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* CHECKING_P */
> > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > new file mode 100644
> > index 0000000..0d49823
> > --- /dev/null
> > +++ b/gcc/optinfo.h
> > @@ -0,0 +1,175 @@
> > +/* Optimization information.
> > +   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_OPTINFO_H
> > +#define GCC_OPTINFO_H
> > +
> > +/* An "optinfo" is a bundle of information describing part of an
> > +   optimization, which can be emitted to zero or more of several
> > +   destinations, such as:
> > +
> > +   * as a "remark" through the diagnostics subsystem
> > +
> > +   * saved to a file as an "optimization record"
> > +
> > +   Currently no such destinations are implemented.
> > +
> > +   They are generated in response to calls to the "dump_*" API in
> > +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> > +   into a pending optinfo instance, with a "dump_*_loc" starting a
> > new
> > +   optinfo instance.
> > +
> > +   The data sent to the dump calls are captured within the pending
> > optinfo
> > +   instance as a sequence of optinfo_items.  For example, given:
> > +
> > +      if (dump_enabled_p ())
> > +        {
> > +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > +                           "not vectorized: live stmt not
> > supported: ");
> > +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > stmt, 0);
> > +        }
> > +
> > +   the "dump_printf_loc" call begins a new optinfo containing two
> > items:
> > +   (1) a text item containing "not vectorized: live stmt not
> > supported: "
> > +   (2) a gimple item for "stmt"
> > +
> > +   Dump destinations are thus able to access rich metadata about
> > the
> > +   items when the optinfo is emitted to them, rather than just
> > having plain
> > +   text.  For example, when saving the above optinfo to a file as
> > an
> > +   "optimization record", the record could capture the source
> > location of
> > +   "stmt" above, rather than just its textual form.
> > +
> > +   The currently pending optinfo is emitted and deleted:
> > +   * each time a "dump_*_loc" call occurs (which starts the next
> > optinfo), or
> > +   * when the dump files are changed (at the end of a pass), or
> > +   * when a garbage collection is about to happen.  This safety
> > measure
> > +     ensures that no GC happens during the lifetime of an optinfo
> > instance,
> > +     and thus that any optinfo_items within the optinfo instances
> > can refer
> > +     to GC-allocated objects without needing to be GTY-marked:
> > they will never
> > +     refer to collected garbage.  optinfo and optinfo_item are not
> > GTY-marked
> > +     as it would make no sense for them to be in PCH files.
> > +
> > +   Dumping to an optinfo instance is non-trivial (due to building
> > optinfo_item
> > +   instances), so all usage should be guarded by
> > +
> > +     if (optinfo_enabled_p ())
> > +
> > +   which is off by default.  */
> > +
> > +
> > +/* Forward decls.  */
> > +struct opt_pass;
> > +class optinfo_item; /* optinfo-internal.h.  */
> > +
> > +/* Should optinfo instances be created?
> > +   All creation of optinfos should be guarded by this predicate.
> > +   Return true if any optinfo destinations are active.  */
> > +
> > +extern bool optinfo_enabled_p ();
> > +
> > +/* Return true if any of the active optinfo destinations make use
> > +   of inlining information.
> > +   (if true, then the information is preserved).  */
> > +
> > +extern bool optinfo_wants_inlining_info_p ();
> > +
> > +/* The various kinds of optinfo.  */
> > +
> > +enum optinfo_kind
> > +{
> > +  OPTINFO_KIND_SUCCESS,
> > +  OPTINFO_KIND_FAILURE,
> > +  OPTINFO_KIND_NOTE,
> > +  OPTINFO_KIND_SCOPE
> > +};
> > +
> > +extern const char *optinfo_kind_to_string (enum optinfo_kind
> > kind);
> > +
> > +/* A bundle of information describing part of an optimization.  */
> > +
> > +class optinfo
> > +{
> > +  friend class dump_context;
> > +
> > + public:
> > +  optinfo (const dump_location_t &loc,
> > +          enum optinfo_kind kind,
> > +          opt_pass *pass)
> > +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> > +  {}
> > +  ~optinfo ();
> > +
> > +  const dump_user_location_t &
> > +  get_user_location () const { return m_loc.get_user_location ();
> > }
> > +
> > +  const dump_impl_location_t &
> > +  get_impl_location () const { return m_loc.get_impl_location ();
> > }
> > +
> > +  enum optinfo_kind get_kind () const { return m_kind; }
> > +  opt_pass *get_pass () const { return m_pass; }
> > +  unsigned int num_items () const { return m_items.length (); }
> > +  const optinfo_item *get_item (unsigned int i) const { return
> > m_items[i]; }
> > +
> > +  location_t get_location_t () const { return m_loc.get_location_t
> > (); }
> > +  profile_count get_count () const { return m_loc.get_count (); }
> > +
> > + private:
> > +  void emit ();
> > +
> > +  /* Pre-canned ways of manipulating the optinfo, for use by
> > friend class
> > +     dump_context.  */
> > +  void handle_dump_file_kind (dump_flags_t);
> > +  void add_string (const char *str);
> > +  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
> > +  void add_printf_va (const char *format, va_list ap)
> > ATTRIBUTE_PRINTF (2, 0);
> > +  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
> > +  void add_tree (tree node, dump_flags_t dump_flags);
> > +  void add_symtab_node (symtab_node *node);
> > +  void add_dec (const wide_int_ref &wi, signop sgn);
> > +
> > +  template<unsigned int N, typename C>
> > +  void add_poly_int (const poly_int<N, C> &value)
> > +  {
> > +    /* Compare with dump_dec (MSG_NOTE, ).  */
> > +
> > +    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > +    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > UNSIGNED;
> > +
> > +    if (value.is_constant ())
> > +      add_dec (value.coeffs[0], sgn);
> > +    else
> > +      {
> > +       add_string ("[");
> > +       for (unsigned int i = 0; i < N; ++i)
> > +         {
> > +           add_dec (value.coeffs[i], sgn);
> > +           add_string (i == N - 1 ? "]" : ",");
> > +         }
> > +      }
> > +  }
> > +
> > + private:
> > +  dump_location_t m_loc;
> > +  enum optinfo_kind m_kind;
> > +  opt_pass *m_pass;
> > +  auto_vec <optinfo_item *> m_items;
> > +};
> > +
> > +#endif /* #ifndef GCC_OPTINFO_H */
> > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> > index 7f4d6f3..989c50a 100644
> > --- a/gcc/selftest-run-tests.c
> > +++ b/gcc/selftest-run-tests.c
> > @@ -72,6 +72,7 @@ selftest::run_tests ()
> >    typed_splay_tree_c_tests ();
> >    unique_ptr_tests_cc_tests ();
> >    opt_proposer_c_tests ();
> > +  optinfo_cc_tests ();
> > 
> >    /* Mid-level data structures.  */
> >    input_c_tests ();
> > diff --git a/gcc/selftest.h b/gcc/selftest.h
> > index 54fc488..48881c9 100644
> > --- a/gcc/selftest.h
> > +++ b/gcc/selftest.h
> > @@ -228,6 +228,7 @@ extern void gimple_c_tests ();
> >  extern void hash_map_tests_c_tests ();
> >  extern void hash_set_tests_c_tests ();
> >  extern void input_c_tests ();
> > +extern void optinfo_cc_tests ();
> >  extern void predict_c_tests ();
> >  extern void pretty_print_c_tests ();
> >  extern void read_rtl_function_c_tests ();
> > --
> > 1.8.5.3
> > 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/2] Add "optinfo" framework
  2018-07-10 11:01                               ` David Malcolm
@ 2018-07-10 11:25                                 ` Richard Biener
  0 siblings, 0 replies; 80+ messages in thread
From: Richard Biener @ 2018-07-10 11:25 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jul 10, 2018 at 1:00 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Mon, 2018-07-09 at 15:00 +0200, Richard Biener wrote:
> > On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > This patch implements a way to consolidate dump_* calls into
> > > optinfo objects, as enabling work towards being able to write out
> > > optimization records to a file, or emit them as diagnostic
> > > "remarks".
> > >
> > > The patch adds the support for building optinfo instances from
> > > dump_*
> > > calls, but leaves implementing any *users* of them to followup
> > > patches.
> > >
> > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > >
> > > OK for trunk?
> >
> > Looks good overall, but ...
> >
> > To "fix" the GC issue you'd need to capture all possibly interesting
> > information from tree/gimple while it is still in flight.  This _may_
> > be
> > necessary anyway since I remember writing code like
> >
> >   fprintf (dump_file, "old: ");
> >   print_gimple_stmt (..., stmt);
> >   gimple_set_rhs1 (stmt, op);
> >   fprintf (dump_file, "new: ");
> >   print_gmple_stmt (..., stmt);
> >   fprintf (dump_file, "\n");
> >
> > capturing interesting information means we know all targeted
> > optinfo channels, right?  And the optinfo consumers
> > need to handle "streams" of input and may not look back.
>
> > I've yet have to look at the 2nd patch but can you comment on
> > this?  How difficult is it to re-wire how the data flows to make
> > stmt re-use like the above possible?
>
> I *think* it's doable: rather than capture, say, a gimple *, the
> optinfo_item would capture the result of pp_gimple_stmt_1, plus some
> metadata.  In fact, it would probably allow for removing the
> optinfo_item subclasses, making optinfo_item concrete, containing
> something like:
>
>   /* Textual form.  */
>   char *m_text;
>   bool m_ownership_of_text;
>
>   /* Metadata for optimization records.  */
>   enum optinfo_item_kind m_kind;
>   location_t m_location;
>
> or somesuch.
>
> I'll have a go at implementing this.

Thanks, that would be much cleaner (if also a bit more fugly
when you need to debug things)

Richard.

> Thanks
> Dave
>
> > Thanks,
> > Richard.
> >
> > > gcc/ChangeLog:
> > >         * Makefile.in (OBJS): Add optinfo.o.
> > >         * coretypes.h (class symtab_node): New forward decl.
> > >         (struct cgraph_node): New forward decl.
> > >         (class varpool_node): New forward decl.
> > >         * dump-context.h: New file.
> > >         * dumpfile.c: Include "optinfo.h", "dump-context.h",
> > > "cgraph.h",
> > >         "tree-pass.h", "optinfo-internal.h".
> > >         (refresh_dumps_are_enabled): Use optinfo_enabled_p.
> > >         (set_dump_file): Call
> > > dumpfile_ensure_any_optinfo_are_flushed.
> > >         (set_alt_dump_file): Likewise.
> > >         (dump_context::~dump_context): New dtor.
> > >         (dump_gimple_stmt): Move implementation to...
> > >         (dump_context::dump_gimple_stmt): ...this new member
> > > function.
> > >         Add the stmt to any pending optinfo, creating one if need
> > > be.
> > >         (dump_gimple_stmt_loc): Move implementation to...
> > >         (dump_context::dump_gimple_stmt_loc): ...this new member
> > > function.
> > >         Convert param "loc" from location_t to const
> > > dump_location_t &.
> > >         Start a new optinfo and add the stmt to it.
> > >         (dump_generic_expr): Move implementation to...
> > >         (dump_context::dump_generic_expr): ...this new member
> > > function.
> > >         Add the tree to any pending optinfo, creating one if need
> > > be.
> > >         (dump_generic_expr_loc): Move implementation to...
> > >         (dump_context::dump_generic_expr_loc): ...this new member
> > >         function.  Add the tree to any pending optinfo, creating
> > > one if
> > >         need be.
> > >         (dump_printf): Move implementation to...
> > >         (dump_context::dump_printf_va): ...this new member
> > > function.  Add
> > >         the text to any pending optinfo, creating one if need be.
> > >         (dump_printf_loc): Move implementation to...
> > >         (dump_context::dump_printf_loc_va): ...this new member
> > > function.
> > >         Convert param "loc" from location_t to const
> > > dump_location_t &.
> > >         Start a new optinfo and add the stmt to it.
> > >         (dump_dec): Move implementation to...
> > >         (dump_context::dump_dec): ...this new member function.  Add
> > > the
> > >         value to any pending optinfo, creating one if need be.
> > >         (dump_context::dump_symtab_node): New member function.
> > >         (dump_context::get_scope_depth): New member function.
> > >         (dump_context::begin_scope): New member function.
> > >         (dump_context::end_scope): New member function.
> > >         (dump_context::ensure_pending_optinfo): New member
> > > function.
> > >         (dump_context::begin_next_optinfo): New member function.
> > >         (dump_context::end_any_optinfo): New member function.
> > >         (dump_context::s_current): New global.
> > >         (dump_context::s_default): New global.
> > >         (dump_scope_depth): Delete global.
> > >         (dumpfile_ensure_any_optinfo_are_flushed): New function.
> > >         (dump_symtab_node): New function.
> > >         (get_dump_scope_depth): Reimplement in terms of
> > > dump_context.
> > >         (dump_begin_scope): Likewise.
> > >         (dump_end_scope): Likewise.
> > >         (selftest::temp_dump_context::temp_dump_context): New ctor.
> > >         (selftest::temp_dump_context::~temp_dump_context): New
> > > dtor.
> > >         (selftest::assert_is_text): New support function.
> > >         (selftest::assert_is_tree): New support function.
> > >         (selftest::assert_is_gimple): New support function.
> > >         (selftest::test_capture_of_dump_calls): New test.
> > >         (selftest::dumpfile_c_tests): Call it.
> > >         * dumpfile.h (dump_printf, dump_printf_loc,
> > > dump_basic_block,
> > >         dump_generic_expr_loc, dump_generic_expr,
> > > dump_gimple_stmt_loc,
> > >         dump_gimple_stmt, dump_dec): Gather these related decls and
> > > add a
> > >         descriptive comment.
> > >         (dump_function, print_combine_total_stats,
> > > enable_rtl_dump_file,
> > >         dump_node, dump_bb): Move these unrelated decls.
> > >         (class dump_manager): Add leading comment.
> > >         * ggc-page.c (ggc_collect): Call
> > >         dumpfile_ensure_any_optinfo_are_flushed.
> > >         * optinfo-internal.h: New file.
> > >         * optinfo.cc: New file.
> > >         * optinfo.h: New file.
> > >         * selftest-run-tests.c (selftest::run_tests): Call
> > >         selftest::optinfo_cc_tests.
> > >         * selftest.h (selftest::optinfo_cc_tests): New decl.
> > > ---
> > >  gcc/Makefile.in          |   1 +
> > >  gcc/coretypes.h          |   7 +
> > >  gcc/dump-context.h       | 128 ++++++++++++
> > >  gcc/dumpfile.c           | 498
> > > +++++++++++++++++++++++++++++++++++++++++++----
> > >  gcc/dumpfile.h           |  82 +++++---
> > >  gcc/ggc-page.c           |   2 +
> > >  gcc/optinfo-internal.h   | 148 ++++++++++++++
> > >  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++
> > >  gcc/optinfo.h            | 175 +++++++++++++++++
> > >  gcc/selftest-run-tests.c |   1 +
> > >  gcc/selftest.h           |   1 +
> > >  11 files changed, 1233 insertions(+), 61 deletions(-)
> > >  create mode 100644 gcc/dump-context.h
> > >  create mode 100644 gcc/optinfo-internal.h
> > >  create mode 100644 gcc/optinfo.cc
> > >  create mode 100644 gcc/optinfo.h
> > >
> > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > > index 66c8b6e..7d36a77 100644
> > > --- a/gcc/Makefile.in
> > > +++ b/gcc/Makefile.in
> > > @@ -1426,6 +1426,7 @@ OBJS = \
> > >         optabs-libfuncs.o \
> > >         optabs-query.o \
> > >         optabs-tree.o \
> > > +       optinfo.o \
> > >         options-save.o \
> > >         opts-global.o \
> > >         passes.o \
> > > diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> > > index 283b4eb..ed0e825 100644
> > > --- a/gcc/coretypes.h
> > > +++ b/gcc/coretypes.h
> > > @@ -134,6 +134,13 @@ struct gomp_single;
> > >  struct gomp_target;
> > >  struct gomp_teams;
> > >
> > > +/* Subclasses of symtab_node, using indentation to show the class
> > > +   hierarchy.  */
> > > +
> > > +class symtab_node;
> > > +  struct cgraph_node;
> > > +  class varpool_node;
> > > +
> > >  union section;
> > >  typedef union section section;
> > >  struct gcc_options;
> > > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > > new file mode 100644
> > > index 0000000..753f714
> > > --- /dev/null
> > > +++ b/gcc/dump-context.h
> > > @@ -0,0 +1,128 @@
> > > +/* Support code for handling the various dump_* calls in
> > > dumpfile.h
> > > +   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_DUMP_CONTEXT_H
> > > +#define GCC_DUMP_CONTEXT_H 1
> > > +
> > > +/* A class for handling the various dump_* calls.
> > > +
> > > +   In particular, this class has responsibility for consolidating
> > > +   the "dump_*" calls into optinfo instances (delimited by
> > > "dump_*_loc"
> > > +   calls), and emitting them.
> > > +
> > > +   Putting this in a class (rather than as global state) allows
> > > +   for selftesting of this code.  */
> > > +
> > > +class dump_context
> > > +{
> > > +  friend class temp_dump_context;
> > > + public:
> > > +  static dump_context &get () { return *s_current; }
> > > +
> > > +  ~dump_context ();
> > > +
> > > +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > +                        gimple *gs, int spc);
> > > +
> > > +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > > +                            const dump_location_t &loc,
> > > +                            dump_flags_t extra_dump_flags,
> > > +                            gimple *gs, int spc);
> > > +
> > > +  void dump_generic_expr (dump_flags_t dump_kind,
> > > +                         dump_flags_t extra_dump_flags,
> > > +                         tree t);
> > > +
> > > +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> > > +                             const dump_location_t &loc,
> > > +                             dump_flags_t extra_dump_flags,
> > > +                             tree t);
> > > +
> > > +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> > > +                      va_list ap) ATTRIBUTE_PRINTF (3, 0);
> > > +
> > > +  void dump_printf_loc_va (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > +                          const char *format, va_list ap)
> > > +    ATTRIBUTE_PRINTF (4, 0);
> > > +
> > > +  template<unsigned int N, typename C>
> > > +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> > > &value);
> > > +
> > > +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node
> > > *node);
> > > +
> > > +  /* Managing nested scopes.  */
> > > +  unsigned int get_scope_depth () const;
> > > +  void begin_scope (const char *name, const dump_location_t &loc);
> > > +  void end_scope ();
> > > +
> > > +  /* For use in selftests; if true then optinfo_enabled_p is
> > > true.  */
> > > +  bool forcibly_enable_optinfo_p () const
> > > +  {
> > > +    return m_forcibly_enable_optinfo;
> > > +  }
> > > +
> > > +  void end_any_optinfo ();
> > > +
> > > + private:
> > > +  optinfo &ensure_pending_optinfo ();
> > > +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> > > +
> > > +  /* For use in selftests; if true then optinfo_enabled_p is
> > > true.  */
> > > +  bool m_forcibly_enable_optinfo;
> > > +
> > > +  /* The current nesting depth of dump scopes, for showing nesting
> > > +     via indentation).  */
> > > +  unsigned int m_scope_depth;
> > > +
> > > +  /* The optinfo currently being accumulated since the last
> > > dump_*_loc call,
> > > +     if any.  */
> > > +  optinfo *m_pending;
> > > +
> > > +  /* The currently active dump_context, for use by the dump_* API
> > > calls.  */
> > > +  static dump_context *s_current;
> > > +
> > > +  /* The default active context.  */
> > > +  static dump_context s_default;
> > > +};
> > > +
> > > +#if CHECKING_P
> > > +
> > > +/* An RAII-style class for use in selftests for temporarily using
> > > a different
> > > +   dump_context.  */
> > > +
> > > +class temp_dump_context
> > > +{
> > > + public:
> > > +  temp_dump_context (bool forcibly_enable_optinfo);
> > > +  ~temp_dump_context ();
> > > +
> > > +  /* Support for selftests.  */
> > > +  optinfo *get_pending_optinfo () const { return
> > > m_context.m_pending; }
> > > +
> > > + private:
> > > +  dump_context m_context;
> > > +  dump_context *m_saved;
> > > +  bool m_saved_flag_remarks;
> > > +};
> > > +
> > > +#endif /* CHECKING_P */
> > > +
> > > +#endif /* GCC_DUMP_CONTEXT_H */
> > > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > > index 5f69f9b..6e089ef 100644
> > > --- a/gcc/dumpfile.c
> > > +++ b/gcc/dumpfile.c
> > > @@ -33,6 +33,11 @@ along with GCC; see the file COPYING3.  If not
> > > see
> > >  #include "gimple.h" /* for dump_user_location_t ctor.  */
> > >  #include "rtl.h" /* for dump_user_location_t ctor.  */
> > >  #include "selftest.h"
> > > +#include "optinfo.h"
> > > +#include "dump-context.h"
> > > +#include "cgraph.h"
> > > +#include "tree-pass.h" /* for "current_pass".  */
> > > +#include "optinfo-internal.h" /* for selftests.  */
> > >
> > >  /* If non-NULL, return one past-the-end of the matching SUBPART of
> > >     the WHOLE string.  */
> > > @@ -64,7 +69,7 @@ bool dumps_are_enabled = false;
> > >  static void
> > >  refresh_dumps_are_enabled ()
> > >  {
> > > -  dumps_are_enabled = (dump_file || alt_dump_file);
> > > +  dumps_are_enabled = (dump_file || alt_dump_file ||
> > > optinfo_enabled_p ());
> > >  }
> > >
> > >  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> > > "dumps_are_enabled"
> > > @@ -73,6 +78,7 @@ refresh_dumps_are_enabled ()
> > >  void
> > >  set_dump_file (FILE *new_dump_file)
> > >  {
> > > +  dumpfile_ensure_any_optinfo_are_flushed ();
> > >    dump_file = new_dump_file;
> > >    refresh_dumps_are_enabled ();
> > >  }
> > > @@ -83,6 +89,7 @@ set_dump_file (FILE *new_dump_file)
> > >  static void
> > >  set_alt_dump_file (FILE *new_alt_dump_file)
> > >  {
> > > +  dumpfile_ensure_any_optinfo_are_flushed ();
> > >    alt_dump_file = new_alt_dump_file;
> > >    refresh_dumps_are_enabled ();
> > >  }
> > > @@ -458,25 +465,44 @@ dump_loc (dump_flags_t dump_kind, FILE
> > > *dfile, source_location loc)
> > >      }
> > >  }
> > >
> > > +/* Implementation of dump_context member functions.  */
> > > +
> > > +/* dump_context's dtor.  */
> > > +
> > > +dump_context::~dump_context ()
> > > +{
> > > +  delete m_pending;
> > > +}
> > > +
> > >  /* Dump gimple statement GS with SPC indentation spaces and
> > >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > > enabled.  */
> > >
> > >  void
> > > -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > -                 gimple *gs, int spc)
> > > +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> > > +                               dump_flags_t extra_dump_flags,
> > > +                               gimple *gs, int spc)
> > >  {
> > >    if (dump_file && (dump_kind & pflags))
> > >      print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > >
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = ensure_pending_optinfo ();
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_stmt (gs, extra_dump_flags);
> > > +    }
> > >  }
> > >
> > >  /* Similar to dump_gimple_stmt, except additionally print source
> > > location.  */
> > >
> > >  void
> > > -dump_gimple_stmt_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > -                     dump_flags_t extra_dump_flags, gimple *gs,
> > > int spc)
> > > +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > > +                                   const dump_location_t &loc,
> > > +                                   dump_flags_t extra_dump_flags,
> > > +                                   gimple *gs, int spc)
> > >  {
> > >    location_t srcloc = loc.get_location_t ();
> > >    if (dump_file && (dump_kind & pflags))
> > > @@ -490,20 +516,35 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> > > const dump_location_t &loc,
> > >        dump_loc (dump_kind, alt_dump_file, srcloc);
> > >        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > >      }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = begin_next_optinfo (loc);
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_stmt (gs, extra_dump_flags);
> > > +    }
> > >  }
> > >
> > >  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > > if
> > >     DUMP_KIND is enabled.  */
> > >
> > >  void
> > > -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > -                  tree t)
> > > +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> > > +                                dump_flags_t extra_dump_flags,
> > > +                                tree t)
> > >  {
> > >    if (dump_file && (dump_kind & pflags))
> > >        print_generic_expr (dump_file, t, dump_flags |
> > > extra_dump_flags);
> > >
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >        print_generic_expr (alt_dump_file, t, dump_flags |
> > > extra_dump_flags);
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = ensure_pending_optinfo ();
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_tree (t, extra_dump_flags);
> > > +    }
> > >  }
> > >
> > >
> > > @@ -511,8 +552,10 @@ dump_generic_expr (dump_flags_t dump_kind,
> > > dump_flags_t extra_dump_flags,
> > >     location.  */
> > >
> > >  void
> > > -dump_generic_expr_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > -                      dump_flags_t extra_dump_flags, tree t)
> > > +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> > > +                                    const dump_location_t &loc,
> > > +                                    dump_flags_t extra_dump_flags,
> > > +                                    tree t)
> > >  {
> > >    location_t srcloc = loc.get_location_t ();
> > >    if (dump_file && (dump_kind & pflags))
> > > @@ -526,53 +569,82 @@ dump_generic_expr_loc (dump_flags_t
> > > dump_kind, const dump_location_t &loc,
> > >        dump_loc (dump_kind, alt_dump_file, srcloc);
> > >        print_generic_expr (alt_dump_file, t, dump_flags |
> > > extra_dump_flags);
> > >      }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = begin_next_optinfo (loc);
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_tree (t, extra_dump_flags);
> > > +    }
> > >  }
> > >
> > >  /* Output a formatted message using FORMAT on appropriate dump
> > > streams.  */
> > >
> > >  void
> > > -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> > > +dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > > *format,
> > > +                             va_list ap)
> > >  {
> > >    if (dump_file && (dump_kind & pflags))
> > >      {
> > > -      va_list ap;
> > > -      va_start (ap, format);
> > > -      vfprintf (dump_file, format, ap);
> > > -      va_end (ap);
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      vfprintf (dump_file, format, aq);
> > > +      va_end (aq);
> > >      }
> > >
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      {
> > > -      va_list ap;
> > > -      va_start (ap, format);
> > > -      vfprintf (alt_dump_file, format, ap);
> > > -      va_end (ap);
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      vfprintf (alt_dump_file, format, aq);
> > > +      va_end (aq);
> > > +    }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = ensure_pending_optinfo ();
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      info.add_printf_va (format, aq);
> > > +      va_end (aq);
> > >      }
> > >  }
> > >
> > > -/* Similar to dump_printf, except source location is also
> > > printed.  */
> > > +/* Similar to dump_printf, except source location is also printed,
> > > and
> > > +   dump location captured.  */
> > >
> > >  void
> > > -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t
> > > &loc,
> > > -                const char *format, ...)
> > > +dump_context::dump_printf_loc_va (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > +                                 const char *format, va_list ap)
> > >  {
> > >    location_t srcloc = loc.get_location_t ();
> > > +
> > >    if (dump_file && (dump_kind & pflags))
> > >      {
> > > -      va_list ap;
> > >        dump_loc (dump_kind, dump_file, srcloc);
> > > -      va_start (ap, format);
> > > -      vfprintf (dump_file, format, ap);
> > > -      va_end (ap);
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      vfprintf (dump_file, format, aq);
> > > +      va_end (aq);
> > >      }
> > >
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      {
> > > -      va_list ap;
> > >        dump_loc (dump_kind, alt_dump_file, srcloc);
> > > -      va_start (ap, format);
> > > -      vfprintf (alt_dump_file, format, ap);
> > > -      va_end (ap);
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      vfprintf (alt_dump_file, format, aq);
> > > +      va_end (aq);
> > > +    }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = begin_next_optinfo (loc);
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      va_list aq;
> > > +      va_copy (aq, ap);
> > > +      info.add_printf_va (format, aq);
> > > +      va_end (aq);
> > >      }
> > >  }
> > >
> > > @@ -580,7 +652,7 @@ dump_printf_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > >
> > >  template<unsigned int N, typename C>
> > >  void
> > > -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> > > +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > > C> &value)
> > >  {
> > >    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > >    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > > UNSIGNED;
> > > @@ -589,6 +661,203 @@ dump_dec (dump_flags_t dump_kind, const
> > > poly_int<N, C> &value)
> > >
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      print_dec (value, alt_dump_file, sgn);
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = ensure_pending_optinfo ();
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_poly_int<N,C> (value);
> > > +    }
> > > +}
> > > +
> > > +/* Output the name of NODE on appropriate dump streams.  */
> > > +
> > > +void
> > > +dump_context::dump_symtab_node (dump_flags_t dump_kind,
> > > symtab_node *node)
> > > +{
> > > +  if (dump_file && (dump_kind & pflags))
> > > +    fprintf (dump_file, "%s", node->dump_name ());
> > > +
> > > +  if (alt_dump_file && (dump_kind & alt_flags))
> > > +    fprintf (alt_dump_file, "%s", node->dump_name ());
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      optinfo &info = ensure_pending_optinfo ();
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_symtab_node (node);
> > > +    }
> > > +}
> > > +
> > > +/* Get the current dump scope-nesting depth.
> > > +   For use by -fopt-info (for showing nesting via
> > > indentation).  */
> > > +
> > > +unsigned int
> > > +dump_context::get_scope_depth () const
> > > +{
> > > +  return m_scope_depth;
> > > +}
> > > +
> > > +/* Push a nested dump scope.
> > > +   Print "=== NAME ===\n" to the dumpfile, if any, and to the
> > > -fopt-info
> > > +   destination, if any.
> > > +   Emit a "scope" optinfo if optinfos are enabled.
> > > +   Increment the scope depth.  */
> > > +
> > > +void
> > > +dump_context::begin_scope (const char *name, const dump_location_t
> > > &loc)
> > > +{
> > > +  /* Specialcase, to avoid going through dump_printf_loc,
> > > +     so that we can create a optinfo of kind
> > > OPTINFO_KIND_SCOPE.  */
> > > +
> > > +  if (dump_file)
> > > +    {
> > > +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > > +      fprintf (dump_file, "=== %s ===\n", name);
> > > +    }
> > > +
> > > +  if (alt_dump_file)
> > > +    {
> > > +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > > +      fprintf (alt_dump_file, "=== %s ===\n", name);
> > > +    }
> > > +
> > > +  if (optinfo_enabled_p ())
> > > +    {
> > > +      end_any_optinfo ();
> > > +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> > > +      info.add_printf ("=== %s ===", name);
> > > +      info.emit ();
> > > +    }
> > > +
> > > +  m_scope_depth++;
> > > +}
> > > +
> > > +/* Pop a nested dump scope.  */
> > > +
> > > +void
> > > +dump_context::end_scope ()
> > > +{
> > > +  end_any_optinfo ();
> > > +  m_scope_depth--;
> > > +}
> > > +
> > > +/* Return the optinfo currently being accumulated, creating one if
> > > +   necessary.  */
> > > +
> > > +optinfo &
> > > +dump_context::ensure_pending_optinfo ()
> > > +{
> > > +  if (!m_pending)
> > > +    return begin_next_optinfo (dump_location_t
> > > (dump_user_location_t ()));
> > > +  return *m_pending;
> > > +}
> > > +
> > > +/* Start a new optinfo and return it, ending any optinfo that was
> > > already
> > > +   accumulated.  */
> > > +
> > > +optinfo &
> > > +dump_context::begin_next_optinfo (const dump_location_t &loc)
> > > +{
> > > +  end_any_optinfo ();
> > > +  gcc_assert (m_pending == NULL);
> > > +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> > > +  return *m_pending;
> > > +}
> > > +
> > > +/* End any optinfo that has been accumulated within this context;
> > > emitting
> > > +   it to any destinations as appropriate - though none have
> > > currently been
> > > +   implemented.  */
> > > +
> > > +void
> > > +dump_context::end_any_optinfo ()
> > > +{
> > > +  if (m_pending)
> > > +    m_pending->emit ();
> > > +  delete m_pending;
> > > +  m_pending = NULL;
> > > +}
> > > +
> > > +/* The current singleton dump_context, and its default.  */
> > > +
> > > +dump_context *dump_context::s_current = &dump_context::s_default;
> > > +dump_context dump_context::s_default;
> > > +
> > > +/* Implementation of dump_* API calls, calling into dump_context
> > > +   member functions.  */
> > > +
> > > +/* Dump gimple statement GS with SPC indentation spaces and
> > > +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > > enabled.  */
> > > +
> > > +void
> > > +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > +                 gimple *gs, int spc)
> > > +{
> > > +  dump_context::get ().dump_gimple_stmt (dump_kind,
> > > extra_dump_flags, gs, spc);
> > > +}
> > > +
> > > +/* Similar to dump_gimple_stmt, except additionally print source
> > > location.  */
> > > +
> > > +void
> > > +dump_gimple_stmt_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > +                     dump_flags_t extra_dump_flags, gimple *gs,
> > > int spc)
> > > +{
> > > +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc,
> > > extra_dump_flags,
> > > +                                            gs, spc);
> > > +}
> > > +
> > > +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > > if
> > > +   DUMP_KIND is enabled.  */
> > > +
> > > +void
> > > +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > +                  tree t)
> > > +{
> > > +  dump_context::get ().dump_generic_expr (dump_kind,
> > > extra_dump_flags, t);
> > > +}
> > > +
> > > +/* Similar to dump_generic_expr, except additionally print the
> > > source
> > > +   location.  */
> > > +
> > > +void
> > > +dump_generic_expr_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc,
> > > +                      dump_flags_t extra_dump_flags, tree t)
> > > +{
> > > +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc,
> > > extra_dump_flags,
> > > +                                             t);
> > > +}
> > > +
> > > +/* Output a formatted message using FORMAT on appropriate dump
> > > streams.  */
> > > +
> > > +void
> > > +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> > > +{
> > > +  va_list ap;
> > > +  va_start (ap, format);
> > > +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> > > +  va_end (ap);
> > > +}
> > > +
> > > +/* Similar to dump_printf, except source location is also printed,
> > > and
> > > +   dump location captured.  */
> > > +
> > > +void
> > > +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t
> > > &loc,
> > > +                const char *format, ...)
> > > +{
> > > +  va_list ap;
> > > +  va_start (ap, format);
> > > +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > > ap);
> > > +  va_end (ap);
> > > +}
> > > +
> > > +/* Output VALUE in decimal to appropriate dump streams.  */
> > > +
> > > +template<unsigned int N, typename C>
> > > +void
> > > +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> > > +{
> > > +  dump_context::get ().dump_dec (dump_kind, value);
> > >  }
> > >
> > >  template void dump_dec (dump_flags_t, const poly_uint16 &);
> > > @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const
> > > poly_uint64 &);
> > >  template void dump_dec (dump_flags_t, const poly_offset_int &);
> > >  template void dump_dec (dump_flags_t, const poly_widest_int &);
> > >
> > > -/* The current dump scope-nesting depth.  */
> > > +/* Emit and delete the currently pending optinfo, if there is one,
> > > +   without the caller needing to know about class
> > > dump_context.  */
> > > +
> > > +void
> > > +dumpfile_ensure_any_optinfo_are_flushed ()
> > > +{
> > > +  dump_context::get().end_any_optinfo ();
> > > +}
> > > +
> > > +/* Output the name of NODE on appropriate dump streams.  */
> > >
> > > -static int dump_scope_depth;
> > > +void
> > > +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> > > +{
> > > +  dump_context::get ().dump_symtab_node (dump_kind, node);
> > > +}
> > >
> > >  /* Get the current dump scope-nesting depth.
> > > -   For use by dump_*_loc (for showing nesting via
> > > indentation).  */
> > > +   For use by -fopt-info (for showing nesting via
> > > indentation).  */
> > >
> > >  unsigned int
> > >  get_dump_scope_depth ()
> > >  {
> > > -  return dump_scope_depth;
> > > +  return dump_context::get ().get_scope_depth ();
> > >  }
> > >
> > >  /* Push a nested dump scope.
> > >     Print "=== NAME ===\n" to the dumpfile, if any, and to the
> > > -fopt-info
> > >     destination, if any.
> > > +   Emit a "scope" opinfo if optinfos are enabled.
> > >     Increment the scope depth.  */
> > >
> > >  void
> > >  dump_begin_scope (const char *name, const dump_location_t &loc)
> > >  {
> > > -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> > > -  dump_scope_depth++;
> > > +  dump_context::get ().begin_scope (name, loc);
> > >  }
> > >
> > >  /* Pop a nested dump scope.  */
> > > @@ -627,7 +909,7 @@ dump_begin_scope (const char *name, const
> > > dump_location_t &loc)
> > >  void
> > >  dump_end_scope ()
> > >  {
> > > -  dump_scope_depth--;
> > > +  dump_context::get ().end_scope ();
> > >  }
> > >
> > >  /* Start a dump for PHASE. Store user-supplied dump flags in
> > > @@ -1180,6 +1462,24 @@ enable_rtl_dump_file (void)
> > >
> > >  #if CHECKING_P
> > >
> > > +/* temp_dump_context's ctor.  Temporarily override the
> > > dump_context
> > > +   (to forcibly enable optinfo-generation).  */
> > > +
> > > +temp_dump_context::temp_dump_context (bool
> > > forcibly_enable_optinfo)
> > > +: m_context (),
> > > +  m_saved (&dump_context ().get ())
> > > +{
> > > +  dump_context::s_current = &m_context;
> > > +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> > > +}
> > > +
> > > +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> > > +
> > > +temp_dump_context::~temp_dump_context ()
> > > +{
> > > +  dump_context::s_current = m_saved;
> > > +}
> > > +
> > >  namespace selftest {
> > >
> > >  /* Verify that the dump_location_t constructors capture the source
> > > location
> > > @@ -1216,12 +1516,136 @@ test_impl_location ()
> > >  #endif
> > >  }
> > >
> > > +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> > > +
> > > +static void
> > > +assert_is_text (const optinfo_item *item, const char
> > > *expected_text)
> > > +{
> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
> > > +  const optinfo_item_text * text_item
> > > +    = static_cast <const optinfo_item_text *> (item);
> > > +  ASSERT_STREQ (text_item->get_text (), expected_text);
> > > +}
> > > +
> > > +/* Verify that ITEM is a tree item, with the expected values.  */
> > > +
> > > +static void
> > > +assert_is_tree (const optinfo_item *item, tree expected_tree,
> > > +               dump_flags_t expected_dump_flags)
> > > +{
> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
> > > +  const optinfo_item_tree * tree_item
> > > +    = static_cast <const optinfo_item_tree *> (item);
> > > +  ASSERT_EQ (tree_item->get_node (), expected_tree);
> > > +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
> > > +}
> > > +
> > > +/* Verify that ITEM is a gimple item, with the expected
> > > values.  */
> > > +
> > > +static void
> > > +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
> > > +                 dump_flags_t expected_dump_flags)
> > > +{
> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
> > > +  const optinfo_item_gimple * gimple_item
> > > +    = static_cast <const optinfo_item_gimple *> (item);
> > > +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
> > > +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
> > > +}
> > > +
> > > +/* Verify that calls to the dump_* API are captured and
> > > consolidated into
> > > +   optimization records. */
> > > +
> > > +static void
> > > +test_capture_of_dump_calls ()
> > > +{
> > > +  dump_location_t loc;
> > > +
> > > +  /* Tree, via dump_generic_expr.  */
> > > +  {
> > > +    temp_dump_context tmp (true);
> > > +    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > > +    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > > +
> > > +    optinfo *info = tmp.get_pending_optinfo ();
> > > +    ASSERT_TRUE (info != NULL);
> > > +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > +    ASSERT_EQ (info->num_items (), 2);
> > > +    assert_is_text (info->get_item (0), "test of tree: ");
> > > +    assert_is_tree (info->get_item (1), integer_zero_node,
> > > TDF_SLIM);
> > > +  }
> > > +
> > > +  /* Tree, via dump_generic_expr_loc.  */
> > > +  {
> > > +    temp_dump_context tmp (true);
> > > +    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > > integer_one_node);
> > > +
> > > +    optinfo *info = tmp.get_pending_optinfo ();
> > > +    ASSERT_TRUE (info != NULL);
> > > +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > +    ASSERT_EQ (info->num_items (), 1);
> > > +    assert_is_tree (info->get_item (0), integer_one_node,
> > > TDF_SLIM);
> > > +  }
> > > +
> > > +  /* Gimple.  */
> > > +  {
> > > +    greturn *stmt = gimple_build_return (NULL);
> > > +
> > > +    temp_dump_context tmp (true);
> > > +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
> > > +
> > > +    optinfo *info = tmp.get_pending_optinfo ();
> > > +    ASSERT_TRUE (info != NULL);
> > > +    ASSERT_EQ (info->num_items (), 1);
> > > +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
> > > +
> > > +    /* Verify that optinfo instances are flushed if a GC is about
> > > to
> > > +       happen (and thus don't need to be GTY-marked).
> > > +       We don't want them in the PCH file, but we don't want the
> > > +       items to have their data collected from under them.  */
> > > +    selftest::forcibly_ggc_collect ();
> > > +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);
> > > +  }
> > > +
> > > +  /* poly_int.  */
> > > +  {
> > > +    temp_dump_context tmp (true);
> > > +    dump_dec (MSG_NOTE, poly_int64 (42));
> > > +
> > > +    optinfo *info = tmp.get_pending_optinfo ();
> > > +    ASSERT_TRUE (info != NULL);
> > > +    ASSERT_EQ (info->num_items (), 1);
> > > +    assert_is_text (info->get_item (0), "42");
> > > +  }
> > > +
> > > +  /* Verify that MSG_* affects optinfo->get_kind (); we tested
> > > MSG_NOTE
> > > +     above.  */
> > > +  {
> > > +    /* MSG_OPTIMIZED_LOCATIONS.  */
> > > +    {
> > > +      temp_dump_context tmp (true);
> > > +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> > > +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > > +                OPTINFO_KIND_SUCCESS);
> > > +    }
> > > +
> > > +    /* MSG_MISSED_OPTIMIZATION.  */
> > > +    {
> > > +      temp_dump_context tmp (true);
> > > +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> > > +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > > +                OPTINFO_KIND_FAILURE);
> > > +    }
> > > +  }
> > > +}
> > > +
> > >  /* Run all of the selftests within this file.  */
> > >
> > >  void
> > >  dumpfile_c_tests ()
> > >  {
> > >    test_impl_location ();
> > > +  test_capture_of_dump_calls ();
> > >  }
> > >
> > >  } // namespace selftest
> > > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > > index 0e588a6..899bb89 100644
> > > --- a/gcc/dumpfile.h
> > > +++ b/gcc/dumpfile.h
> > > @@ -420,30 +420,6 @@ extern FILE *dump_begin (int, dump_flags_t *);
> > >  extern void dump_end (int, FILE *);
> > >  extern int opt_info_switch_p (const char *);
> > >  extern const char *dump_flag_name (int);
> > > -extern void dump_printf (dump_flags_t, const char *, ...)
> > > ATTRIBUTE_PRINTF_2;
> > > -extern void dump_printf_loc (dump_flags_t, const dump_location_t
> > > &,
> > > -                            const char *, ...) ATTRIBUTE_PRINTF_3;
> > > -extern void dump_function (int phase, tree fn);
> > > -extern void dump_basic_block (dump_flags_t, basic_block, int);
> > > -extern void dump_generic_expr_loc (dump_flags_t, const
> > > dump_location_t &,
> > > -                                  dump_flags_t, tree);
> > > -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> > > -extern void dump_gimple_stmt_loc (dump_flags_t, const
> > > dump_location_t &,
> > > -                                 dump_flags_t, gimple *, int);
> > > -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple
> > > *, int);
> > > -extern void print_combine_total_stats (void);
> > > -extern bool enable_rtl_dump_file (void);
> > > -
> > > -template<unsigned int N, typename C>
> > > -void dump_dec (dump_flags_t, const poly_int<N, C> &);
> > > -
> > > -/* In tree-dump.c  */
> > > -extern void dump_node (const_tree, dump_flags_t, FILE *);
> > > -
> > > -/* In combine.c  */
> > > -extern void dump_combine_total_stats (FILE *);
> > > -/* In cfghooks.c  */
> > > -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> > >
> > >  /* Global variables used to communicate with passes.  */
> > >  extern FILE *dump_file;
> > > @@ -461,6 +437,49 @@ dump_enabled_p (void)
> > >    return dumps_are_enabled;
> > >  }
> > >
> > > +/* The following API calls (which *don't* take a "FILE *")
> > > +   write the output to zero or more locations:
> > > +   (a) the active dump_file, if any
> > > +   (b) the -fopt-info destination, if any
> > > +   (c) to the "optinfo" destinations, if any:
> > > +
> > > +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> > > +                                   |
> > > +                                   +--> (b) alt_dump_file
> > > +                                   |
> > > +                                   `--> (c) optinfo
> > > +                                            `---> optinfo
> > > destinations
> > > +
> > > +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> > > +   instance: all subsequent dump_* calls are consolidated into
> > > +   that optinfo, until the next dump_*_loc call (or a change in
> > > +   dump scope, or a call to
> > > dumpfile_ensure_any_optinfo_are_flushed).
> > > +
> > > +   A group of dump_* calls should be guarded by:
> > > +
> > > +     if (dump_enabled_p ())
> > > +
> > > +   to minimize the work done for the common case where dumps
> > > +   are disabled.  */
> > > +
> > > +extern void dump_printf (dump_flags_t, const char *, ...)
> > > ATTRIBUTE_PRINTF_2;
> > > +extern void dump_printf_loc (dump_flags_t, const dump_location_t
> > > &,
> > > +                            const char *, ...) ATTRIBUTE_PRINTF_3;
> > > +extern void dump_function (int phase, tree fn);
> > > +extern void dump_basic_block (dump_flags_t, basic_block, int);
> > > +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
> > > +extern void dump_generic_expr_loc (dump_flags_t, const
> > > dump_location_t &,
> > > +                                  dump_flags_t, tree);
> > > +extern void dump_gimple_stmt_loc (dump_flags_t, const
> > > dump_location_t &,
> > > +                                 dump_flags_t, gimple *, int);
> > > +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple
> > > *, int);
> > > +extern void dump_symtab_node (dump_flags_t, symtab_node *);
> > > +
> > > +template<unsigned int N, typename C>
> > > +void dump_dec (dump_flags_t, const poly_int<N, C> &);
> > > +
> > > +extern void dumpfile_ensure_any_optinfo_are_flushed ();
> > > +
> > >  /* Managing nested scopes, so that dumps can express the call
> > > chain
> > >     leading to a dump message.  */
> > >
> > > @@ -500,8 +519,23 @@ class auto_dump_scope
> > >  #define AUTO_DUMP_SCOPE(NAME, LOC) \
> > >    auto_dump_scope scope (NAME, LOC)
> > >
> > > +extern void dump_function (int phase, tree fn);
> > > +extern void print_combine_total_stats (void);
> > > +extern bool enable_rtl_dump_file (void);
> > > +
> > > +/* In tree-dump.c  */
> > > +extern void dump_node (const_tree, dump_flags_t, FILE *);
> > > +
> > > +/* In combine.c  */
> > > +extern void dump_combine_total_stats (FILE *);
> > > +/* In cfghooks.c  */
> > > +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> > > +
> > >  namespace gcc {
> > >
> > > +/* A class for managing all of the various dump files used by the
> > > +   optimization passes.  */
> > > +
> > >  class dump_manager
> > >  {
> > >  public:
> > > diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
> > > index 51783e5..f3a2119 100644
> > > --- a/gcc/ggc-page.c
> > > +++ b/gcc/ggc-page.c
> > > @@ -2177,6 +2177,8 @@ ggc_collect (void)
> > >    if (G.allocated < allocated_last_gc + min_expand &&
> > > !ggc_force_collect)
> > >      return;
> > >
> > > +  dumpfile_ensure_any_optinfo_are_flushed ();
> > > +
> > >    timevar_push (TV_GC);
> > >    if (!quiet_flag)
> > >      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated
> > > / 1024);
> > > diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
> > > new file mode 100644
> > > index 0000000..8144174
> > > --- /dev/null
> > > +++ b/gcc/optinfo-internal.h
> > > @@ -0,0 +1,148 @@
> > > +/* Implementation details of optinfo.
> > > +   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_OPTINFO_INTERNAL_H
> > > +#define GCC_OPTINFO_INTERNAL_H
> > > +
> > > +/* Multiple dispatch: there are various kinds of items within an
> > > optinfo,
> > > +   and various destinations to send optinfo to.
> > > +
> > > +   Handling this for now by exposing all of the item subclasses,
> > > +   and having the destinations handle them with "switch"
> > > statements.  */
> > > +
> > > +/* An enum for discriminating between optinfo_item subclasses.  */
> > > +
> > > +enum optinfo_item_kind
> > > +{
> > > +  OPTINFO_ITEM_KIND_TEXT,
> > > +  OPTINFO_ITEM_KIND_TREE,
> > > +  OPTINFO_ITEM_KIND_GIMPLE,
> > > +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> > > +};
> > > +
> > > +/* Abstract base class for items within an optinfo.  */
> > > +
> > > +class optinfo_item
> > > +{
> > > + public:
> > > +  virtual ~optinfo_item () {}
> > > +
> > > +  virtual enum optinfo_item_kind get_kind () const = 0;
> > > +};
> > > +
> > > +/* optinfo_item subclasses.  */
> > > +
> > > +/* Item within an optinfo: text, either owned by the item
> > > +   (for optinfo_printf), or borrowed (for string literals).  */
> > > +
> > > +class optinfo_item_text : public optinfo_item
> > > +{
> > > + public:
> > > +  optinfo_item_text (char *text, bool owned)
> > > +  : m_text (text), m_owned (owned)
> > > +  {}
> > > +  ~optinfo_item_text ()
> > > +  {
> > > +    if (m_owned)
> > > +      free (m_text);
> > > +  }
> > > +
> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > > +  {
> > > +    return OPTINFO_ITEM_KIND_TEXT;
> > > +  }
> > > +
> > > +  const char *get_text () const { return m_text; }
> > > +
> > > +  void trim_trailing_whitespace ();
> > > +
> > > + private:
> > > +  char *m_text;
> > > +  bool m_owned;
> > > +};
> > > +
> > > +/* Item within an optinfo: a tree, with dump flags.
> > > +   Note that this is not GTY-marked; see the description of
> > > +   class optinfo for a discussion of the interaction with the
> > > +   garbage-collector.  */
> > > +
> > > +class optinfo_item_tree : public optinfo_item
> > > +{
> > > + public:
> > > +  optinfo_item_tree (tree node, dump_flags_t dump_flags)
> > > +    : m_node (node), m_dump_flags (dump_flags)
> > > +  {}
> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > > +  {
> > > +    return OPTINFO_ITEM_KIND_TREE;
> > > +  }
> > > +
> > > +  tree get_node () const { return m_node; }
> > > +  dump_flags_t get_flags () const { return m_dump_flags; }
> > > +
> > > + private:
> > > +  tree m_node;
> > > +  dump_flags_t m_dump_flags;
> > > +};
> > > +
> > > +/* Item within an optinfo: a gimple statement.
> > > +   Note that this is not GTY-marked; see the description of
> > > +   class optinfo for a discussion of the interaction with the
> > > +   garbage-collector.  */
> > > +
> > > +class optinfo_item_gimple : public optinfo_item
> > > +{
> > > + public:
> > > +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
> > > +    : m_stmt (stmt), m_dump_flags (dump_flags) {}
> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > > +  {
> > > +    return OPTINFO_ITEM_KIND_GIMPLE;
> > > +  }
> > > +
> > > +  gimple *get_stmt () const { return m_stmt; }
> > > +  dump_flags_t get_flags () const { return m_dump_flags; }
> > > +
> > > + private:
> > > +  gimple *m_stmt;
> > > +  dump_flags_t m_dump_flags;
> > > +};
> > > +
> > > +/* Item within an optinfo: a symbol table node.
> > > +   Note that this is not GTY-marked; see the description of
> > > +   class optinfo for a discussion of the interaction with the
> > > +   garbage-collector.  */
> > > +
> > > +class optinfo_item_symtab_node : public optinfo_item
> > > +{
> > > + public:
> > > +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
> > > +  {
> > > +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
> > > +  }
> > > +
> > > +  symtab_node *get_node () const { return m_node; }
> > > +
> > > + private:
> > > +  symtab_node *m_node;
> > > +};
> > > +
> > > +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
> > > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > > new file mode 100644
> > > index 0000000..1da7d37
> > > --- /dev/null
> > > +++ b/gcc/optinfo.cc
> > > @@ -0,0 +1,251 @@
> > > +/* Optimization information.
> > > +   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/>.  */
> > > +
> > > +#include "config.h"
> > > +#include "system.h"
> > > +#include "coretypes.h"
> > > +
> > > +#include "backend.h"
> > > +#include "tree.h"
> > > +#include "gimple.h"
> > > +
> > > +#include "optinfo.h"
> > > +#include "optinfo-internal.h"
> > > +#include "dump-context.h"
> > > +#include "selftest.h"
> > > +
> > > +/* Remove any trailing whitespace characters from this text item.
> > > +   Primarily for use in stripping trailing newline characters when
> > > +   emitting remarks (since the diagnostic subsystem doesn't expect
> > > +   trailing newlines in messages).  */
> > > +
> > > +void
> > > +optinfo_item_text::trim_trailing_whitespace ()
> > > +{
> > > +  size_t len = strlen (m_text);
> > > +  if (len == 0)
> > > +    return;
> > > +
> > > +  size_t new_len = len;
> > > +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
> > > +    new_len--;
> > > +
> > > +  if (new_len == len)
> > > +    return;
> > > +
> > > +  if (m_owned)
> > > +    m_text[new_len] = '\0';
> > > +  else
> > > +    {
> > > +      m_text = xstrndup (m_text, new_len);
> > > +      m_owned = true;
> > > +    }
> > > +}
> > > +
> > > +/* Get a string from KIND.  */
> > > +
> > > +const char *
> > > +optinfo_kind_to_string (enum optinfo_kind kind)
> > > +{
> > > +  switch (kind)
> > > +    {
> > > +    default:
> > > +      gcc_unreachable ();
> > > +    case OPTINFO_KIND_SUCCESS:
> > > +      return "success";
> > > +    case OPTINFO_KIND_FAILURE:
> > > +      return "failure";
> > > +    case OPTINFO_KIND_NOTE:
> > > +      return "note";
> > > +    case OPTINFO_KIND_SCOPE:
> > > +      return "scope";
> > > +    }
> > > +}
> > > +
> > > +/* optinfo's dtor.  */
> > > +
> > > +optinfo::~optinfo ()
> > > +{
> > > +  /* Cleanup.  */
> > > +  unsigned i;
> > > +  optinfo_item *item;
> > > +  FOR_EACH_VEC_ELT (m_items, i, item)
> > > +    delete item;
> > > +}
> > > +
> > > +/* Emit the optinfo to all of the active destinations.  */
> > > +
> > > +void
> > > +optinfo::emit ()
> > > +{
> > > +  /* Eliminate any trailing whitespace.  */
> > > +  while (m_items.length () > 0)
> > > +    {
> > > +      optinfo_item *last_item = m_items[m_items.length () - 1];
> > > +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
> > > +       break;
> > > +
> > > +      optinfo_item_text *last_text = (optinfo_item_text
> > > *)last_item;
> > > +      last_text->trim_trailing_whitespace ();
> > > +
> > > +      if (strlen (last_text->get_text ()) > 0)
> > > +       break;
> > > +
> > > +      m_items.pop ();
> > > +      delete last_item;
> > > +    }
> > > +
> > > +  /* currently this is a no-op.  */
> > > +}
> > > +
> > > +/* Update the optinfo's kind based on DUMP_KIND.  */
> > > +
> > > +void
> > > +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> > > +{
> > > +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> > > +    m_kind = OPTINFO_KIND_SUCCESS;
> > > +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> > > +    m_kind = OPTINFO_KIND_FAILURE;
> > > +  else if (dump_kind & MSG_NOTE)
> > > +    m_kind = OPTINFO_KIND_NOTE;
> > > +}
> > > +
> > > +/* Append a string literal to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_string (const char *str)
> > > +{
> > > +  optinfo_item *item
> > > +    = new optinfo_item_text (const_cast <char *> (str), false);
> > > +  m_items.safe_push (item);
> > > +}
> > > +
> > > +/* Append printf-formatted text to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_printf (const char *format, ...)
> > > +{
> > > +  va_list ap;
> > > +  va_start (ap, format);
> > > +  add_printf_va (format, ap);
> > > +  va_end (ap);
> > > +}
> > > +
> > > +/* Append printf-formatted text to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_printf_va (const char *format, va_list ap)
> > > +{
> > > +  char *formatted_text = xvasprintf (format, ap);
> > > +  optinfo_item *item
> > > +    = new optinfo_item_text (formatted_text, true);
> > > +  m_items.safe_push (item);
> > > +}
> > > +
> > > +/* Append a gimple statement to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
> > > +{
> > > +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
> > > +}
> > > +
> > > +/* Append a tree node to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_tree (tree node, dump_flags_t dump_flags)
> > > +{
> > > +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
> > > +}
> > > +
> > > +/* Append a symbol table node to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_symtab_node (symtab_node *node)
> > > +{
> > > +  m_items.safe_push (new optinfo_item_symtab_node (node));
> > > +}
> > > +
> > > +/* Append the decimal represenation of a wide_int_ref to this
> > > +   optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> > > +{
> > > +  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> > > +  print_dec (wi, buf, sgn);
> > > +  optinfo_item *item
> > > +    = new optinfo_item_text (xstrdup (buf), true);
> > > +  m_items.safe_push (item);
> > > +}
> > > +
> > > +/* Should optinfo instances be created?
> > > +   All creation of optinfos should be guarded by this predicate.
> > > +   Return true if any optinfo destinations are active.  */
> > > +
> > > +bool optinfo_enabled_p ()
> > > +{
> > > +  /* Currently no destinations are implemented, just a hook for
> > > +     selftests.  */
> > > +  return dump_context::get ().forcibly_enable_optinfo_p ();
> > > +}
> > > +
> > > +/* Return true if any of the active optinfo destinations make use
> > > +   of inlining information.
> > > +   (if true, then the information is preserved).  */
> > > +
> > > +bool optinfo_wants_inlining_info_p ()
> > > +{
> > > +  return false;
> > > +}
> > > +
> > > +#if CHECKING_P
> > > +
> > > +namespace selftest {
> > > +
> > > +/* Verify that optinfo_item_text::trim_trailing_whitespace turns
> > > +   INPUT into EXPECTED.  */
> > > +
> > > +static void
> > > +test_trim_trailing_whitespace (const char *input, const char
> > > *expected)
> > > +{
> > > +  optinfo_item_text item (const_cast <char *> (input), false);
> > > +  item.trim_trailing_whitespace ();
> > > +  ASSERT_STREQ (item.get_text (), expected);
> > > +}
> > > +
> > > +/* Run all of the selftests within this file.  */
> > > +
> > > +void
> > > +optinfo_cc_tests ()
> > > +{
> > > +  /* Verify that optinfo_item_text::trim_trailing_whitespace
> > > works.  */
> > > +  test_trim_trailing_whitespace ("", "");
> > > +  test_trim_trailing_whitespace ("\n", "");
> > > +  test_trim_trailing_whitespace ("foo", "foo");
> > > +  test_trim_trailing_whitespace ("foo\n", "foo");
> > > +  test_trim_trailing_whitespace ("foo\n\n", "foo");
> > > +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
> > > +}
> > > +
> > > +} // namespace selftest
> > > +
> > > +#endif /* CHECKING_P */
> > > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > > new file mode 100644
> > > index 0000000..0d49823
> > > --- /dev/null
> > > +++ b/gcc/optinfo.h
> > > @@ -0,0 +1,175 @@
> > > +/* Optimization information.
> > > +   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_OPTINFO_H
> > > +#define GCC_OPTINFO_H
> > > +
> > > +/* An "optinfo" is a bundle of information describing part of an
> > > +   optimization, which can be emitted to zero or more of several
> > > +   destinations, such as:
> > > +
> > > +   * as a "remark" through the diagnostics subsystem
> > > +
> > > +   * saved to a file as an "optimization record"
> > > +
> > > +   Currently no such destinations are implemented.
> > > +
> > > +   They are generated in response to calls to the "dump_*" API in
> > > +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> > > +   into a pending optinfo instance, with a "dump_*_loc" starting a
> > > new
> > > +   optinfo instance.
> > > +
> > > +   The data sent to the dump calls are captured within the pending
> > > optinfo
> > > +   instance as a sequence of optinfo_items.  For example, given:
> > > +
> > > +      if (dump_enabled_p ())
> > > +        {
> > > +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > > +                           "not vectorized: live stmt not
> > > supported: ");
> > > +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > > stmt, 0);
> > > +        }
> > > +
> > > +   the "dump_printf_loc" call begins a new optinfo containing two
> > > items:
> > > +   (1) a text item containing "not vectorized: live stmt not
> > > supported: "
> > > +   (2) a gimple item for "stmt"
> > > +
> > > +   Dump destinations are thus able to access rich metadata about
> > > the
> > > +   items when the optinfo is emitted to them, rather than just
> > > having plain
> > > +   text.  For example, when saving the above optinfo to a file as
> > > an
> > > +   "optimization record", the record could capture the source
> > > location of
> > > +   "stmt" above, rather than just its textual form.
> > > +
> > > +   The currently pending optinfo is emitted and deleted:
> > > +   * each time a "dump_*_loc" call occurs (which starts the next
> > > optinfo), or
> > > +   * when the dump files are changed (at the end of a pass), or
> > > +   * when a garbage collection is about to happen.  This safety
> > > measure
> > > +     ensures that no GC happens during the lifetime of an optinfo
> > > instance,
> > > +     and thus that any optinfo_items within the optinfo instances
> > > can refer
> > > +     to GC-allocated objects without needing to be GTY-marked:
> > > they will never
> > > +     refer to collected garbage.  optinfo and optinfo_item are not
> > > GTY-marked
> > > +     as it would make no sense for them to be in PCH files.
> > > +
> > > +   Dumping to an optinfo instance is non-trivial (due to building
> > > optinfo_item
> > > +   instances), so all usage should be guarded by
> > > +
> > > +     if (optinfo_enabled_p ())
> > > +
> > > +   which is off by default.  */
> > > +
> > > +
> > > +/* Forward decls.  */
> > > +struct opt_pass;
> > > +class optinfo_item; /* optinfo-internal.h.  */
> > > +
> > > +/* Should optinfo instances be created?
> > > +   All creation of optinfos should be guarded by this predicate.
> > > +   Return true if any optinfo destinations are active.  */
> > > +
> > > +extern bool optinfo_enabled_p ();
> > > +
> > > +/* Return true if any of the active optinfo destinations make use
> > > +   of inlining information.
> > > +   (if true, then the information is preserved).  */
> > > +
> > > +extern bool optinfo_wants_inlining_info_p ();
> > > +
> > > +/* The various kinds of optinfo.  */
> > > +
> > > +enum optinfo_kind
> > > +{
> > > +  OPTINFO_KIND_SUCCESS,
> > > +  OPTINFO_KIND_FAILURE,
> > > +  OPTINFO_KIND_NOTE,
> > > +  OPTINFO_KIND_SCOPE
> > > +};
> > > +
> > > +extern const char *optinfo_kind_to_string (enum optinfo_kind
> > > kind);
> > > +
> > > +/* A bundle of information describing part of an optimization.  */
> > > +
> > > +class optinfo
> > > +{
> > > +  friend class dump_context;
> > > +
> > > + public:
> > > +  optinfo (const dump_location_t &loc,
> > > +          enum optinfo_kind kind,
> > > +          opt_pass *pass)
> > > +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> > > +  {}
> > > +  ~optinfo ();
> > > +
> > > +  const dump_user_location_t &
> > > +  get_user_location () const { return m_loc.get_user_location ();
> > > }
> > > +
> > > +  const dump_impl_location_t &
> > > +  get_impl_location () const { return m_loc.get_impl_location ();
> > > }
> > > +
> > > +  enum optinfo_kind get_kind () const { return m_kind; }
> > > +  opt_pass *get_pass () const { return m_pass; }
> > > +  unsigned int num_items () const { return m_items.length (); }
> > > +  const optinfo_item *get_item (unsigned int i) const { return
> > > m_items[i]; }
> > > +
> > > +  location_t get_location_t () const { return m_loc.get_location_t
> > > (); }
> > > +  profile_count get_count () const { return m_loc.get_count (); }
> > > +
> > > + private:
> > > +  void emit ();
> > > +
> > > +  /* Pre-canned ways of manipulating the optinfo, for use by
> > > friend class
> > > +     dump_context.  */
> > > +  void handle_dump_file_kind (dump_flags_t);
> > > +  void add_string (const char *str);
> > > +  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
> > > +  void add_printf_va (const char *format, va_list ap)
> > > ATTRIBUTE_PRINTF (2, 0);
> > > +  void add_stmt (gimple *stmt, dump_flags_t dump_flags);
> > > +  void add_tree (tree node, dump_flags_t dump_flags);
> > > +  void add_symtab_node (symtab_node *node);
> > > +  void add_dec (const wide_int_ref &wi, signop sgn);
> > > +
> > > +  template<unsigned int N, typename C>
> > > +  void add_poly_int (const poly_int<N, C> &value)
> > > +  {
> > > +    /* Compare with dump_dec (MSG_NOTE, ).  */
> > > +
> > > +    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > > +    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > > UNSIGNED;
> > > +
> > > +    if (value.is_constant ())
> > > +      add_dec (value.coeffs[0], sgn);
> > > +    else
> > > +      {
> > > +       add_string ("[");
> > > +       for (unsigned int i = 0; i < N; ++i)
> > > +         {
> > > +           add_dec (value.coeffs[i], sgn);
> > > +           add_string (i == N - 1 ? "]" : ",");
> > > +         }
> > > +      }
> > > +  }
> > > +
> > > + private:
> > > +  dump_location_t m_loc;
> > > +  enum optinfo_kind m_kind;
> > > +  opt_pass *m_pass;
> > > +  auto_vec <optinfo_item *> m_items;
> > > +};
> > > +
> > > +#endif /* #ifndef GCC_OPTINFO_H */
> > > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> > > index 7f4d6f3..989c50a 100644
> > > --- a/gcc/selftest-run-tests.c
> > > +++ b/gcc/selftest-run-tests.c
> > > @@ -72,6 +72,7 @@ selftest::run_tests ()
> > >    typed_splay_tree_c_tests ();
> > >    unique_ptr_tests_cc_tests ();
> > >    opt_proposer_c_tests ();
> > > +  optinfo_cc_tests ();
> > >
> > >    /* Mid-level data structures.  */
> > >    input_c_tests ();
> > > diff --git a/gcc/selftest.h b/gcc/selftest.h
> > > index 54fc488..48881c9 100644
> > > --- a/gcc/selftest.h
> > > +++ b/gcc/selftest.h
> > > @@ -228,6 +228,7 @@ extern void gimple_c_tests ();
> > >  extern void hash_map_tests_c_tests ();
> > >  extern void hash_set_tests_c_tests ();
> > >  extern void input_c_tests ();
> > > +extern void optinfo_cc_tests ();
> > >  extern void predict_c_tests ();
> > >  extern void pretty_print_c_tests ();
> > >  extern void read_rtl_function_c_tests ();
> > > --
> > > 1.8.5.3
> > >

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 1/2] v5: Add "optinfo" framework
  2018-07-09 13:05                             ` Richard Biener
@ 2018-07-11 10:53                               ` David Malcolm
  2018-07-11 10:53                                 ` [PATCH 2/2] Add "-fsave-optimization-record" David Malcolm
                                                   ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: David Malcolm @ 2018-07-11 10:53 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

Changes relative to v4:
* eliminated optinfo subclasses as discussed
* eliminated optinfo-internal.h, moving what remained into optinfo.h
* added support for dump_gimple_expr_loc and dump_gimple_expr
* more selftests

This patch implements a way to consolidate dump_* calls into
optinfo objects, as enabling work towards being able to write out
optimization records to a file (I'm focussing on that destination
in this patch kit, rather than diagnostic remarks).

The patch adds the support for building optinfo instances from dump_*
calls, but leaves implementing any *users* of them to followup patches.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o.
	* coretypes.h (class symtab_node): New forward decl.
	(struct cgraph_node): New forward decl.
	(class varpool_node): New forward decl.
	* dump-context.h: New file.
	* dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
	"tree-pass.h".
	(refresh_dumps_are_enabled): Use optinfo_enabled_p.
	(set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
	(set_alt_dump_file): Likewise.
	(dump_context::~dump_context): New dtor.
	(dump_gimple_stmt): Move implementation to...
	(dump_context::dump_gimple_stmt): ...this new member function.
	Add the stmt to any pending optinfo, creating one if need be.
	(dump_gimple_stmt_loc): Move implementation to...
	(dump_context::dump_gimple_stmt_loc): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_gimple_expr): Move implementation to...
	(dump_context::dump_gimple_expr): ...this new member function.
	Add the stmt to any pending optinfo, creating one if need be.
	(dump_gimple_expr_loc): Move implementation to...
	(dump_context::dump_gimple_expr_loc): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_generic_expr): Move implementation to...
	(dump_context::dump_generic_expr): ...this new member function.
	Add the tree to any pending optinfo, creating one if need be.
	(dump_generic_expr_loc): Move implementation to...
	(dump_context::dump_generic_expr_loc): ...this new member
	function.  Add the tree to any pending optinfo, creating one if
	need be.
	(dump_printf): Move implementation to...
	(dump_context::dump_printf_va): ...this new member function.  Add
	the text to any pending optinfo, creating one if need be.
	(dump_printf_loc): Move implementation to...
	(dump_context::dump_printf_loc_va): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_dec): Move implementation to...
	(dump_context::dump_dec): ...this new member function.  Add the
	value to any pending optinfo, creating one if need be.
	(dump_context::dump_symtab_node): New member function.
	(dump_context::get_scope_depth): New member function.
	(dump_context::begin_scope): New member function.
	(dump_context::end_scope): New member function.
	(dump_context::ensure_pending_optinfo): New member function.
	(dump_context::begin_next_optinfo): New member function.
	(dump_context::end_any_optinfo): New member function.
	(dump_context::s_current): New global.
	(dump_context::s_default): New global.
	(dump_scope_depth): Delete global.
	(dumpfile_ensure_any_optinfo_are_flushed): New function.
	(dump_symtab_node): New function.
	(get_dump_scope_depth): Reimplement in terms of dump_context.
	(dump_begin_scope): Likewise.
	(dump_end_scope): Likewise.
	(selftest::temp_dump_context::temp_dump_context): New ctor.
	(selftest::temp_dump_context::~temp_dump_context): New dtor.
	(selftest::verify_item): New function.
	(ASSERT_IS_TEXT): New macro.
	(ASSERT_IS_TREE): New macro.
	(ASSERT_IS_GIMPLE): New macro.
	(selftest::test_capture_of_dump_calls): New test.
	(selftest::dumpfile_c_tests): Call it.
	* dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
	(dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc)
	(dump_gimple_stmt, dump_dec): Gather these related decls and add a
	descriptive comment.
	(dump_function, print_combine_total_stats, enable_rtl_dump_file)
	(dump_node, dump_bb): Move these unrelated decls.
	(class dump_manager): Add leading comment.
	* optinfo.cc: New file.
	* optinfo.h: New file.
---
 gcc/Makefile.in    |   1 +
 gcc/coretypes.h    |   7 +
 gcc/dump-context.h | 138 +++++++++++++
 gcc/dumpfile.c     | 597 +++++++++++++++++++++++++++++++++++++++++++++++++----
 gcc/dumpfile.h     |  84 +++++---
 gcc/optinfo.cc     | 236 +++++++++++++++++++++
 gcc/optinfo.h      | 203 ++++++++++++++++++
 7 files changed, 1200 insertions(+), 66 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2a05a66..dd1dfc1 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1427,6 +1427,7 @@ OBJS = \
 	optabs-libfuncs.o \
 	optabs-query.o \
 	optabs-tree.o \
+	optinfo.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..ed0e825 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@ struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
new file mode 100644
index 0000000..a191e3a
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,138 @@
+/* Support code for handling the various dump_* calls in dumpfile.h
+   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_DUMP_CONTEXT_H
+#define GCC_DUMP_CONTEXT_H 1
+
+/* A class for handling the various dump_* calls.
+
+   In particular, this class has responsibility for consolidating
+   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
+   calls), and emitting them.
+
+   Putting this in a class (rather than as global state) allows
+   for selftesting of this code.  */
+
+class dump_context
+{
+  friend class temp_dump_context;
+ public:
+  static dump_context &get () { return *s_current; }
+
+  ~dump_context ();
+
+  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
+			     const dump_location_t &loc,
+			     dump_flags_t extra_dump_flags,
+			     gimple *gs, int spc);
+
+  void dump_gimple_expr (dump_flags_t dump_kind,
+			 dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_expr_loc (dump_flags_t dump_kind,
+			    const dump_location_t &loc,
+			    dump_flags_t extra_dump_flags,
+			    gimple *gs,
+			    int spc);
+
+  void dump_generic_expr (dump_flags_t dump_kind,
+			  dump_flags_t extra_dump_flags,
+			  tree t);
+
+  void dump_generic_expr_loc (dump_flags_t dump_kind,
+			      const dump_location_t &loc,
+			      dump_flags_t extra_dump_flags,
+			      tree t);
+
+  void dump_printf_va (dump_flags_t dump_kind, const char *format,
+		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
+
+  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
+			   const char *format, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0);
+
+  template<unsigned int N, typename C>
+  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
+
+  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
+
+  /* Managing nested scopes.  */
+  unsigned int get_scope_depth () const;
+  void begin_scope (const char *name, const dump_location_t &loc);
+  void end_scope ();
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool forcibly_enable_optinfo_p () const
+  {
+    return m_forcibly_enable_optinfo;
+  }
+
+  void end_any_optinfo ();
+
+ private:
+  optinfo &ensure_pending_optinfo ();
+  optinfo &begin_next_optinfo (const dump_location_t &loc);
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool m_forcibly_enable_optinfo;
+
+  /* The current nesting depth of dump scopes, for showing nesting
+     via indentation).  */
+  unsigned int m_scope_depth;
+
+  /* The optinfo currently being accumulated since the last dump_*_loc call,
+     if any.  */
+  optinfo *m_pending;
+
+  /* The currently active dump_context, for use by the dump_* API calls.  */
+  static dump_context *s_current;
+
+  /* The default active context.  */
+  static dump_context s_default;
+};
+
+#if CHECKING_P
+
+/* An RAII-style class for use in selftests for temporarily using a different
+   dump_context.  */
+
+class temp_dump_context
+{
+ public:
+  temp_dump_context (bool forcibly_enable_optinfo);
+  ~temp_dump_context ();
+
+  /* Support for selftests.  */
+  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+
+ private:
+  dump_context m_context;
+  dump_context *m_saved;
+  bool m_saved_flag_remarks;
+};
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 7ed1796..38f9539 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -33,6 +33,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h" /* for dump_user_location_t ctor.  */
 #include "rtl.h" /* for dump_user_location_t ctor.  */
 #include "selftest.h"
+#include "optinfo.h"
+#include "dump-context.h"
+#include "cgraph.h"
+#include "tree-pass.h" /* for "current_pass".  */
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -64,7 +68,7 @@ bool dumps_are_enabled = false;
 static void
 refresh_dumps_are_enabled ()
 {
-  dumps_are_enabled = (dump_file || alt_dump_file);
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
 }
 
 /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
@@ -73,6 +77,7 @@ refresh_dumps_are_enabled ()
 void
 set_dump_file (FILE *new_dump_file)
 {
+  dumpfile_ensure_any_optinfo_are_flushed ();
   dump_file = new_dump_file;
   refresh_dumps_are_enabled ();
 }
@@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file)
 static void
 set_alt_dump_file (FILE *new_alt_dump_file)
 {
+  dumpfile_ensure_any_optinfo_are_flushed ();
   alt_dump_file = new_alt_dump_file;
   refresh_dumps_are_enabled ();
 }
@@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Implementation of dump_context member functions.  */
+
+/* dump_context's dtor.  */
+
+dump_context::~dump_context ()
+{
+  delete m_pending;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
 void
-dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
+				dump_flags_t extra_dump_flags,
+				gimple *gs, int spc)
 {
   if (dump_file && (dump_kind & pflags))
     print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs, int spc)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
+    }
 }
 
 /* Dump gimple statement GS with SPC indentation spaces and
@@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
    Do not terminate with a newline or semicolon.  */
 
 void
-dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+dump_context::dump_gimple_expr (dump_flags_t dump_kind,
+				dump_flags_t extra_dump_flags,
+				gimple *gs, int spc)
 {
   if (dump_file && (dump_kind & pflags))
     print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
+    }
 }
 
 /* Similar to dump_gimple_expr, except additionally print source location.  */
 
 void
-dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs,
+				    int spc)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       dump_loc (dump_kind, alt_dump_file, srcloc);
       print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
+    }
 }
 
 
@@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
    DUMP_KIND is enabled.  */
 
 void
-dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		   tree t)
+dump_context::dump_generic_expr (dump_flags_t dump_kind,
+				 dump_flags_t extra_dump_flags,
+				 tree t)
 {
   if (dump_file && (dump_kind & pflags))
       print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
 
   if (alt_dump_file && (dump_kind & alt_flags))
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, dump_flags | extra_dump_flags);
+    }
 }
 
 
@@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
    location.  */
 
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
+				     const dump_location_t &loc,
+				     dump_flags_t extra_dump_flags,
+				     tree t)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       dump_loc (dump_kind, alt_dump_file, srcloc);
       print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
     }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_tree (t, dump_flags | extra_dump_flags);
+    }
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
+			      va_list ap)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
 
 void
-dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		 const char *format, ...)
+dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
+				  const dump_location_t &loc,
+				  const char *format, va_list ap)
 {
   location_t srcloc = loc.get_location_t ();
+
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
       dump_loc (dump_kind, dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (dump_file, format, aq);
+      va_end (aq);
     }
 
   if (alt_dump_file && (dump_kind & alt_flags))
     {
-      va_list ap;
       dump_loc (dump_kind, alt_dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+      va_list aq;
+      va_copy (aq, ap);
+      info.add_printf_va (format, aq);
+      va_end (aq);
     }
 }
 
@@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 
 template<unsigned int N, typename C>
 void
-dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
   STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
   signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
@@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
+    }
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
+
+void
+dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", node->dump_name ());
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", node->dump_name ());
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
+    }
+}
+
+/* Get the current dump scope-nesting depth.
+   For use by -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+dump_context::get_scope_depth () const
+{
+  return m_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Emit a "scope" optinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+void
+dump_context::begin_scope (const char *name, const dump_location_t &loc)
+{
+  /* Specialcase, to avoid going through dump_printf_loc,
+     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
+
+  if (dump_file)
+    {
+      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      fprintf (dump_file, "=== %s ===\n", name);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      end_any_optinfo ();
+      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
+      info.add_printf ("=== %s ===", name);
+      info.emit ();
+    }
+
+  m_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_context::end_scope ()
+{
+  end_any_optinfo ();
+  m_scope_depth--;
+}
+
+/* Return the optinfo currently being accumulated, creating one if
+   necessary.  */
+
+optinfo &
+dump_context::ensure_pending_optinfo ()
+{
+  if (!m_pending)
+    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
+  return *m_pending;
+}
+
+/* Start a new optinfo and return it, ending any optinfo that was already
+   accumulated.  */
+
+optinfo &
+dump_context::begin_next_optinfo (const dump_location_t &loc)
+{
+  end_any_optinfo ();
+  gcc_assert (m_pending == NULL);
+  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
+  return *m_pending;
+}
+
+/* End any optinfo that has been accumulated within this context; emitting
+   it to any destinations as appropriate - though none have currently been
+   implemented.  */
+
+void
+dump_context::end_any_optinfo ()
+{
+  if (m_pending)
+    m_pending->emit ();
+  delete m_pending;
+  m_pending = NULL;
+}
+
+/* The current singleton dump_context, and its default.  */
+
+dump_context *dump_context::s_current = &dump_context::s_default;
+dump_context dump_context::s_default;
+
+/* Implementation of dump_* API calls, calling into dump_context
+   member functions.  */
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_stmt, except additionally print source location.  */
+
+void
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
+   Do not terminate with a newline or semicolon.  */
+
+void
+dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_expr, except additionally print source location.  */
+
+void
+dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
+   DUMP_KIND is enabled.  */
+
+void
+dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		   tree t)
+{
+  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
+}
+
+/* Similar to dump_generic_expr, except additionally print the source
+   location.  */
+
+void
+dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		       dump_flags_t extra_dump_flags, tree t)
+{
+  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
+					      t);
+}
+
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
+void
+dump_printf (dump_flags_t dump_kind, const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  va_end (ap);
+}
+
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
+
+void
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		 const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  va_end (ap);
+}
+
+/* Output VALUE in decimal to appropriate dump streams.  */
+
+template<unsigned int N, typename C>
+void
+dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+{
+  dump_context::get ().dump_dec (dump_kind, value);
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const poly_wide_int &value)
     print_hex (value, alt_dump_file);
 }
 
-/* The current dump scope-nesting depth.  */
+/* Emit and delete the currently pending optinfo, if there is one,
+   without the caller needing to know about class dump_context.  */
+
+void
+dumpfile_ensure_any_optinfo_are_flushed ()
+{
+  dump_context::get().end_any_optinfo ();
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
 
-static int dump_scope_depth;
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  dump_context::get ().dump_symtab_node (dump_kind, node);
+}
 
 /* Get the current dump scope-nesting depth.
-   For use by dump_*_loc (for showing nesting via indentation).  */
+   For use by -fopt-info (for showing nesting via indentation).  */
 
 unsigned int
 get_dump_scope_depth ()
 {
-  return dump_scope_depth;
+  return dump_context::get ().get_scope_depth ();
 }
 
 /* Push a nested dump scope.
    Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
    destination, if any.
+   Emit a "scope" opinfo if optinfos are enabled.
    Increment the scope depth.  */
 
 void
 dump_begin_scope (const char *name, const dump_location_t &loc)
 {
-  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
-  dump_scope_depth++;
+  dump_context::get ().begin_scope (name, loc);
 }
 
 /* Pop a nested dump scope.  */
@@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc)
 void
 dump_end_scope ()
 {
-  dump_scope_depth--;
+  dump_context::get ().end_scope ();
 }
 
 /* Start a dump for PHASE. Store user-supplied dump flags in
@@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void)
 
 #if CHECKING_P
 
+/* temp_dump_context's ctor.  Temporarily override the dump_context
+   (to forcibly enable optinfo-generation).  */
+
+temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
+: m_context (),
+  m_saved (&dump_context ().get ())
+{
+  dump_context::s_current = &m_context;
+  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
+}
+
+/* temp_dump_context's dtor.  Restore the saved dump_context.  */
+
+temp_dump_context::~temp_dump_context ()
+{
+  dump_context::s_current = m_saved;
+}
+
 namespace selftest {
 
 /* Verify that the dump_location_t constructors capture the source location
@@ -1274,12 +1613,188 @@ test_impl_location ()
 #endif
 }
 
+/* Verify that ITEM has the expected values.  */
+
+static void
+verify_item (const location &loc,
+	     const optinfo_item *item,
+	     enum optinfo_item_kind expected_kind,
+	     location_t expected_location,
+	     const char *expected_text)
+{
+  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
+  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
+  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
+		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls (const line_table_case &case_)
+{
+  /* Generate a location_t for testing.  */
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
+  linemap_line_start (line_table, 5, 100);
+  linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+  location_t where = linemap_position_for_column (line_table, 10);
+
+  dump_location_t loc = dump_location_t::from_location_t (where);
+
+  /* Test of dump_printf.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 1);
+    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
+  }
+
+  /* Tree, via dump_generic_expr.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_location_t (), where);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
+    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
+  }
+
+  /* Tree, via dump_generic_expr_loc.  */
+  {
+    temp_dump_context tmp (true);
+    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_location_t (), where);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 1);
+    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
+  }
+
+  /* Gimple.  */
+  {
+    greturn *stmt = gimple_build_return (NULL);
+    gimple_set_location (stmt, where);
+
+    /* dump_gimple_stmt_loc.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
+
+      optinfo *info = tmp.get_pending_optinfo ();
+      ASSERT_TRUE (info != NULL);
+      ASSERT_EQ (info->num_items (), 1);
+      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+    }
+
+    /* dump_gimple_stmt.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
+
+      optinfo *info = tmp.get_pending_optinfo ();
+      ASSERT_TRUE (info != NULL);
+      ASSERT_EQ (info->num_items (), 1);
+      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+    }
+
+    /* dump_gimple_expr_loc.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
+
+      optinfo *info = tmp.get_pending_optinfo ();
+      ASSERT_TRUE (info != NULL);
+      ASSERT_EQ (info->num_items (), 1);
+      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+    }
+
+    /* dump_gimple_expr.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
+
+      optinfo *info = tmp.get_pending_optinfo ();
+      ASSERT_TRUE (info != NULL);
+      ASSERT_EQ (info->num_items (), 1);
+      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+    }
+  }
+
+  /* poly_int.  */
+  {
+    temp_dump_context tmp (true);
+    dump_dec (MSG_NOTE, poly_int64 (42));
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    ASSERT_IS_TEXT (info->get_item (0), "42");
+  }
+
+  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
+     above.  */
+  {
+    /* MSG_OPTIMIZED_LOCATIONS.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_SUCCESS);
+    }
+
+    /* MSG_MISSED_OPTIMIZATION.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_FAILURE);
+    }
+  }
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 dumpfile_c_tests ()
 {
   test_impl_location ();
+  for_each_line_table_case (test_capture_of_dump_calls);
 }
 
 } // namespace selftest
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 4a71ef7..3096a89 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
+
+/* Global variables used to communicate with passes.  */
+extern FILE *dump_file;
+extern dump_flags_t dump_flags;
+extern const char *dump_file_name;
+
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
+
+/* Return true if any of the dumps is enabled, false otherwise. */
+static inline bool
+dump_enabled_p (void)
+{
+  return dumps_are_enabled;
+}
+
+/* The following API calls (which *don't* take a "FILE *")
+   write the output to zero or more locations:
+   (a) the active dump_file, if any
+   (b) the -fopt-info destination, if any
+   (c) to the "optinfo" destinations, if any:
+
+   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
+                                   |
+                                   +--> (b) alt_dump_file
+                                   |
+                                   `--> (c) optinfo
+                                            `---> optinfo destinations
+
+   For optinfos, the dump_*_loc mark the beginning of an optinfo
+   instance: all subsequent dump_* calls are consolidated into
+   that optinfo, until the next dump_*_loc call (or a change in
+   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
+
+   A group of dump_* calls should be guarded by:
+
+     if (dump_enabled_p ())
+
+   to minimize the work done for the common case where dumps
+   are disabled.  */
+
 extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
 extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
 			     const char *, ...) ATTRIBUTE_PRINTF_3;
@@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
 extern void dump_gimple_expr_loc (dump_flags_t, const dump_location_t &,
 				  dump_flags_t, gimple *, int);
 extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
 
 template<unsigned int N, typename C>
 void dump_dec (dump_flags_t, const poly_int<N, C> &);
 extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
 extern void dump_hex (dump_flags_t, const poly_wide_int &);
 
-/* In tree-dump.c  */
-extern void dump_node (const_tree, dump_flags_t, FILE *);
-
-/* In combine.c  */
-extern void dump_combine_total_stats (FILE *);
-/* In cfghooks.c  */
-extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
-
-/* Global variables used to communicate with passes.  */
-extern FILE *dump_file;
-extern dump_flags_t dump_flags;
-extern const char *dump_file_name;
-
-extern bool dumps_are_enabled;
-
-extern void set_dump_file (FILE *new_dump_file);
-
-/* Return true if any of the dumps is enabled, false otherwise. */
-static inline bool
-dump_enabled_p (void)
-{
-  return dumps_are_enabled;
-}
+extern void dumpfile_ensure_any_optinfo_are_flushed ();
 
 /* Managing nested scopes, so that dumps can express the call chain
    leading to a dump message.  */
@@ -505,8 +524,23 @@ class auto_dump_scope
 #define AUTO_DUMP_SCOPE(NAME, LOC) \
   auto_dump_scope scope (NAME, LOC)
 
+extern void dump_function (int phase, tree fn);
+extern void print_combine_total_stats (void);
+extern bool enable_rtl_dump_file (void);
+
+/* In tree-dump.c  */
+extern void dump_node (const_tree, dump_flags_t, FILE *);
+
+/* In combine.c  */
+extern void dump_combine_total_stats (FILE *);
+/* In cfghooks.c  */
+extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
+
 namespace gcc {
 
+/* A class for managing all of the various dump files used by the
+   optimization passes.  */
+
 class dump_manager
 {
 public:
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..6f224bc
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,236 @@
+/* Optimization information.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "dump-context.h"
+#include "pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+#include "selftest.h"
+
+/* optinfo_item's ctor.  */
+
+optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
+			    char *text, bool owned)
+: m_kind (kind), m_location (location), m_text (text), m_owned (owned)
+{
+}
+
+/* optinfo_item's dtor.  */
+
+optinfo_item::~optinfo_item ()
+{
+  if (m_owned)
+    free (m_text);
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit ()
+{
+  /* currently this is a no-op.  */
+}
+
+/* Update the optinfo's kind based on DUMP_KIND.  */
+
+void
+optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    m_kind = OPTINFO_KIND_NOTE;
+}
+
+/* Append a string literal to this optinfo.  */
+
+void
+optinfo::add_string (const char *str)
+{
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			const_cast <char *> (str), false);
+  m_items.safe_push (item);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  add_printf_va (format, ap);
+  va_end (ap);
+}
+
+/* Append printf-formatted text to this optinfo.  */
+
+void
+optinfo::add_printf_va (const char *format, va_list ap)
+{
+  char *formatted_text = xvasprintf (format, ap);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			formatted_text, true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo, equivalent to
+   print_gimple_stmt.  */
+
+void
+optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags)
+{
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
+  pp_newline (&pp);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
+			xstrdup (pp_formatted_text (&pp)), true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo, equivalent to
+   print_gimple_expr.  */
+
+void
+optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags)
+{
+  dump_flags |= TDF_RHS_ONLY;
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
+			xstrdup (pp_formatted_text (&pp)), true);
+  m_items.safe_push (item);
+}
+
+/* Append a tree node to this optinfo, equivalent to print_generic_expr.  */
+
+void
+optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_translate_identifiers (&pp) = false;
+  dump_generic_node (&pp, node, 0, dump_flags, false);
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (EXPR_HAS_LOCATION (node))
+    loc = EXPR_LOCATION (node);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc,
+			xstrdup (pp_formatted_text (&pp)), true);
+  m_items.safe_push (item);
+}
+
+/* Append a symbol table node to this optinfo.  */
+
+void
+optinfo::add_symtab_node (symtab_node *node)
+{
+  location_t loc = DECL_SOURCE_LOCATION (node->decl);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
+			xstrdup (node->dump_name ()), true);
+  m_items.safe_push (item);
+}
+
+/* Append the decimal represenation of a wide_int_ref to this
+   optinfo.  */
+
+void
+optinfo::add_dec (const wide_int_ref &wi, signop sgn)
+{
+  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+  print_dec (wi, buf, sgn);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			xstrdup (buf), true);
+  m_items.safe_push (item);
+}
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+bool optinfo_enabled_p ()
+{
+  /* Currently no destinations are implemented, just a hook for
+     selftests.  */
+  return dump_context::get ().forcibly_enable_optinfo_p ();
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return false;
+}
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..5bdb9eb
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,203 @@
+/* Optimization information.
+   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_OPTINFO_H
+#define GCC_OPTINFO_H
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations, such as:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file as an "optimization record"
+
+   Currently no such destinations are implemented.
+
+   They are generated in response to calls to the "dump_*" API in
+   dumpfile.h; repeated calls to the "dump_*" API are consolidated
+   into a pending optinfo instance, with a "dump_*_loc" starting a new
+   optinfo instance.
+
+   The data sent to the dump calls are captured within the pending optinfo
+   instance as a sequence of optinfo_items.  For example, given:
+
+      if (dump_enabled_p ())
+        {
+          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                           "not vectorized: live stmt not supported: ");
+          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+        }
+
+   the "dump_printf_loc" call begins a new optinfo containing two items:
+   (1) a text item containing "not vectorized: live stmt not supported: "
+   (2) a gimple item for "stmt"
+
+   Dump destinations are thus able to access rich metadata about the
+   items when the optinfo is emitted to them, rather than just having plain
+   text.  For example, when saving the above optinfo to a file as an
+   "optimization record", the record could capture the source location of
+   "stmt" above, rather than just its textual form.
+
+   The currently pending optinfo is emitted and deleted:
+   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
+   * when the dump files are changed (at the end of a pass)
+
+   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
+   instances), so all usage should be guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.  */
+
+
+/* Forward decls.  */
+struct opt_pass;
+class optinfo_item; /* optinfo-internal.h.  */
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.  */
+
+class optinfo
+{
+  friend class dump_context;
+
+ public:
+  optinfo (const dump_location_t &loc,
+	   enum optinfo_kind kind,
+	   opt_pass *pass)
+  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
+  {}
+  ~optinfo ();
+
+  const dump_user_location_t &
+  get_user_location () const { return m_loc.get_user_location (); }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_loc.get_impl_location (); }
+
+  enum optinfo_kind get_kind () const { return m_kind; }
+  opt_pass *get_pass () const { return m_pass; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const { return m_loc.get_location_t (); }
+  profile_count get_count () const { return m_loc.get_count (); }
+
+ private:
+  void emit ();
+
+  /* Pre-canned ways of manipulating the optinfo, for use by friend class
+     dump_context.  */
+  void handle_dump_file_kind (dump_flags_t);
+  void add_string (const char *str);
+  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
+  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags);
+  void add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags);
+  void add_tree (tree node, dump_flags_t dump_flags);
+  void add_symtab_node (symtab_node *node);
+  void add_dec (const wide_int_ref &wi, signop sgn);
+
+  template<unsigned int N, typename C>
+  void add_poly_int (const poly_int<N, C> &value)
+  {
+    /* Compare with dump_dec (MSG_NOTE, ).  */
+
+    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
+    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
+
+    if (value.is_constant ())
+      add_dec (value.coeffs[0], sgn);
+    else
+      {
+	add_string ("[");
+	for (unsigned int i = 0; i < N; ++i)
+	  {
+	    add_dec (value.coeffs[i], sgn);
+	    add_string (i == N - 1 ? "]" : ",");
+	  }
+      }
+  }
+
+ private:
+  dump_location_t m_loc;
+  enum optinfo_kind m_kind;
+  opt_pass *m_pass;
+  auto_vec <optinfo_item *> m_items;
+};
+
+/* An enum for discriminating between different kinds of optinfo_item.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* An item within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  optinfo_item (enum optinfo_item_kind kind, location_t location,
+		char *text, bool owned);
+  ~optinfo_item ();
+
+  enum optinfo_item_kind get_kind () const { return m_kind; }
+  location_t get_location () const { return m_location; }
+  const char *get_text () const { return m_text; }
+
+ private:
+  /* Metadata (e.g. for optimization records).  */
+  enum optinfo_item_kind m_kind;
+  location_t m_location;
+
+  /* The textual form of the item.  */
+  char *m_text;
+  bool m_owned;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 2/2] Add "-fsave-optimization-record"
  2018-07-11 10:53                               ` [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
@ 2018-07-11 10:53                                 ` David Malcolm
  2018-07-19 12:39                                   ` Richard Biener
  2018-07-18 20:20                                 ` [PING] Re: [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
  2018-07-19 12:18                                 ` Richard Biener
  2 siblings, 1 reply; 80+ messages in thread
From: David Malcolm @ 2018-07-11 10:53 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, David Malcolm

This patch implements a -fsave-optimization-record option, which
leads to a JSON file being written out, recording the dump_* calls
made (via the optinfo infrastructure in the previous patch).

The patch includes a minimal version of the JSON patch I posted last
year, with just enough support needed for optimization records (I
removed all of the parser code, leaving just the code for building
in-memory JSON trees and writing them to a pretty_printer).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
	(CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
	* common.opt (fsave-optimization-record): New option.
	* coretypes.h (struct kv_pair): Move here from dumpfile.c.
	* doc/invoke.texi (-fsave-optimization-record): New option.
	* dumpfile.c: Include "optinfo-emit-json.h".
	(struct kv_pair): Move to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_context::end_scope): Call
	optimization_records_maybe_pop_dump_scope.
	* dumpfile.h (optgroup_options): New decl.
	* json.cc: New file.
	* json.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo.cc: Include "optinfo-emit-json.h".
	(optinfo::emit): Call optimization_records_maybe_record_optinfo.
	(optinfo_enabled_p): Check optimization_records_enabled_p.
	(optinfo_wants_inlining_info_p): Likewise.
	* optinfo.h: Update comment.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* selftest-run-tests.c (selftest::run_tests): Call json_cc_tests
	and optinfo_emit_json_cc_tests.
	* selftest.h (selftest::json_cc_tests): New decl.
	(selftest::optinfo_emit_json_cc_tests): New decl.
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_finish.
	(do_compile): Call optimization_records_start.
	* tree-ssa-live.c: Include optinfo.h.
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
---
 gcc/Makefile.in          |   3 +
 gcc/common.opt           |   4 +
 gcc/coretypes.h          |   8 +
 gcc/doc/invoke.texi      |   8 +-
 gcc/dumpfile.c           |  15 +-
 gcc/dumpfile.h           |   3 +
 gcc/json.cc              | 293 ++++++++++++++++++++++++
 gcc/json.h               | 166 ++++++++++++++
 gcc/optinfo-emit-json.cc | 568 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h  |  36 +++
 gcc/optinfo.cc           |  11 +-
 gcc/optinfo.h            |   4 -
 gcc/profile-count.c      |  28 +++
 gcc/profile-count.h      |   5 +
 gcc/selftest-run-tests.c |   2 +
 gcc/selftest.h           |   2 +
 gcc/toplev.c             |   5 +
 gcc/tree-ssa-live.c      |   4 +-
 18 files changed, 1143 insertions(+), 22 deletions(-)
 create mode 100644 gcc/json.cc
 create mode 100644 gcc/json.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index dd1dfc1..b871640 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1387,6 +1387,7 @@ OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
@@ -1428,6 +1429,7 @@ OBJS = \
 	optabs-query.o \
 	optabs-tree.o \
 	optinfo.o \
+	optinfo-emit-json.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
@@ -2251,6 +2253,7 @@ s-bversion: BASE-VER
 	$(STAMP) s-bversion
 
 CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
 
 pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
 		    $(srcdir)/gen-pass-instances.awk
diff --git a/gcc/common.opt b/gcc/common.opt
index 5a50bc27..a13c709 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1950,6 +1950,10 @@ fopt-info-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fopt-info[-<type>=filename]	Dump compiler optimization details.
 
+fsave-optimization-record
+Common Report Var(flag_save_optimization_record) Optimization
+Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index ed0e825..2fd20e4 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -332,6 +332,14 @@ namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 56cd122..c239c53 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
 -freorder-blocks-algorithm=@var{algorithm} @gol
 -freorder-blocks-and-partition  -freorder-functions @gol
 -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
--frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
+-frounding-math  -fsave-optimization-record @gol
+-fsched2-use-superblocks  -fsched-pressure @gol
 -fsched-spec-load  -fsched-spec-load-dangerous @gol
 -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
 -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
@@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer control of this setting
 using C99's @code{FENV_ACCESS} pragma.  This command-line option
 will be used to specify the default state for @code{FENV_ACCESS}.
 
+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 38f9539..331b7fb 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dump-context.h"
 #include "cgraph.h"
 #include "tree-pass.h" /* for "current_pass".  */
+#include "optinfo-emit-json.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -118,14 +119,6 @@ static struct dump_file_info dump_files[TDI_end] =
   DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
 };
 
-/* Define a name->number mapping for a dump flag value.  */
-template <typename ValueType>
-struct kv_pair
-{
-  const char *const name;	/* the name of the value */
-  const ValueType value;	/* the value of the name */
-};
-
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
 static const kv_pair<dump_flags_t> dump_options[] =
@@ -176,7 +169,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 };
 
 /* Flags used for -fopt-info groups.  */
-static const kv_pair<optgroup_flags_t> optgroup_options[] =
+const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -794,6 +787,7 @@ dump_context::end_scope ()
 {
   end_any_optinfo ();
   m_scope_depth--;
+  optimization_records_maybe_pop_dump_scope ();
 }
 
 /* Return the optinfo currently being accumulated, creating one if
@@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc)
 }
 
 /* End any optinfo that has been accumulated within this context; emitting
-   it to any destinations as appropriate - though none have currently been
-   implemented.  */
+   it to any destinations as appropriate, such as optimization records.  */
 
 void
 dump_context::end_any_optinfo ()
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 3096a89..e4823f8 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,6 +420,7 @@ extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
+extern const kv_pair<optgroup_flags_t> optgroup_options[];
 
 /* Global variables used to communicate with passes.  */
 extern FILE *dump_file;
@@ -442,6 +443,7 @@ dump_enabled_p (void)
    (a) the active dump_file, if any
    (b) the -fopt-info destination, if any
    (c) to the "optinfo" destinations, if any:
+       (c.1) as optimization records
 
    dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                    |
@@ -449,6 +451,7 @@ dump_enabled_p (void)
                                    |
                                    `--> (c) optinfo
                                             `---> optinfo destinations
+                                                  (c.1) optimization records
 
    For optinfos, the dump_*_loc mark the beginning of an optinfo
    instance: all subsequent dump_* calls are consolidated into
diff --git a/gcc/json.cc b/gcc/json.cc
new file mode 100644
index 0000000..3c2aa77
--- /dev/null
+++ b/gcc/json.cc
@@ -0,0 +1,293 @@
+/* JSON trees
+   Copyright (C) 2017-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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "math.h"
+#include "selftest.h"
+
+using namespace json;
+
+/* class json::value.  */
+
+/* Dump this json::value tree to OUTF.
+   No formatting is done.  There are no guarantees about the order
+   in which the key/value pairs of json::objects are printed.  */
+
+void
+value::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+/* class json::object, a subclass of json::value, representing
+   an unordered collection of key/value pairs.  */
+
+/* json:object's dtor.  */
+
+object::~object ()
+{
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      delete ((*it).second);
+    }
+}
+
+/* Implementation of json::value::print for json::object.  */
+
+void
+object::print (pretty_printer *pp) const
+{
+  /* Note that the order is not guaranteed.  */
+  pp_character (pp, '{');
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      if (it != m_map.begin ())
+	pp_string (pp, ", ");
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
+      value->print (pp);
+    }
+  pp_character (pp, '}');
+}
+
+/* Set the json::value * for KEY, taking ownership of VALUE
+   (and taking a copy of KEY if necessary).  */
+
+void
+object::set (const char *key, value *v)
+{
+  value **ptr = m_map.get (key);
+  if (ptr)
+    {
+      /* If the key is already present, delete the existing value
+	 and overwrite it.  */
+      delete *ptr;
+      *ptr = v;
+    }
+  else
+    /* If the key wasn't already present, take a copy of the key,
+       and store the value.  */
+    m_map.put (xstrdup (key), v);
+}
+
+/* class json::array, a subclass of json::value, representing
+   an ordered collection of values.  */
+
+/* json::array's dtor.  */
+
+array::~array ()
+{
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    delete v;
+}
+
+/* Implementation of json::value::print for json::array.  */
+
+void
+array::print (pretty_printer *pp) const
+{
+  pp_character (pp, '[');
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    {
+      if (i)
+	pp_string (pp, ", ");
+      v->print (pp);
+    }
+  pp_character (pp, ']');
+}
+
+/* class json::number, a subclass of json::value, wrapping a double.  */
+
+/* Implementation of json::value::print for json::number.  */
+
+void
+number::print (pretty_printer *pp) const
+{
+  char tmp[1024];
+  snprintf (tmp, sizeof (tmp), "%g", m_value);
+  pp_string (pp, tmp);
+}
+
+/* class json::string, a subclass of json::value.  */
+
+void
+string::print (pretty_printer *pp) const
+{
+  pp_character (pp, '"');
+  for (const char *ptr = m_utf8; *ptr; ptr++)
+    {
+      char ch = *ptr;
+      switch (ch)
+	{
+	case '"':
+	  pp_string (pp, "\\\"");
+	  break;
+	case '\\':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\b':
+	  pp_string (pp, "\\b");
+	  break;
+	case '\f':
+	  pp_string (pp, "\\f");
+	  break;
+	case '\n':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\r':
+	  pp_string (pp, "\\r");
+	  break;
+	case '\t':
+	  pp_string (pp, "\\t");
+	  break;
+
+	default:
+	  pp_character (pp, ch);
+	}
+    }
+  pp_character (pp, '"');
+}
+
+/* class json::literal, a subclass of json::value.  */
+
+/* Implementation of json::value::print for json::literal.  */
+
+void
+literal::print (pretty_printer *pp) const
+{
+  switch (m_kind)
+    {
+    case JSON_TRUE:
+      pp_string (pp, "true");
+      break;
+    case JSON_FALSE:
+      pp_string (pp, "false");
+      break;
+    case JSON_NULL:
+      pp_string (pp, "null");
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+\f
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Verify that JV->print () prints EXPECTED_JSON.  */
+
+static void
+assert_print_eq (const json::value &jv, const char *expected_json)
+{
+  pretty_printer pp;
+  jv.print (&pp);
+  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
+}
+
+/* Verify that JSON objects are written correctly.  We can't test more than
+   one key/value pair, as we don't impose a guaranteed ordering.  */
+
+static void
+test_writing_objects ()
+{
+  object obj;
+  obj.set ("foo", new json::string ("bar"));
+  assert_print_eq (obj, "{\"foo\": \"bar\"}");
+}
+
+/* Verify that JSON arrays are written correctly.  */
+
+static void
+test_writing_arrays ()
+{
+  array arr;
+  assert_print_eq (arr, "[]");
+
+  arr.append (new json::string ("foo"));
+  assert_print_eq (arr, "[\"foo\"]");
+
+  arr.append (new json::string ("bar"));
+  assert_print_eq (arr, "[\"foo\", \"bar\"]");
+}
+
+/* Verify that JSON numbers are written correctly.  */
+
+static void
+test_writing_numbers ()
+{
+  assert_print_eq (number (0), "0");
+  assert_print_eq (number (42), "42");
+  assert_print_eq (number (-100), "-100");
+}
+
+/* Verify that JSON strings are written correctly.  */
+
+static void
+test_writing_strings ()
+{
+  string foo ("foo");
+  assert_print_eq (foo, "\"foo\"");
+
+  string contains_quotes ("before \"quoted\" after");
+  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\"");
+}
+
+/* Verify that JSON strings are written correctly.  */
+
+static void
+test_writing_literals ()
+{
+  assert_print_eq (literal (JSON_TRUE), "true");
+  assert_print_eq (literal (JSON_FALSE), "false");
+  assert_print_eq (literal (JSON_NULL), "null");
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_cc_tests ()
+{
+  test_writing_objects ();
+  test_writing_arrays ();
+  test_writing_numbers ();
+  test_writing_strings ();
+  test_writing_literals ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json.h b/gcc/json.h
new file mode 100644
index 0000000..5c3274c
--- /dev/null
+++ b/gcc/json.h
@@ -0,0 +1,166 @@
+/* JSON trees
+   Copyright (C) 2017-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_JSON_H
+#define GCC_JSON_H
+
+/* Implementation of JSON, a lightweight data-interchange format.
+
+   See http://www.json.org/
+   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+   and https://tools.ietf.org/html/rfc7159
+
+   Supports creating a DOM-like tree of json::value *, and then dumping
+   json::value * to text.  */
+
+namespace json
+{
+
+/* Forward decls of json::value and its subclasses (using indentation
+   to denote inheritance.  */
+
+class value;
+  class object;
+  class array;
+  class number;
+  class string;
+  class literal;
+
+/* An enum for discriminating the subclasses of json::value.  */
+
+enum kind
+{
+  /* class json::object.  */
+  JSON_OBJECT,
+
+  /* class json::array.  */
+  JSON_ARRAY,
+
+  /* class json::number.  */
+  JSON_NUMBER,
+
+  /* class json::string.  */
+  JSON_STRING,
+
+  /* class json::literal uses these three values to identify the
+     particular literal.  */
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+
+/* Base class of JSON value.  */
+
+class value
+{
+ public:
+  virtual ~value () {}
+  virtual enum kind get_kind () const = 0;
+  virtual void print (pretty_printer *pp) const = 0;
+
+  void dump (FILE *) const;
+};
+
+/* Subclass of value for objects: an unordered collection of
+   key/value pairs.  */
+
+class object : public value
+{
+ public:
+  ~object ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  void set (const char *key, value *v);
+
+ private:
+  typedef hash_map <char *, value *,
+    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
+  map_t m_map;
+};
+
+/* Subclass of value for arrays.  */
+
+class array : public value
+{
+ public:
+  ~array ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  void append (value *v) { m_elements.safe_push (v); }
+
+ private:
+  auto_vec<value *> m_elements;
+};
+
+/* Subclass of value for numbers.  */
+
+class number : public value
+{
+ public:
+  number (double value) : m_value (value) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  double get () const { return m_value; }
+
+ private:
+  double m_value;
+};
+
+/* Subclass of value for strings.  */
+
+class string : public value
+{
+ public:
+  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
+  ~string () { free (m_utf8); }
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  const char *get_string () const { return m_utf8; }
+
+ private:
+  char *m_utf8;
+};
+
+/* Subclass of value for the three JSON literals "true", "false",
+   and "null".  */
+
+class literal : public value
+{
+ public:
+  literal (enum kind kind) : m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+ private:
+  enum kind m_kind;
+};
+
+} // namespace json
+
+#endif  /* GCC_JSON_H  */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..bf1172a
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,568 @@
+/* Emit optimization information as JSON files.
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+#include "version.h"
+#include "context.h"
+#include "pass_manager.h"
+#include "selftest.h"
+#include "dump-context.h"
+
+/* A class for writing out optimization records in JSON format.  */
+
+class optrecord_json_writer
+{
+public:
+  optrecord_json_writer ();
+  ~optrecord_json_writer ();
+  void write () const;
+  void add_record (const optinfo *optinfo);
+  void pop_scope ();
+
+  void add_record (json::object *obj);
+  json::object *impl_location_to_json (dump_impl_location_t loc);
+  json::object *location_to_json (location_t loc);
+  json::object *profile_count_to_json (profile_count count);
+  json::string *get_id_value_for_pass (opt_pass *pass);
+  json::object *pass_to_json (opt_pass *pass);
+  json::value *inlining_chain_to_json (location_t loc);
+  json::object *optinfo_to_json (const optinfo *optinfo);
+  void add_pass_list (json::array *arr, opt_pass *pass);
+
+private:
+  /* The root value for the JSON file.
+     Currently the JSON values are stored in memory, and flushed when the
+     compiler exits.  It would probably be better to simply write out
+     the JSON as we go.  */
+  json::array *m_root_tuple;
+
+  /* The currently open scopes, for expressing nested optimization records.  */
+  vec<json::array *> m_scopes;
+};
+
+/* optrecord_json_writer's ctor.  Populate the top-level parts of the
+   in-memory JSON representation.  */
+
+optrecord_json_writer::optrecord_json_writer ()
+  : m_root_tuple (NULL), m_scopes ()
+{
+  m_root_tuple = new json::array ();
+
+  /* Populate with metadata; compare with toplev.c: print_version.  */
+  json::object *metadata = new json::object ();
+  m_root_tuple->append (metadata);
+  metadata->set ("format", new json::string ("1"));
+  json::object *generator = new json::object ();
+  metadata->set ("generator", generator);
+  generator->set ("name", new json::string (lang_hooks.name));
+  generator->set ("pkgversion", new json::string (pkgversion_string));
+  generator->set ("version", new json::string (version_string));
+  /* TARGET_NAME is passed in by the Makefile.  */
+  generator->set ("target", new json::string (TARGET_NAME));
+
+  /* TODO: capture command-line?
+     see gen_producer_string in dwarf2out.c (currently static).  */
+
+  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
+
+  json::array *passes = new json::array ();
+  m_root_tuple->append (passes);
+
+  /* Call add_pass_list for all of the pass lists.  */
+  {
+#define DEF_PASS_LIST(LIST) \
+    add_pass_list (passes, g->get_passes ()->LIST);
+    GCC_PASS_LISTS
+#undef DEF_PASS_LIST
+  }
+
+  json::array *records = new json::array ();
+  m_root_tuple->append (records);
+
+  m_scopes.safe_push (records);
+}
+
+/* optrecord_json_writer's ctor.
+   Delete the in-memory JSON representation.  */
+
+optrecord_json_writer::~optrecord_json_writer ()
+{
+  delete m_root_tuple;
+}
+
+/* Choose an appropriate filename, and write the saved records to it.  */
+
+void
+optrecord_json_writer::write () const
+{
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      m_root_tuple->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+}
+
+/* Add a record for OPTINFO to the queue of records to be written.  */
+
+void
+optrecord_json_writer::add_record (const optinfo *optinfo)
+{
+  json::object *obj = optinfo_to_json (optinfo);
+
+  add_record (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      m_scopes.safe_push (children);
+    }
+}
+
+/* Private methods of optrecord_json_writer.  */
+
+/* Add record OBJ to the the innermost scope.  */
+
+void
+optrecord_json_writer::add_record (json::object *obj)
+{
+  /* Add to innermost scope.  */
+  gcc_assert (m_scopes.length () > 0);
+  m_scopes[m_scopes.length () - 1]->append (obj);
+}
+
+/* Pop the innermost scope.  */
+
+void
+optrecord_json_writer::pop_scope ()
+{
+  m_scopes.pop ();
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  if (loc.m_function)
+    obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+json::object *
+optrecord_json_writer::profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Get a string for use when referring to PASS in the saved optimization
+   records.  */
+
+json::string *
+optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
+{
+  pretty_printer pp;
+  /* this is host-dependent, but will be consistent for a given host.  */
+  pp_pointer (&pp, static_cast<void *> (pass));
+  return new json::string (pp_formatted_text (&pp));
+}
+
+/* Create a JSON object representing PASS.  */
+
+json::object *
+optrecord_json_writer::pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("id", get_id_value_for_pass (pass));
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+json::value *
+optrecord_json_writer::inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  const char *printable_name
+	    = lang_hooks.decl_printable_name (fndecl, 2);
+	  obj->set ("fndecl", new json::string (printable_name));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* Create a JSON object representing OPTINFO.  */
+
+json::object *
+optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
+{
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    message->append (new json::string (item->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("expr", new json::string (item->get_text ()));
+
+	    /* Capture any location for the node.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+
+	    message->append (json_item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("stmt", new json::string (item->get_text ()));
+
+	    /* Capture any location for the stmt.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+
+	    message->append (json_item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("symtab_node", new json::string (item->get_text ()));
+
+	    /* Capture any location for the node.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+	    message->append (json_item);
+	  }
+	  break;
+	}
+   }
+
+  if (optinfo->get_pass ())
+    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
+
+  profile_count count = optinfo->get_count ();
+  if (count.initialized_p ())
+    obj->set ("count", profile_count_to_json (count));
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  location_t loc = optinfo->get_location_t ();
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  return obj;
+}
+
+/* Add a json description of PASS and its siblings to ARR, recursing into
+   child passes (adding their descriptions within a "children" array).  */
+
+void
+optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
+{
+  do
+    {
+      json::object *pass_obj = pass_to_json (pass);
+      arr->append (pass_obj);
+      if (pass->sub)
+	{
+	  json::array *sub = new json::array ();
+	  pass_obj->set ("children", sub);
+	  add_pass_list (sub, pass->sub);
+	}
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* File-level interface to rest of compiler (to avoid exposing
+   class optrecord_json_writer outside of this file).  */
+
+static optrecord_json_writer *the_json_writer;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  the_json_writer = new optrecord_json_writer ();
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go, before cleaning
+   up.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->write ();
+
+  delete the_json_writer;
+  the_json_writer = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return the_json_writer != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->add_record (optinfo);
+}
+
+/* Handling for the end of a dump scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_dump_scope ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->pop_scope ();
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that we can build a JSON optimization record from dump_*
+   calls.  */
+
+static void
+test_building_json_from_dump_calls ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+  optinfo *info = tmp.get_pending_optinfo ();
+  ASSERT_TRUE (info != NULL);
+  ASSERT_EQ (info->num_items (), 2);
+
+  optrecord_json_writer writer;
+  json::object *json_obj = writer.optinfo_to_json (info);
+  ASSERT_TRUE (json_obj != NULL);
+
+  /* Verify that the json is sane.  */
+  pretty_printer pp;
+  json_obj->print (&pp);
+  const char *json_str = pp_formatted_text (&pp);
+  ASSERT_STR_CONTAINS (json_str, "impl_location");
+  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
+  ASSERT_STR_CONTAINS (json_str,
+		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
+  delete json_obj;
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_json_cc_tests ()
+{
+  test_building_json_from_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..3628d56
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,36 @@
+/* Emit optimization information as JSON files.
+   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_OPTINFO_EMIT_JSON_H
+#define GCC_OPTINFO_EMIT_JSON_H
+
+class optinfo;
+struct opt_pass;
+
+extern void optimization_records_start ();
+extern void optimization_records_finish ();
+
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_dump_scope ();
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index 6f224bc..93de9d9 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 
 #include "optinfo.h"
+#include "optinfo-emit-json.h"
 #include "dump-context.h"
 #include "pretty-print.h"
 #include "gimple-pretty-print.h"
@@ -85,7 +86,8 @@ optinfo::~optinfo ()
 void
 optinfo::emit ()
 {
-  /* currently this is a no-op.  */
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
 }
 
 /* Update the optinfo's kind based on DUMP_KIND.  */
@@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn)
 
 bool optinfo_enabled_p ()
 {
-  /* Currently no destinations are implemented, just a hook for
-     selftests.  */
-  return dump_context::get ().forcibly_enable_optinfo_p ();
+  return (dump_context::get ().forcibly_enable_optinfo_p ()
+	  || optimization_records_enabled_p ());
 }
 
 /* Return true if any of the active optinfo destinations make use
@@ -232,5 +233,5 @@ bool optinfo_enabled_p ()
 
 bool optinfo_wants_inlining_info_p ()
 {
-  return false;
+  return optimization_records_enabled_p ();
 }
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index 5bdb9eb..5f09022 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -25,12 +25,8 @@ along with GCC; see the file COPYING3.  If not see
    optimization, which can be emitted to zero or more of several
    destinations, such as:
 
-   * as a "remark" through the diagnostics subsystem
-
    * saved to a file as an "optimization record"
 
-   Currently no such destinations are implemented.
-
    They are generated in response to calls to the "dump_*" API in
    dumpfile.h; repeated calls to the "dump_*" API are consolidated
    into a pending optinfo instance, with a "dump_*_loc" starting a new
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@ enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@ public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 7f4d6f3..5adb033 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,8 @@ selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
+  json_cc_tests ();
+  optinfo_emit_json_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 54fc488..ede7732 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -228,6 +228,8 @@ extern void gimple_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void json_cc_tests ();
+extern void optinfo_emit_json_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d108096..a047390 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -487,6 +488,8 @@ compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2048,6 +2051,8 @@ do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 7447f7a..2623d9b 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PING] Re: [PATCH 1/2] v5: Add "optinfo" framework
  2018-07-11 10:53                               ` [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
  2018-07-11 10:53                                 ` [PATCH 2/2] Add "-fsave-optimization-record" David Malcolm
@ 2018-07-18 20:20                                 ` David Malcolm
  2018-07-19 12:18                                 ` Richard Biener
  2 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-07-18 20:20 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

Ping, re these patches:

"[PATCH 1/2] v5: Add "optinfo" framework"
  https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00535.html
 
"[PATCH 2/2] Add "-fsave-optimization-record""
  https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00536.html

Thanks
Dave

On Wed, 2018-07-11 at 07:37 -0400, David Malcolm wrote:
> Changes relative to v4:
> * eliminated optinfo subclasses as discussed
> * eliminated optinfo-internal.h, moving what remained into optinfo.h
> * added support for dump_gimple_expr_loc and dump_gimple_expr
> * more selftests
> 
> This patch implements a way to consolidate dump_* calls into
> optinfo objects, as enabling work towards being able to write out
> optimization records to a file (I'm focussing on that destination
> in this patch kit, rather than diagnostic remarks).
> 
> The patch adds the support for building optinfo instances from dump_*
> calls, but leaves implementing any *users* of them to followup
> patches.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> OK for trunk?
> 
> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add optinfo.o.
> 	* coretypes.h (class symtab_node): New forward decl.
> 	(struct cgraph_node): New forward decl.
> 	(class varpool_node): New forward decl.
> 	* dump-context.h: New file.
> 	* dumpfile.c: Include "optinfo.h", "dump-context.h",
> "cgraph.h",
> 	"tree-pass.h".
> 	(refresh_dumps_are_enabled): Use optinfo_enabled_p.
> 	(set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
> 	(set_alt_dump_file): Likewise.
> 	(dump_context::~dump_context): New dtor.
> 	(dump_gimple_stmt): Move implementation to...
> 	(dump_context::dump_gimple_stmt): ...this new member function.
> 	Add the stmt to any pending optinfo, creating one if need be.
> 	(dump_gimple_stmt_loc): Move implementation to...
> 	(dump_context::dump_gimple_stmt_loc): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_gimple_expr): Move implementation to...
> 	(dump_context::dump_gimple_expr): ...this new member function.
> 	Add the stmt to any pending optinfo, creating one if need be.
> 	(dump_gimple_expr_loc): Move implementation to...
> 	(dump_context::dump_gimple_expr_loc): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_generic_expr): Move implementation to...
> 	(dump_context::dump_generic_expr): ...this new member function.
> 	Add the tree to any pending optinfo, creating one if need be.
> 	(dump_generic_expr_loc): Move implementation to...
> 	(dump_context::dump_generic_expr_loc): ...this new member
> 	function.  Add the tree to any pending optinfo, creating one if
> 	need be.
> 	(dump_printf): Move implementation to...
> 	(dump_context::dump_printf_va): ...this new member
> function.  Add
> 	the text to any pending optinfo, creating one if need be.
> 	(dump_printf_loc): Move implementation to...
> 	(dump_context::dump_printf_loc_va): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_dec): Move implementation to...
> 	(dump_context::dump_dec): ...this new member function.  Add the
> 	value to any pending optinfo, creating one if need be.
> 	(dump_context::dump_symtab_node): New member function.
> 	(dump_context::get_scope_depth): New member function.
> 	(dump_context::begin_scope): New member function.
> 	(dump_context::end_scope): New member function.
> 	(dump_context::ensure_pending_optinfo): New member function.
> 	(dump_context::begin_next_optinfo): New member function.
> 	(dump_context::end_any_optinfo): New member function.
> 	(dump_context::s_current): New global.
> 	(dump_context::s_default): New global.
> 	(dump_scope_depth): Delete global.
> 	(dumpfile_ensure_any_optinfo_are_flushed): New function.
> 	(dump_symtab_node): New function.
> 	(get_dump_scope_depth): Reimplement in terms of dump_context.
> 	(dump_begin_scope): Likewise.
> 	(dump_end_scope): Likewise.
> 	(selftest::temp_dump_context::temp_dump_context): New ctor.
> 	(selftest::temp_dump_context::~temp_dump_context): New dtor.
> 	(selftest::verify_item): New function.
> 	(ASSERT_IS_TEXT): New macro.
> 	(ASSERT_IS_TREE): New macro.
> 	(ASSERT_IS_GIMPLE): New macro.
> 	(selftest::test_capture_of_dump_calls): New test.
> 	(selftest::dumpfile_c_tests): Call it.
> 	* dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
> 	(dump_generic_expr_loc, dump_generic_expr,
> dump_gimple_stmt_loc)
> 	(dump_gimple_stmt, dump_dec): Gather these related decls and
> add a
> 	descriptive comment.
> 	(dump_function, print_combine_total_stats,
> enable_rtl_dump_file)
> 	(dump_node, dump_bb): Move these unrelated decls.
> 	(class dump_manager): Add leading comment.
> 	* optinfo.cc: New file.
> 	* optinfo.h: New file.
> ---
>  gcc/Makefile.in    |   1 +
>  gcc/coretypes.h    |   7 +
>  gcc/dump-context.h | 138 +++++++++++++
>  gcc/dumpfile.c     | 597
> +++++++++++++++++++++++++++++++++++++++++++++++++----
>  gcc/dumpfile.h     |  84 +++++---
>  gcc/optinfo.cc     | 236 +++++++++++++++++++++
>  gcc/optinfo.h      | 203 ++++++++++++++++++
>  7 files changed, 1200 insertions(+), 66 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 2a05a66..dd1dfc1 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1427,6 +1427,7 @@ OBJS = \
>  	optabs-libfuncs.o \
>  	optabs-query.o \
>  	optabs-tree.o \
> +	optinfo.o \
>  	options-save.o \
>  	opts-global.o \
>  	passes.o \
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..ed0e825 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>  
> +/* Subclasses of symtab_node, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..a191e3a
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,138 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   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_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by
> "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +			 gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +			     const dump_location_t &loc,
> +			     dump_flags_t extra_dump_flags,
> +			     gimple *gs, int spc);
> +
> +  void dump_gimple_expr (dump_flags_t dump_kind,
> +			 dump_flags_t extra_dump_flags,
> +			 gimple *gs, int spc);
> +
> +  void dump_gimple_expr_loc (dump_flags_t dump_kind,
> +			    const dump_location_t &loc,
> +			    dump_flags_t extra_dump_flags,
> +			    gimple *gs,
> +			    int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +			  dump_flags_t extra_dump_flags,
> +			  tree t);
> +
> +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> +			      const dump_location_t &loc,
> +			      dump_flags_t extra_dump_flags,
> +			      tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
> +
> +  void dump_printf_loc_va (dump_flags_t dump_kind, const
> dump_location_t &loc,
> +			   const char *format, va_list ap)
> +    ATTRIBUTE_PRINTF (4, 0);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is
> true.  */
> +  bool forcibly_enable_optinfo_p () const
> +  {
> +    return m_forcibly_enable_optinfo;
> +  }
> +
> +  void end_any_optinfo ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is
> true.  */
> +  bool m_forcibly_enable_optinfo;
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last
> dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API
> calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII-style class for use in selftests for temporarily using a
> different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool forcibly_enable_optinfo);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return
> m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 7ed1796..38f9539 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -33,6 +33,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h" /* for dump_user_location_t ctor.  */
>  #include "rtl.h" /* for dump_user_location_t ctor.  */
>  #include "selftest.h"
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "cgraph.h"
> +#include "tree-pass.h" /* for "current_pass".  */
>  
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -64,7 +68,7 @@ bool dumps_are_enabled = false;
>  static void
>  refresh_dumps_are_enabled ()
>  {
> -  dumps_are_enabled = (dump_file || alt_dump_file);
> +  dumps_are_enabled = (dump_file || alt_dump_file ||
> optinfo_enabled_p ());
>  }
>  
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> "dumps_are_enabled"
> @@ -73,6 +77,7 @@ refresh_dumps_are_enabled ()
>  void
>  set_dump_file (FILE *new_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file)
>  static void
>  set_alt_dump_file (FILE *new_alt_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    alt_dump_file = new_alt_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile,
> source_location loc)
>      }
>  }
>  
> +/* Implementation of dump_context member functions.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>  
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		  gimple *gs, int spc)
> +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> +				dump_flags_t extra_dump_flags,
> +				gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_stmt (dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  /* Similar to dump_gimple_stmt, except additionally print source
> location.  */
>  
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +				    const dump_location_t &loc,
> +				    dump_flags_t extra_dump_flags,
> +				    gimple *gs, int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  /* Dump gimple statement GS with SPC indentation spaces and
> @@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>     Do not terminate with a newline or semicolon.  */
>  
>  void
> -dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		  gimple *gs, int spc)
> +dump_context::dump_gimple_expr (dump_flags_t dump_kind,
> +				dump_flags_t extra_dump_flags,
> +				gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_expr (dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_expr (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  /* Similar to dump_gimple_expr, except additionally print source
> location.  */
>  
>  void
> -dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
> +				    const dump_location_t &loc,
> +				    dump_flags_t extra_dump_flags,
> +				    gimple *gs,
> +				    int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_expr (alt_dump_file, gs, spc, dump_flags |
> extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  
> @@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>     DUMP_KIND is enabled.  */
>  
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		   tree t)
> +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> +				 dump_flags_t extra_dump_flags,
> +				 tree t)
>  {
>    if (dump_file && (dump_kind & pflags))
>        print_generic_expr (dump_file, t, dump_flags |
> extra_dump_flags);
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>        print_generic_expr (alt_dump_file, t, dump_flags |
> extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  
> @@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind,
> dump_flags_t extra_dump_flags,
>     location.  */
>  
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		       dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> +				     const dump_location_t &loc,
> +				     dump_flags_t extra_dump_flags,
> +				     tree t)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_generic_expr (alt_dump_file, t, dump_flags |
> extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, dump_flags | extra_dump_flags);
> +    }
>  }
>  
>  /* Output a formatted message using FORMAT on appropriate dump
> streams.  */
>  
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> *format,
> +			      va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>  
> -/* Similar to dump_printf, except source location is also
> printed.  */
> +/* Similar to dump_printf, except source location is also printed,
> and
> +   dump location captured.  */
>  
>  void
> -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -		 const char *format, ...)
> +dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
> +				  const dump_location_t &loc,
> +				  const char *format, va_list ap)
>  {
>    location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>  
> @@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const
> dump_location_t &loc,
>  
>  template<unsigned int N, typename C>
>  void
> -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> @@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const
> poly_int<N, C> &value)
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
> +    }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +void
> +dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node
> *node)
> +{
> +  if (dump_file && (dump_kind & pflags))
> +    fprintf (dump_file, "%s", node->dump_name ());
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    fprintf (alt_dump_file, "%s", node->dump_name ());
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
> +    }
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-
> info
> +   destination, if any.
> +   Emit a "scope" optinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +void
> +dump_context::begin_scope (const char *name, const dump_location_t
> &loc)
> +{
> +  /* Specialcase, to avoid going through dump_printf_loc,
> +     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
> +
> +  if (dump_file)
> +    {
> +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> +      fprintf (dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t
> ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was
> already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context;
> emitting
> +   it to any destinations as appropriate - though none have
> currently been
> +   implemented.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   member functions.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		  gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind,
> extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source
> location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc,
> extra_dump_flags,
> +					     gs, spc);
> +}
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
> +   Do not terminate with a newline or semicolon.  */
> +
> +void
> +dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		  gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr (dump_kind,
> extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_expr, except additionally print source
> location.  */
> +
> +void
> +dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +{
> +  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc,
> extra_dump_flags,
> +					     gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		   tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind,
> extra_dump_flags, t);
> +}
> +
> +/* Similar to dump_generic_expr, except additionally print the
> source
> +   location.  */
> +
> +void
> +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		       dump_flags_t extra_dump_flags, tree t)
> +{
> +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc,
> extra_dump_flags,
> +					      t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump
> streams.  */
> +
> +void
> +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed,
> and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +		 const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> ap);
> +  va_end (ap);
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>  
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const
> poly_wide_int &value)
>      print_hex (value, alt_dump_file);
>  }
>  
> -/* The current dump scope-nesting depth.  */
> +/* Emit and delete the currently pending optinfo, if there is one,
> +   without the caller needing to know about class dump_context.  */
> +
> +void
> +dumpfile_ensure_any_optinfo_are_flushed ()
> +{
> +  dump_context::get().end_any_optinfo ();
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
>  
> -static int dump_scope_depth;
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
>  
>  /* Get the current dump scope-nesting depth.
> -   For use by dump_*_loc (for showing nesting via indentation).  */
> +   For use by -fopt-info (for showing nesting via indentation).  */
>  
>  unsigned int
>  get_dump_scope_depth ()
>  {
> -  return dump_scope_depth;
> +  return dump_context::get ().get_scope_depth ();
>  }
>  
>  /* Push a nested dump scope.
>     Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-
> info
>     destination, if any.
> +   Emit a "scope" opinfo if optinfos are enabled.
>     Increment the scope depth.  */
>  
>  void
>  dump_begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> -  dump_scope_depth++;
> +  dump_context::get ().begin_scope (name, loc);
>  }
>  
>  /* Pop a nested dump scope.  */
> @@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const
> dump_location_t &loc)
>  void
>  dump_end_scope ()
>  {
> -  dump_scope_depth--;
> +  dump_context::get ().end_scope ();
>  }
>  
>  /* Start a dump for PHASE. Store user-supplied dump flags in
> @@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void)
>  
>  #if CHECKING_P
>  
> +/* temp_dump_context's ctor.  Temporarily override the dump_context
> +   (to forcibly enable optinfo-generation).  */
> +
> +temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
> +: m_context (),
> +  m_saved (&dump_context ().get ())
> +{
> +  dump_context::s_current = &m_context;
> +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +}
> +
>  namespace selftest {
>  
>  /* Verify that the dump_location_t constructors capture the source
> location
> @@ -1274,12 +1613,188 @@ test_impl_location ()
>  #endif
>  }
>  
> +/* Verify that ITEM has the expected values.  */
> +
> +static void
> +verify_item (const location &loc,
> +	     const optinfo_item *item,
> +	     enum optinfo_item_kind expected_kind,
> +	     location_t expected_location,
> +	     const char *expected_text)
> +{
> +  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
> +  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
> +  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
> +}
> +
> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> +
> +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT,
> \
> +		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		
>     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a tree item, with the expected values.  */
> +
> +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE,
> \
> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a gimple item, with the expected values.  */
> +
> +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM),
> OPTINFO_ITEM_KIND_GIMPLE, \
> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
> +  SELFTEST_END_STMT
> +
> +/* Verify that calls to the dump_* API are captured and consolidated
> into
> +   optimization records. */
> +
> +static void
> +test_capture_of_dump_calls (const line_table_case &case_)
> +{
> +  /* Generate a location_t for testing.  */
> +  line_table_test ltt (case_);
> +  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
> +  linemap_line_start (line_table, 5, 100);
> +  linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> +  location_t where = linemap_position_for_column (line_table, 10);
> +
> +  dump_location_t loc = dump_location_t::from_location_t (where);
> +
> +  /* Test of dump_printf.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> +  }
> +
> +  /* Tree, via dump_generic_expr.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_location_t (), where);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 2);
> +    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> +    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> +  }
> +
> +  /* Tree, via dump_generic_expr_loc.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> integer_one_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_location_t (), where);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> +  }
> +
> +  /* Gimple.  */
> +  {
> +    greturn *stmt = gimple_build_return (NULL);
> +    gimple_set_location (stmt, where);
> +
> +    /* dump_gimple_stmt_loc.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +    }
> +
> +    /* dump_gimple_stmt.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +    }
> +
> +    /* dump_gimple_expr_loc.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +    }
> +
> +    /* dump_gimple_expr.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +    }
> +  }
> +
> +  /* poly_int.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_dec (MSG_NOTE, poly_int64 (42));
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TEXT (info->get_item (0), "42");
> +  }
> +
> +  /* Verify that MSG_* affects optinfo->get_kind (); we tested
> MSG_NOTE
> +     above.  */
> +  {
> +    /* MSG_OPTIMIZED_LOCATIONS.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +		 OPTINFO_KIND_SUCCESS);
> +    }
> +
> +    /* MSG_MISSED_OPTIMIZATION.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +		 OPTINFO_KIND_FAILURE);
> +    }
> +  }
> +}
> +
>  /* Run all of the selftests within this file.  */
>  
>  void
>  dumpfile_c_tests ()
>  {
>    test_impl_location ();
> +  for_each_line_table_case (test_capture_of_dump_calls);
>  }
>  
>  } // namespace selftest
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 4a71ef7..3096a89 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> +
> +/* Global variables used to communicate with passes.  */
> +extern FILE *dump_file;
> +extern dump_flags_t dump_flags;
> +extern const char *dump_file_name;
> +
> +extern bool dumps_are_enabled;
> +
> +extern void set_dump_file (FILE *new_dump_file);
> +
> +/* Return true if any of the dumps is enabled, false otherwise. */
> +static inline bool
> +dump_enabled_p (void)
> +{
> +  return dumps_are_enabled;
> +}
> +
> +/* The following API calls (which *don't* take a "FILE *")
> +   write the output to zero or more locations:
> +   (a) the active dump_file, if any
> +   (b) the -fopt-info destination, if any
> +   (c) to the "optinfo" destinations, if any:
> +
> +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> +                                   |
> +                                   +--> (b) alt_dump_file
> +                                   |
> +                                   `--> (c) optinfo
> +                                            `---> optinfo
> destinations
> +
> +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> +   instance: all subsequent dump_* calls are consolidated into
> +   that optinfo, until the next dump_*_loc call (or a change in
> +   dump scope, or a call to
> dumpfile_ensure_any_optinfo_are_flushed).
> +
> +   A group of dump_* calls should be guarded by:
> +
> +     if (dump_enabled_p ())
> +
> +   to minimize the work done for the common case where dumps
> +   are disabled.  */
> +
>  extern void dump_printf (dump_flags_t, const char *, ...)
> ATTRIBUTE_PRINTF_2;
>  extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
>  			     const char *, ...) ATTRIBUTE_PRINTF_3;
> @@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t,
> dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr_loc (dump_flags_t, const
> dump_location_t &,
>  				  dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *,
> int);
> -extern void print_combine_total_stats (void);
> -extern bool enable_rtl_dump_file (void);
> +extern void dump_symtab_node (dump_flags_t, symtab_node *);
>  
>  template<unsigned int N, typename C>
>  void dump_dec (dump_flags_t, const poly_int<N, C> &);
>  extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
>  extern void dump_hex (dump_flags_t, const poly_wide_int &);
>  
> -/* In tree-dump.c  */
> -extern void dump_node (const_tree, dump_flags_t, FILE *);
> -
> -/* In combine.c  */
> -extern void dump_combine_total_stats (FILE *);
> -/* In cfghooks.c  */
> -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> -
> -/* Global variables used to communicate with passes.  */
> -extern FILE *dump_file;
> -extern dump_flags_t dump_flags;
> -extern const char *dump_file_name;
> -
> -extern bool dumps_are_enabled;
> -
> -extern void set_dump_file (FILE *new_dump_file);
> -
> -/* Return true if any of the dumps is enabled, false otherwise. */
> -static inline bool
> -dump_enabled_p (void)
> -{
> -  return dumps_are_enabled;
> -}
> +extern void dumpfile_ensure_any_optinfo_are_flushed ();
>  
>  /* Managing nested scopes, so that dumps can express the call chain
>     leading to a dump message.  */
> @@ -505,8 +524,23 @@ class auto_dump_scope
>  #define AUTO_DUMP_SCOPE(NAME, LOC) \
>    auto_dump_scope scope (NAME, LOC)
>  
> +extern void dump_function (int phase, tree fn);
> +extern void print_combine_total_stats (void);
> +extern bool enable_rtl_dump_file (void);
> +
> +/* In tree-dump.c  */
> +extern void dump_node (const_tree, dump_flags_t, FILE *);
> +
> +/* In combine.c  */
> +extern void dump_combine_total_stats (FILE *);
> +/* In cfghooks.c  */
> +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> +
>  namespace gcc {
>  
> +/* A class for managing all of the various dump files used by the
> +   optimization passes.  */
> +
>  class dump_manager
>  {
>  public:
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> new file mode 100644
> index 0000000..6f224bc
> --- /dev/null
> +++ b/gcc/optinfo.cc
> @@ -0,0 +1,236 @@
> +/* Optimization information.
> +   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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +#include "selftest.h"
> +
> +/* optinfo_item's ctor.  */
> +
> +optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t
> location,
> +			    char *text, bool owned)
> +: m_kind (kind), m_location (location), m_text (text), m_owned
> (owned)
> +{
> +}
> +
> +/* optinfo_item's dtor.  */
> +
> +optinfo_item::~optinfo_item ()
> +{
> +  if (m_owned)
> +    free (m_text);
> +}
> +
> +/* Get a string from KIND.  */
> +
> +const char *
> +optinfo_kind_to_string (enum optinfo_kind kind)
> +{
> +  switch (kind)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case OPTINFO_KIND_SUCCESS:
> +      return "success";
> +    case OPTINFO_KIND_FAILURE:
> +      return "failure";
> +    case OPTINFO_KIND_NOTE:
> +      return "note";
> +    case OPTINFO_KIND_SCOPE:
> +      return "scope";
> +    }
> +}
> +
> +/* optinfo's dtor.  */
> +
> +optinfo::~optinfo ()
> +{
> +  /* Cleanup.  */
> +  unsigned i;
> +  optinfo_item *item;
> +  FOR_EACH_VEC_ELT (m_items, i, item)
> +    delete item;
> +}
> +
> +/* Emit the optinfo to all of the active destinations.  */
> +
> +void
> +optinfo::emit ()
> +{
> +  /* currently this is a no-op.  */
> +}
> +
> +/* Update the optinfo's kind based on DUMP_KIND.  */
> +
> +void
> +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> +{
> +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> +    m_kind = OPTINFO_KIND_SUCCESS;
> +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> +    m_kind = OPTINFO_KIND_FAILURE;
> +  else if (dump_kind & MSG_NOTE)
> +    m_kind = OPTINFO_KIND_NOTE;
> +}
> +
> +/* Append a string literal to this optinfo.  */
> +
> +void
> +optinfo::add_string (const char *str)
> +{
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +			const_cast <char *> (str), false);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf (const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  add_printf_va (format, ap);
> +  va_end (ap);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf_va (const char *format, va_list ap)
> +{
> +  char *formatted_text = xvasprintf (format, ap);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +			formatted_text, true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a gimple statement to this optinfo, equivalent to
> +   print_gimple_stmt.  */
> +
> +void
> +optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t
> dump_flags)
> +{
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
> +  pp_newline (&pp);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location
> (stmt),
> +			xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a gimple statement to this optinfo, equivalent to
> +   print_gimple_expr.  */
> +
> +void
> +optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t
> dump_flags)
> +{
> +  dump_flags |= TDF_RHS_ONLY;
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location
> (stmt),
> +			xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a tree node to this optinfo, equivalent to
> print_generic_expr.  */
> +
> +void
> +optinfo::add_tree (tree node, dump_flags_t dump_flags)
> +{
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_translate_identifiers (&pp) = false;
> +  dump_generic_node (&pp, node, 0, dump_flags, false);
> +
> +  location_t loc = UNKNOWN_LOCATION;
> +  if (EXPR_HAS_LOCATION (node))
> +    loc = EXPR_LOCATION (node);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc,
> +			xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a symbol table node to this optinfo.  */
> +
> +void
> +optinfo::add_symtab_node (symtab_node *node)
> +{
> +  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> +			xstrdup (node->dump_name ()), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append the decimal represenation of a wide_int_ref to this
> +   optinfo.  */
> +
> +void
> +optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> +{
> +  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> +  print_dec (wi, buf, sgn);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +			xstrdup (buf), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +bool optinfo_enabled_p ()
> +{
> +  /* Currently no destinations are implemented, just a hook for
> +     selftests.  */
> +  return dump_context::get ().forcibly_enable_optinfo_p ();
> +}
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +bool optinfo_wants_inlining_info_p ()
> +{
> +  return false;
> +}
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> new file mode 100644
> index 0000000..5bdb9eb
> --- /dev/null
> +++ b/gcc/optinfo.h
> @@ -0,0 +1,203 @@
> +/* Optimization information.
> +   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_OPTINFO_H
> +#define GCC_OPTINFO_H
> +
> +/* An "optinfo" is a bundle of information describing part of an
> +   optimization, which can be emitted to zero or more of several
> +   destinations, such as:
> +
> +   * as a "remark" through the diagnostics subsystem
> +
> +   * saved to a file as an "optimization record"
> +
> +   Currently no such destinations are implemented.
> +
> +   They are generated in response to calls to the "dump_*" API in
> +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> +   into a pending optinfo instance, with a "dump_*_loc" starting a
> new
> +   optinfo instance.
> +
> +   The data sent to the dump calls are captured within the pending
> optinfo
> +   instance as a sequence of optinfo_items.  For example, given:
> +
> +      if (dump_enabled_p ())
> +        {
> +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                           "not vectorized: live stmt not supported:
> ");
> +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt,
> 0);
> +        }
> +
> +   the "dump_printf_loc" call begins a new optinfo containing two
> items:
> +   (1) a text item containing "not vectorized: live stmt not
> supported: "
> +   (2) a gimple item for "stmt"
> +
> +   Dump destinations are thus able to access rich metadata about the
> +   items when the optinfo is emitted to them, rather than just
> having plain
> +   text.  For example, when saving the above optinfo to a file as an
> +   "optimization record", the record could capture the source
> location of
> +   "stmt" above, rather than just its textual form.
> +
> +   The currently pending optinfo is emitted and deleted:
> +   * each time a "dump_*_loc" call occurs (which starts the next
> optinfo), or
> +   * when the dump files are changed (at the end of a pass)
> +
> +   Dumping to an optinfo instance is non-trivial (due to building
> optinfo_item
> +   instances), so all usage should be guarded by
> +
> +     if (optinfo_enabled_p ())
> +
> +   which is off by default.  */
> +
> +
> +/* Forward decls.  */
> +struct opt_pass;
> +class optinfo_item; /* optinfo-internal.h.  */
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +extern bool optinfo_enabled_p ();
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +extern bool optinfo_wants_inlining_info_p ();
> +
> +/* The various kinds of optinfo.  */
> +
> +enum optinfo_kind
> +{
> +  OPTINFO_KIND_SUCCESS,
> +  OPTINFO_KIND_FAILURE,
> +  OPTINFO_KIND_NOTE,
> +  OPTINFO_KIND_SCOPE
> +};
> +
> +extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
> +
> +/* A bundle of information describing part of an optimization.  */
> +
> +class optinfo
> +{
> +  friend class dump_context;
> +
> + public:
> +  optinfo (const dump_location_t &loc,
> +	   enum optinfo_kind kind,
> +	   opt_pass *pass)
> +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> +  {}
> +  ~optinfo ();
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_loc.get_user_location (); }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_loc.get_impl_location (); }
> +
> +  enum optinfo_kind get_kind () const { return m_kind; }
> +  opt_pass *get_pass () const { return m_pass; }
> +  unsigned int num_items () const { return m_items.length (); }
> +  const optinfo_item *get_item (unsigned int i) const { return
> m_items[i]; }
> +
> +  location_t get_location_t () const { return m_loc.get_location_t
> (); }
> +  profile_count get_count () const { return m_loc.get_count (); }
> +
> + private:
> +  void emit ();
> +
> +  /* Pre-canned ways of manipulating the optinfo, for use by friend
> class
> +     dump_context.  */
> +  void handle_dump_file_kind (dump_flags_t);
> +  void add_string (const char *str);
> +  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
> +  void add_printf_va (const char *format, va_list ap)
> ATTRIBUTE_PRINTF (2, 0);
> +  void add_gimple_stmt (gimple *stmt, int spc, dump_flags_t
> dump_flags);
> +  void add_gimple_expr (gimple *stmt, int spc, dump_flags_t
> dump_flags);
> +  void add_tree (tree node, dump_flags_t dump_flags);
> +  void add_symtab_node (symtab_node *node);
> +  void add_dec (const wide_int_ref &wi, signop sgn);
> +
> +  template<unsigned int N, typename C>
> +  void add_poly_int (const poly_int<N, C> &value)
> +  {
> +    /* Compare with dump_dec (MSG_NOTE, ).  */
> +
> +    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> +    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> UNSIGNED;
> +
> +    if (value.is_constant ())
> +      add_dec (value.coeffs[0], sgn);
> +    else
> +      {
> +	add_string ("[");
> +	for (unsigned int i = 0; i < N; ++i)
> +	  {
> +	    add_dec (value.coeffs[i], sgn);
> +	    add_string (i == N - 1 ? "]" : ",");
> +	  }
> +      }
> +  }
> +
> + private:
> +  dump_location_t m_loc;
> +  enum optinfo_kind m_kind;
> +  opt_pass *m_pass;
> +  auto_vec <optinfo_item *> m_items;
> +};
> +
> +/* An enum for discriminating between different kinds of
> optinfo_item.  */
> +
> +enum optinfo_item_kind
> +{
> +  OPTINFO_ITEM_KIND_TEXT,
> +  OPTINFO_ITEM_KIND_TREE,
> +  OPTINFO_ITEM_KIND_GIMPLE,
> +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> +};
> +
> +/* An item within an optinfo.  */
> +
> +class optinfo_item
> +{
> + public:
> +  optinfo_item (enum optinfo_item_kind kind, location_t location,
> +		char *text, bool owned);
> +  ~optinfo_item ();
> +
> +  enum optinfo_item_kind get_kind () const { return m_kind; }
> +  location_t get_location () const { return m_location; }
> +  const char *get_text () const { return m_text; }
> +
> + private:
> +  /* Metadata (e.g. for optimization records).  */
> +  enum optinfo_item_kind m_kind;
> +  location_t m_location;
> +
> +  /* The textual form of the item.  */
> +  char *m_text;
> +  bool m_owned;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_H */

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/2] v5: Add "optinfo" framework
  2018-07-11 10:53                               ` [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
  2018-07-11 10:53                                 ` [PATCH 2/2] Add "-fsave-optimization-record" David Malcolm
  2018-07-18 20:20                                 ` [PING] Re: [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
@ 2018-07-19 12:18                                 ` Richard Biener
  2 siblings, 0 replies; 80+ messages in thread
From: Richard Biener @ 2018-07-19 12:18 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> Changes relative to v4:
> * eliminated optinfo subclasses as discussed
> * eliminated optinfo-internal.h, moving what remained into optinfo.h
> * added support for dump_gimple_expr_loc and dump_gimple_expr
> * more selftests
>
> This patch implements a way to consolidate dump_* calls into
> optinfo objects, as enabling work towards being able to write out
> optimization records to a file (I'm focussing on that destination
> in this patch kit, rather than diagnostic remarks).
>
> The patch adds the support for building optinfo instances from dump_*
> calls, but leaves implementing any *users* of them to followup patches.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

dump_context::get ().dump_symtab_node and friends is a bit
visually disturbing.  They are well-hidden so I guess I simply
look away for a second ;)

Otherwise looks very good now, thus...

... OK.

Thanks and sorry for the delay in reviewing.
Richard.

> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add optinfo.o.
>         * coretypes.h (class symtab_node): New forward decl.
>         (struct cgraph_node): New forward decl.
>         (class varpool_node): New forward decl.
>         * dump-context.h: New file.
>         * dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
>         "tree-pass.h".
>         (refresh_dumps_are_enabled): Use optinfo_enabled_p.
>         (set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
>         (set_alt_dump_file): Likewise.
>         (dump_context::~dump_context): New dtor.
>         (dump_gimple_stmt): Move implementation to...
>         (dump_context::dump_gimple_stmt): ...this new member function.
>         Add the stmt to any pending optinfo, creating one if need be.
>         (dump_gimple_stmt_loc): Move implementation to...
>         (dump_context::dump_gimple_stmt_loc): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_gimple_expr): Move implementation to...
>         (dump_context::dump_gimple_expr): ...this new member function.
>         Add the stmt to any pending optinfo, creating one if need be.
>         (dump_gimple_expr_loc): Move implementation to...
>         (dump_context::dump_gimple_expr_loc): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_generic_expr): Move implementation to...
>         (dump_context::dump_generic_expr): ...this new member function.
>         Add the tree to any pending optinfo, creating one if need be.
>         (dump_generic_expr_loc): Move implementation to...
>         (dump_context::dump_generic_expr_loc): ...this new member
>         function.  Add the tree to any pending optinfo, creating one if
>         need be.
>         (dump_printf): Move implementation to...
>         (dump_context::dump_printf_va): ...this new member function.  Add
>         the text to any pending optinfo, creating one if need be.
>         (dump_printf_loc): Move implementation to...
>         (dump_context::dump_printf_loc_va): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_dec): Move implementation to...
>         (dump_context::dump_dec): ...this new member function.  Add the
>         value to any pending optinfo, creating one if need be.
>         (dump_context::dump_symtab_node): New member function.
>         (dump_context::get_scope_depth): New member function.
>         (dump_context::begin_scope): New member function.
>         (dump_context::end_scope): New member function.
>         (dump_context::ensure_pending_optinfo): New member function.
>         (dump_context::begin_next_optinfo): New member function.
>         (dump_context::end_any_optinfo): New member function.
>         (dump_context::s_current): New global.
>         (dump_context::s_default): New global.
>         (dump_scope_depth): Delete global.
>         (dumpfile_ensure_any_optinfo_are_flushed): New function.
>         (dump_symtab_node): New function.
>         (get_dump_scope_depth): Reimplement in terms of dump_context.
>         (dump_begin_scope): Likewise.
>         (dump_end_scope): Likewise.
>         (selftest::temp_dump_context::temp_dump_context): New ctor.
>         (selftest::temp_dump_context::~temp_dump_context): New dtor.
>         (selftest::verify_item): New function.
>         (ASSERT_IS_TEXT): New macro.
>         (ASSERT_IS_TREE): New macro.
>         (ASSERT_IS_GIMPLE): New macro.
>         (selftest::test_capture_of_dump_calls): New test.
>         (selftest::dumpfile_c_tests): Call it.
>         * dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
>         (dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc)
>         (dump_gimple_stmt, dump_dec): Gather these related decls and add a
>         descriptive comment.
>         (dump_function, print_combine_total_stats, enable_rtl_dump_file)
>         (dump_node, dump_bb): Move these unrelated decls.
>         (class dump_manager): Add leading comment.
>         * optinfo.cc: New file.
>         * optinfo.h: New file.
> ---
>  gcc/Makefile.in    |   1 +
>  gcc/coretypes.h    |   7 +
>  gcc/dump-context.h | 138 +++++++++++++
>  gcc/dumpfile.c     | 597 +++++++++++++++++++++++++++++++++++++++++++++++++----
>  gcc/dumpfile.h     |  84 +++++---
>  gcc/optinfo.cc     | 236 +++++++++++++++++++++
>  gcc/optinfo.h      | 203 ++++++++++++++++++
>  7 files changed, 1200 insertions(+), 66 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 2a05a66..dd1dfc1 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1427,6 +1427,7 @@ OBJS = \
>         optabs-libfuncs.o \
>         optabs-query.o \
>         optabs-tree.o \
> +       optinfo.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..ed0e825 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>
> +/* Subclasses of symtab_node, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..a191e3a
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,138 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   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_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                            const dump_location_t &loc,
> +                            dump_flags_t extra_dump_flags,
> +                            gimple *gs, int spc);
> +
> +  void dump_gimple_expr (dump_flags_t dump_kind,
> +                        dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_expr_loc (dump_flags_t dump_kind,
> +                           const dump_location_t &loc,
> +                           dump_flags_t extra_dump_flags,
> +                           gimple *gs,
> +                           int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +                         dump_flags_t extra_dump_flags,
> +                         tree t);
> +
> +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> +                             const dump_location_t &loc,
> +                             dump_flags_t extra_dump_flags,
> +                             tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                      va_list ap) ATTRIBUTE_PRINTF (3, 0);
> +
> +  void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
> +                          const char *format, va_list ap)
> +    ATTRIBUTE_PRINTF (4, 0);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool forcibly_enable_optinfo_p () const
> +  {
> +    return m_forcibly_enable_optinfo;
> +  }
> +
> +  void end_any_optinfo ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool m_forcibly_enable_optinfo;
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII-style class for use in selftests for temporarily using a different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool forcibly_enable_optinfo);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 7ed1796..38f9539 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -33,6 +33,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h" /* for dump_user_location_t ctor.  */
>  #include "rtl.h" /* for dump_user_location_t ctor.  */
>  #include "selftest.h"
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "cgraph.h"
> +#include "tree-pass.h" /* for "current_pass".  */
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -64,7 +68,7 @@ bool dumps_are_enabled = false;
>  static void
>  refresh_dumps_are_enabled ()
>  {
> -  dumps_are_enabled = (dump_file || alt_dump_file);
> +  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
>  }
>
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
> @@ -73,6 +77,7 @@ refresh_dumps_are_enabled ()
>  void
>  set_dump_file (FILE *new_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file)
>  static void
>  set_alt_dump_file (FILE *new_alt_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    alt_dump_file = new_alt_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>      }
>  }
>
> +/* Implementation of dump_context member functions.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
> +                               dump_flags_t extra_dump_flags,
> +                               gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
>
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs, int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>
>  /* Dump gimple statement GS with SPC indentation spaces and
> @@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>     Do not terminate with a newline or semicolon.  */
>
>  void
> -dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +dump_context::dump_gimple_expr (dump_flags_t dump_kind,
> +                               dump_flags_t extra_dump_flags,
> +                               gimple *gs, int spc)
>  {
>    if (dump_file && (dump_kind & pflags))
>      print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>
>  /* Similar to dump_gimple_expr, except additionally print source location.  */
>
>  void
> -dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs,
> +                                   int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +    }
>  }
>
>
> @@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>     DUMP_KIND is enabled.  */
>
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                  tree t)
> +dump_context::dump_generic_expr (dump_flags_t dump_kind,
> +                                dump_flags_t extra_dump_flags,
> +                                tree t)
>  {
>    if (dump_file && (dump_kind & pflags))
>        print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, dump_flags | extra_dump_flags);
> +    }
>  }
>
>
> @@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>     location.  */
>
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                      dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> +                                    const dump_location_t &loc,
> +                                    dump_flags_t extra_dump_flags,
> +                                    tree t)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        dump_loc (dump_kind, alt_dump_file, srcloc);
>        print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
>      }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_tree (t, dump_flags | extra_dump_flags);
> +    }
>  }
>
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                             va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> -/* Similar to dump_printf, except source location is also printed.  */
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
>
>  void
> -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                const char *format, ...)
> +dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
> +                                 const dump_location_t &loc,
> +                                 const char *format, va_list ap)
>  {
>    location_t srcloc = loc.get_location_t ();
> +
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (dump_file, format, aq);
> +      va_end (aq);
>      }
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      info.add_printf_va (format, aq);
> +      va_end (aq);
>      }
>  }
>
> @@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>
>  template<unsigned int N, typename C>
>  void
> -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> @@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
> +    }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +void
> +dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  if (dump_file && (dump_kind & pflags))
> +    fprintf (dump_file, "%s", node->dump_name ());
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    fprintf (alt_dump_file, "%s", node->dump_name ());
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
> +    }
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Emit a "scope" optinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +void
> +dump_context::begin_scope (const char *name, const dump_location_t &loc)
> +{
> +  /* Specialcase, to avoid going through dump_printf_loc,
> +     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
> +
> +  if (dump_file)
> +    {
> +      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> +      fprintf (dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context; emitting
> +   it to any destinations as appropriate - though none have currently been
> +   implemented.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   member functions.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
> +   Do not terminate with a newline or semicolon.  */
> +
> +void
> +dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_expr, except additionally print source location.  */
> +
> +void
> +dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                  tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
> +}
> +
> +/* Similar to dump_generic_expr, except additionally print the source
> +   location.  */
> +
> +void
> +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                      dump_flags_t extra_dump_flags, tree t)
> +{
> +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
> +                                             t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump streams.  */
> +
> +void
> +dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const poly_wide_int &value)
>      print_hex (value, alt_dump_file);
>  }
>
> -/* The current dump scope-nesting depth.  */
> +/* Emit and delete the currently pending optinfo, if there is one,
> +   without the caller needing to know about class dump_context.  */
> +
> +void
> +dumpfile_ensure_any_optinfo_are_flushed ()
> +{
> +  dump_context::get().end_any_optinfo ();
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
>
> -static int dump_scope_depth;
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
>
>  /* Get the current dump scope-nesting depth.
> -   For use by dump_*_loc (for showing nesting via indentation).  */
> +   For use by -fopt-info (for showing nesting via indentation).  */
>
>  unsigned int
>  get_dump_scope_depth ()
>  {
> -  return dump_scope_depth;
> +  return dump_context::get ().get_scope_depth ();
>  }
>
>  /* Push a nested dump scope.
>     Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
>     destination, if any.
> +   Emit a "scope" opinfo if optinfos are enabled.
>     Increment the scope depth.  */
>
>  void
>  dump_begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> -  dump_scope_depth++;
> +  dump_context::get ().begin_scope (name, loc);
>  }
>
>  /* Pop a nested dump scope.  */
> @@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc)
>  void
>  dump_end_scope ()
>  {
> -  dump_scope_depth--;
> +  dump_context::get ().end_scope ();
>  }
>
>  /* Start a dump for PHASE. Store user-supplied dump flags in
> @@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void)
>
>  #if CHECKING_P
>
> +/* temp_dump_context's ctor.  Temporarily override the dump_context
> +   (to forcibly enable optinfo-generation).  */
> +
> +temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
> +: m_context (),
> +  m_saved (&dump_context ().get ())
> +{
> +  dump_context::s_current = &m_context;
> +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +}
> +
>  namespace selftest {
>
>  /* Verify that the dump_location_t constructors capture the source location
> @@ -1274,12 +1613,188 @@ test_impl_location ()
>  #endif
>  }
>
> +/* Verify that ITEM has the expected values.  */
> +
> +static void
> +verify_item (const location &loc,
> +            const optinfo_item *item,
> +            enum optinfo_item_kind expected_kind,
> +            location_t expected_location,
> +            const char *expected_text)
> +{
> +  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
> +  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
> +  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
> +}
> +
> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> +
> +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
> +                UNKNOWN_LOCATION, (EXPECTED_TEXT));                \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a tree item, with the expected values.  */
> +
> +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
> +                (EXPECTED_LOCATION), (EXPECTED_TEXT));     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a gimple item, with the expected values.  */
> +
> +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
> +                (EXPECTED_LOCATION), (EXPECTED_TEXT));     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that calls to the dump_* API are captured and consolidated into
> +   optimization records. */
> +
> +static void
> +test_capture_of_dump_calls (const line_table_case &case_)
> +{
> +  /* Generate a location_t for testing.  */
> +  line_table_test ltt (case_);
> +  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
> +  linemap_line_start (line_table, 5, 100);
> +  linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> +  location_t where = linemap_position_for_column (line_table, 10);
> +
> +  dump_location_t loc = dump_location_t::from_location_t (where);
> +
> +  /* Test of dump_printf.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> +  }
> +
> +  /* Tree, via dump_generic_expr.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_location_t (), where);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 2);
> +    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> +    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> +  }
> +
> +  /* Tree, via dump_generic_expr_loc.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->get_location_t (), where);
> +    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> +  }
> +
> +  /* Gimple.  */
> +  {
> +    greturn *stmt = gimple_build_return (NULL);
> +    gimple_set_location (stmt, where);
> +
> +    /* dump_gimple_stmt_loc.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +    }
> +
> +    /* dump_gimple_stmt.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +    }
> +
> +    /* dump_gimple_expr_loc.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +    }
> +
> +    /* dump_gimple_expr.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +      optinfo *info = tmp.get_pending_optinfo ();
> +      ASSERT_TRUE (info != NULL);
> +      ASSERT_EQ (info->num_items (), 1);
> +      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +    }
> +  }
> +
> +  /* poly_int.  */
> +  {
> +    temp_dump_context tmp (true);
> +    dump_dec (MSG_NOTE, poly_int64 (42));
> +
> +    optinfo *info = tmp.get_pending_optinfo ();
> +    ASSERT_TRUE (info != NULL);
> +    ASSERT_EQ (info->num_items (), 1);
> +    ASSERT_IS_TEXT (info->get_item (0), "42");
> +  }
> +
> +  /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
> +     above.  */
> +  {
> +    /* MSG_OPTIMIZED_LOCATIONS.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_SUCCESS);
> +    }
> +
> +    /* MSG_MISSED_OPTIMIZATION.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_FAILURE);
> +    }
> +  }
> +}
> +
>  /* Run all of the selftests within this file.  */
>
>  void
>  dumpfile_c_tests ()
>  {
>    test_impl_location ();
> +  for_each_line_table_case (test_capture_of_dump_calls);
>  }
>
>  } // namespace selftest
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 4a71ef7..3096a89 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> +
> +/* Global variables used to communicate with passes.  */
> +extern FILE *dump_file;
> +extern dump_flags_t dump_flags;
> +extern const char *dump_file_name;
> +
> +extern bool dumps_are_enabled;
> +
> +extern void set_dump_file (FILE *new_dump_file);
> +
> +/* Return true if any of the dumps is enabled, false otherwise. */
> +static inline bool
> +dump_enabled_p (void)
> +{
> +  return dumps_are_enabled;
> +}
> +
> +/* The following API calls (which *don't* take a "FILE *")
> +   write the output to zero or more locations:
> +   (a) the active dump_file, if any
> +   (b) the -fopt-info destination, if any
> +   (c) to the "optinfo" destinations, if any:
> +
> +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> +                                   |
> +                                   +--> (b) alt_dump_file
> +                                   |
> +                                   `--> (c) optinfo
> +                                            `---> optinfo destinations
> +
> +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> +   instance: all subsequent dump_* calls are consolidated into
> +   that optinfo, until the next dump_*_loc call (or a change in
> +   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
> +
> +   A group of dump_* calls should be guarded by:
> +
> +     if (dump_enabled_p ())
> +
> +   to minimize the work done for the common case where dumps
> +   are disabled.  */
> +
>  extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
>  extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
>                              const char *, ...) ATTRIBUTE_PRINTF_3;
> @@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr_loc (dump_flags_t, const dump_location_t &,
>                                   dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *, int);
> -extern void print_combine_total_stats (void);
> -extern bool enable_rtl_dump_file (void);
> +extern void dump_symtab_node (dump_flags_t, symtab_node *);
>
>  template<unsigned int N, typename C>
>  void dump_dec (dump_flags_t, const poly_int<N, C> &);
>  extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
>  extern void dump_hex (dump_flags_t, const poly_wide_int &);
>
> -/* In tree-dump.c  */
> -extern void dump_node (const_tree, dump_flags_t, FILE *);
> -
> -/* In combine.c  */
> -extern void dump_combine_total_stats (FILE *);
> -/* In cfghooks.c  */
> -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> -
> -/* Global variables used to communicate with passes.  */
> -extern FILE *dump_file;
> -extern dump_flags_t dump_flags;
> -extern const char *dump_file_name;
> -
> -extern bool dumps_are_enabled;
> -
> -extern void set_dump_file (FILE *new_dump_file);
> -
> -/* Return true if any of the dumps is enabled, false otherwise. */
> -static inline bool
> -dump_enabled_p (void)
> -{
> -  return dumps_are_enabled;
> -}
> +extern void dumpfile_ensure_any_optinfo_are_flushed ();
>
>  /* Managing nested scopes, so that dumps can express the call chain
>     leading to a dump message.  */
> @@ -505,8 +524,23 @@ class auto_dump_scope
>  #define AUTO_DUMP_SCOPE(NAME, LOC) \
>    auto_dump_scope scope (NAME, LOC)
>
> +extern void dump_function (int phase, tree fn);
> +extern void print_combine_total_stats (void);
> +extern bool enable_rtl_dump_file (void);
> +
> +/* In tree-dump.c  */
> +extern void dump_node (const_tree, dump_flags_t, FILE *);
> +
> +/* In combine.c  */
> +extern void dump_combine_total_stats (FILE *);
> +/* In cfghooks.c  */
> +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> +
>  namespace gcc {
>
> +/* A class for managing all of the various dump files used by the
> +   optimization passes.  */
> +
>  class dump_manager
>  {
>  public:
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> new file mode 100644
> index 0000000..6f224bc
> --- /dev/null
> +++ b/gcc/optinfo.cc
> @@ -0,0 +1,236 @@
> +/* Optimization information.
> +   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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +#include "selftest.h"
> +
> +/* optinfo_item's ctor.  */
> +
> +optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
> +                           char *text, bool owned)
> +: m_kind (kind), m_location (location), m_text (text), m_owned (owned)
> +{
> +}
> +
> +/* optinfo_item's dtor.  */
> +
> +optinfo_item::~optinfo_item ()
> +{
> +  if (m_owned)
> +    free (m_text);
> +}
> +
> +/* Get a string from KIND.  */
> +
> +const char *
> +optinfo_kind_to_string (enum optinfo_kind kind)
> +{
> +  switch (kind)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case OPTINFO_KIND_SUCCESS:
> +      return "success";
> +    case OPTINFO_KIND_FAILURE:
> +      return "failure";
> +    case OPTINFO_KIND_NOTE:
> +      return "note";
> +    case OPTINFO_KIND_SCOPE:
> +      return "scope";
> +    }
> +}
> +
> +/* optinfo's dtor.  */
> +
> +optinfo::~optinfo ()
> +{
> +  /* Cleanup.  */
> +  unsigned i;
> +  optinfo_item *item;
> +  FOR_EACH_VEC_ELT (m_items, i, item)
> +    delete item;
> +}
> +
> +/* Emit the optinfo to all of the active destinations.  */
> +
> +void
> +optinfo::emit ()
> +{
> +  /* currently this is a no-op.  */
> +}
> +
> +/* Update the optinfo's kind based on DUMP_KIND.  */
> +
> +void
> +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> +{
> +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> +    m_kind = OPTINFO_KIND_SUCCESS;
> +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> +    m_kind = OPTINFO_KIND_FAILURE;
> +  else if (dump_kind & MSG_NOTE)
> +    m_kind = OPTINFO_KIND_NOTE;
> +}
> +
> +/* Append a string literal to this optinfo.  */
> +
> +void
> +optinfo::add_string (const char *str)
> +{
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +                       const_cast <char *> (str), false);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf (const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  add_printf_va (format, ap);
> +  va_end (ap);
> +}
> +
> +/* Append printf-formatted text to this optinfo.  */
> +
> +void
> +optinfo::add_printf_va (const char *format, va_list ap)
> +{
> +  char *formatted_text = xvasprintf (format, ap);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +                       formatted_text, true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a gimple statement to this optinfo, equivalent to
> +   print_gimple_stmt.  */
> +
> +void
> +optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags)
> +{
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
> +  pp_newline (&pp);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
> +                       xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a gimple statement to this optinfo, equivalent to
> +   print_gimple_expr.  */
> +
> +void
> +optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags)
> +{
> +  dump_flags |= TDF_RHS_ONLY;
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
> +                       xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a tree node to this optinfo, equivalent to print_generic_expr.  */
> +
> +void
> +optinfo::add_tree (tree node, dump_flags_t dump_flags)
> +{
> +  pretty_printer pp;
> +  pp_needs_newline (&pp) = true;
> +  pp_translate_identifiers (&pp) = false;
> +  dump_generic_node (&pp, node, 0, dump_flags, false);
> +
> +  location_t loc = UNKNOWN_LOCATION;
> +  if (EXPR_HAS_LOCATION (node))
> +    loc = EXPR_LOCATION (node);
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc,
> +                       xstrdup (pp_formatted_text (&pp)), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append a symbol table node to this optinfo.  */
> +
> +void
> +optinfo::add_symtab_node (symtab_node *node)
> +{
> +  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> +                       xstrdup (node->dump_name ()), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Append the decimal represenation of a wide_int_ref to this
> +   optinfo.  */
> +
> +void
> +optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> +{
> +  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> +  print_dec (wi, buf, sgn);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +                       xstrdup (buf), true);
> +  m_items.safe_push (item);
> +}
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +bool optinfo_enabled_p ()
> +{
> +  /* Currently no destinations are implemented, just a hook for
> +     selftests.  */
> +  return dump_context::get ().forcibly_enable_optinfo_p ();
> +}
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +bool optinfo_wants_inlining_info_p ()
> +{
> +  return false;
> +}
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> new file mode 100644
> index 0000000..5bdb9eb
> --- /dev/null
> +++ b/gcc/optinfo.h
> @@ -0,0 +1,203 @@
> +/* Optimization information.
> +   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_OPTINFO_H
> +#define GCC_OPTINFO_H
> +
> +/* An "optinfo" is a bundle of information describing part of an
> +   optimization, which can be emitted to zero or more of several
> +   destinations, such as:
> +
> +   * as a "remark" through the diagnostics subsystem
> +
> +   * saved to a file as an "optimization record"
> +
> +   Currently no such destinations are implemented.
> +
> +   They are generated in response to calls to the "dump_*" API in
> +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> +   into a pending optinfo instance, with a "dump_*_loc" starting a new
> +   optinfo instance.
> +
> +   The data sent to the dump calls are captured within the pending optinfo
> +   instance as a sequence of optinfo_items.  For example, given:
> +
> +      if (dump_enabled_p ())
> +        {
> +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                           "not vectorized: live stmt not supported: ");
> +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
> +        }
> +
> +   the "dump_printf_loc" call begins a new optinfo containing two items:
> +   (1) a text item containing "not vectorized: live stmt not supported: "
> +   (2) a gimple item for "stmt"
> +
> +   Dump destinations are thus able to access rich metadata about the
> +   items when the optinfo is emitted to them, rather than just having plain
> +   text.  For example, when saving the above optinfo to a file as an
> +   "optimization record", the record could capture the source location of
> +   "stmt" above, rather than just its textual form.
> +
> +   The currently pending optinfo is emitted and deleted:
> +   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
> +   * when the dump files are changed (at the end of a pass)
> +
> +   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
> +   instances), so all usage should be guarded by
> +
> +     if (optinfo_enabled_p ())
> +
> +   which is off by default.  */
> +
> +
> +/* Forward decls.  */
> +struct opt_pass;
> +class optinfo_item; /* optinfo-internal.h.  */
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +extern bool optinfo_enabled_p ();
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +extern bool optinfo_wants_inlining_info_p ();
> +
> +/* The various kinds of optinfo.  */
> +
> +enum optinfo_kind
> +{
> +  OPTINFO_KIND_SUCCESS,
> +  OPTINFO_KIND_FAILURE,
> +  OPTINFO_KIND_NOTE,
> +  OPTINFO_KIND_SCOPE
> +};
> +
> +extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
> +
> +/* A bundle of information describing part of an optimization.  */
> +
> +class optinfo
> +{
> +  friend class dump_context;
> +
> + public:
> +  optinfo (const dump_location_t &loc,
> +          enum optinfo_kind kind,
> +          opt_pass *pass)
> +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> +  {}
> +  ~optinfo ();
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_loc.get_user_location (); }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_loc.get_impl_location (); }
> +
> +  enum optinfo_kind get_kind () const { return m_kind; }
> +  opt_pass *get_pass () const { return m_pass; }
> +  unsigned int num_items () const { return m_items.length (); }
> +  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
> +
> +  location_t get_location_t () const { return m_loc.get_location_t (); }
> +  profile_count get_count () const { return m_loc.get_count (); }
> +
> + private:
> +  void emit ();
> +
> +  /* Pre-canned ways of manipulating the optinfo, for use by friend class
> +     dump_context.  */
> +  void handle_dump_file_kind (dump_flags_t);
> +  void add_string (const char *str);
> +  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
> +  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
> +  void add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags);
> +  void add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags);
> +  void add_tree (tree node, dump_flags_t dump_flags);
> +  void add_symtab_node (symtab_node *node);
> +  void add_dec (const wide_int_ref &wi, signop sgn);
> +
> +  template<unsigned int N, typename C>
> +  void add_poly_int (const poly_int<N, C> &value)
> +  {
> +    /* Compare with dump_dec (MSG_NOTE, ).  */
> +
> +    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> +    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> +
> +    if (value.is_constant ())
> +      add_dec (value.coeffs[0], sgn);
> +    else
> +      {
> +       add_string ("[");
> +       for (unsigned int i = 0; i < N; ++i)
> +         {
> +           add_dec (value.coeffs[i], sgn);
> +           add_string (i == N - 1 ? "]" : ",");
> +         }
> +      }
> +  }
> +
> + private:
> +  dump_location_t m_loc;
> +  enum optinfo_kind m_kind;
> +  opt_pass *m_pass;
> +  auto_vec <optinfo_item *> m_items;
> +};
> +
> +/* An enum for discriminating between different kinds of optinfo_item.  */
> +
> +enum optinfo_item_kind
> +{
> +  OPTINFO_ITEM_KIND_TEXT,
> +  OPTINFO_ITEM_KIND_TREE,
> +  OPTINFO_ITEM_KIND_GIMPLE,
> +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> +};
> +
> +/* An item within an optinfo.  */
> +
> +class optinfo_item
> +{
> + public:
> +  optinfo_item (enum optinfo_item_kind kind, location_t location,
> +               char *text, bool owned);
> +  ~optinfo_item ();
> +
> +  enum optinfo_item_kind get_kind () const { return m_kind; }
> +  location_t get_location () const { return m_location; }
> +  const char *get_text () const { return m_text; }
> +
> + private:
> +  /* Metadata (e.g. for optimization records).  */
> +  enum optinfo_item_kind m_kind;
> +  location_t m_location;
> +
> +  /* The textual form of the item.  */
> +  char *m_text;
> +  bool m_owned;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_H */
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/2] Add "-fsave-optimization-record"
  2018-07-11 10:53                                 ` [PATCH 2/2] Add "-fsave-optimization-record" David Malcolm
@ 2018-07-19 12:39                                   ` Richard Biener
  2018-07-20 15:45                                     ` David Malcolm
  0 siblings, 1 reply; 80+ messages in thread
From: Richard Biener @ 2018-07-19 12:39 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch implements a -fsave-optimization-record option, which
> leads to a JSON file being written out, recording the dump_* calls
> made (via the optinfo infrastructure in the previous patch).
>
> The patch includes a minimal version of the JSON patch I posted last
> year, with just enough support needed for optimization records (I
> removed all of the parser code, leaving just the code for building
> in-memory JSON trees and writing them to a pretty_printer).
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+

I guess this should note that it is experimental and in no way
complete.  Maybe list areas where reports will be generated,
like vectorization?

Did you check what happens with -flto -fsave-optimization-record?
Will the compile-phase emit a json file for each source (expected,
like with early inlining decisions)?  Will the WPA phase emit one
(IPA decisions?) or will IPA decisions be recorded in the LTRANS
one?  How will the LTRANS ones be named and where can they
be found?  You don't need to solve all the issues with this patch
but they should be eventually addressed somehow.

I don't question the use or implementation of JSON, I'll just
approve it.

The rest looks obvious enough, thus OK.

Some overall blurb in the documentation or changes.html
on how to use this would be nice of course.

Thanks,
Richard.


> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
>         (CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
>         * common.opt (fsave-optimization-record): New option.
>         * coretypes.h (struct kv_pair): Move here from dumpfile.c.
>         * doc/invoke.texi (-fsave-optimization-record): New option.
>         * dumpfile.c: Include "optinfo-emit-json.h".
>         (struct kv_pair): Move to coretypes.h.
>         (optgroup_options): Make non-static.
>         (dump_context::end_scope): Call
>         optimization_records_maybe_pop_dump_scope.
>         * dumpfile.h (optgroup_options): New decl.
>         * json.cc: New file.
>         * json.h: New file.
>         * optinfo-emit-json.cc: New file.
>         * optinfo-emit-json.h: New file.
>         * optinfo.cc: Include "optinfo-emit-json.h".
>         (optinfo::emit): Call optimization_records_maybe_record_optinfo.
>         (optinfo_enabled_p): Check optimization_records_enabled_p.
>         (optinfo_wants_inlining_info_p): Likewise.
>         * optinfo.h: Update comment.
>         * profile-count.c (profile_quality_as_string): New function.
>         * profile-count.h (profile_quality_as_string): New decl.
>         (profile_count::quality): New accessor.
>         * selftest-run-tests.c (selftest::run_tests): Call json_cc_tests
>         and optinfo_emit_json_cc_tests.
>         * selftest.h (selftest::json_cc_tests): New decl.
>         (selftest::optinfo_emit_json_cc_tests): New decl.
>         * toplev.c: Include "optinfo-emit-json.h".
>         (compile_file): Call optimization_records_finish.
>         (do_compile): Call optimization_records_start.
>         * tree-ssa-live.c: Include optinfo.h.
>         (remove_unused_scope_block_p): Retain inlining information if
>         optinfo_wants_inlining_info_p returns true.
> ---
>  gcc/Makefile.in          |   3 +
>  gcc/common.opt           |   4 +
>  gcc/coretypes.h          |   8 +
>  gcc/doc/invoke.texi      |   8 +-
>  gcc/dumpfile.c           |  15 +-
>  gcc/dumpfile.h           |   3 +
>  gcc/json.cc              | 293 ++++++++++++++++++++++++
>  gcc/json.h               | 166 ++++++++++++++
>  gcc/optinfo-emit-json.cc | 568 +++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/optinfo-emit-json.h  |  36 +++
>  gcc/optinfo.cc           |  11 +-
>  gcc/optinfo.h            |   4 -
>  gcc/profile-count.c      |  28 +++
>  gcc/profile-count.h      |   5 +
>  gcc/selftest-run-tests.c |   2 +
>  gcc/selftest.h           |   2 +
>  gcc/toplev.c             |   5 +
>  gcc/tree-ssa-live.c      |   4 +-
>  18 files changed, 1143 insertions(+), 22 deletions(-)
>  create mode 100644 gcc/json.cc
>  create mode 100644 gcc/json.h
>  create mode 100644 gcc/optinfo-emit-json.cc
>  create mode 100644 gcc/optinfo-emit-json.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index dd1dfc1..b871640 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1387,6 +1387,7 @@ OBJS = \
>         ira-color.o \
>         ira-emit.o \
>         ira-lives.o \
> +       json.o \
>         jump.o \
>         langhooks.o \
>         lcm.o \
> @@ -1428,6 +1429,7 @@ OBJS = \
>         optabs-query.o \
>         optabs-tree.o \
>         optinfo.o \
> +       optinfo-emit-json.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> @@ -2251,6 +2253,7 @@ s-bversion: BASE-VER
>         $(STAMP) s-bversion
>
>  CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
> +CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
>
>  pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
>                     $(srcdir)/gen-pass-instances.awk
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 5a50bc27..a13c709 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1950,6 +1950,10 @@ fopt-info-
>  Common Joined RejectNegative Var(common_deferred_options) Defer
>  -fopt-info[-<type>=filename]   Dump compiler optimization details.
>
> +fsave-optimization-record
> +Common Report Var(flag_save_optimization_record) Optimization
> +Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
> +
>  foptimize-register-move
>  Common Ignore
>  Does nothing. Preserved for backward compatibility.
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index ed0e825..2fd20e4 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -332,6 +332,14 @@ namespace gcc {
>
>  typedef std::pair <tree, tree> tree_pair;
>
> +/* Define a name->value mapping.  */
> +template <typename ValueType>
> +struct kv_pair
> +{
> +  const char *const name;      /* the name of the value */
> +  const ValueType value;       /* the value of the name */
> +};
> +
>  #else
>
>  struct _dont_use_rtx_here_;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 56cd122..c239c53 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
>  -freorder-blocks-algorithm=@var{algorithm} @gol
>  -freorder-blocks-and-partition  -freorder-functions @gol
>  -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
> --frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
> +-frounding-math  -fsave-optimization-record @gol
> +-fsched2-use-superblocks  -fsched-pressure @gol
>  -fsched-spec-load  -fsched-spec-load-dangerous @gol
>  -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
>  -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
> @@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer control of this setting
>  using C99's @code{FENV_ACCESS} pragma.  This command-line option
>  will be used to specify the default state for @code{FENV_ACCESS}.
>
> +@item -fsave-optimization-record
> +@opindex fsave-optimization-record
> +Write a SRCFILE.opt-record.json file detailing what optimizations
> +were performed.
> +
>  @item -fsignaling-nans
>  @opindex fsignaling-nans
>  Compile code assuming that IEEE signaling NaNs may generate user-visible
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 38f9539..331b7fb 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "dump-context.h"
>  #include "cgraph.h"
>  #include "tree-pass.h" /* for "current_pass".  */
> +#include "optinfo-emit-json.h"
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -118,14 +119,6 @@ static struct dump_file_info dump_files[TDI_end] =
>    DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
>  };
>
> -/* Define a name->number mapping for a dump flag value.  */
> -template <typename ValueType>
> -struct kv_pair
> -{
> -  const char *const name;      /* the name of the value */
> -  const ValueType value;       /* the value of the name */
> -};
> -
>  /* Table of dump options. This must be consistent with the TDF_* flags
>     in dumpfile.h and opt_info_options below. */
>  static const kv_pair<dump_flags_t> dump_options[] =
> @@ -176,7 +169,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
>  };
>
>  /* Flags used for -fopt-info groups.  */
> -static const kv_pair<optgroup_flags_t> optgroup_options[] =
> +const kv_pair<optgroup_flags_t> optgroup_options[] =
>  {
>    {"ipa", OPTGROUP_IPA},
>    {"loop", OPTGROUP_LOOP},
> @@ -794,6 +787,7 @@ dump_context::end_scope ()
>  {
>    end_any_optinfo ();
>    m_scope_depth--;
> +  optimization_records_maybe_pop_dump_scope ();
>  }
>
>  /* Return the optinfo currently being accumulated, creating one if
> @@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc)
>  }
>
>  /* End any optinfo that has been accumulated within this context; emitting
> -   it to any destinations as appropriate - though none have currently been
> -   implemented.  */
> +   it to any destinations as appropriate, such as optimization records.  */
>
>  void
>  dump_context::end_any_optinfo ()
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 3096a89..e4823f8 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,7 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> +extern const kv_pair<optgroup_flags_t> optgroup_options[];
>
>  /* Global variables used to communicate with passes.  */
>  extern FILE *dump_file;
> @@ -442,6 +443,7 @@ dump_enabled_p (void)
>     (a) the active dump_file, if any
>     (b) the -fopt-info destination, if any
>     (c) to the "optinfo" destinations, if any:
> +       (c.1) as optimization records
>
>     dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
>                                     |
> @@ -449,6 +451,7 @@ dump_enabled_p (void)
>                                     |
>                                     `--> (c) optinfo
>                                              `---> optinfo destinations
> +                                                  (c.1) optimization records
>
>     For optinfos, the dump_*_loc mark the beginning of an optinfo
>     instance: all subsequent dump_* calls are consolidated into
> diff --git a/gcc/json.cc b/gcc/json.cc
> new file mode 100644
> index 0000000..3c2aa77
> --- /dev/null
> +++ b/gcc/json.cc
> @@ -0,0 +1,293 @@
> +/* JSON trees
> +   Copyright (C) 2017-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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "json.h"
> +#include "pretty-print.h"
> +#include "math.h"
> +#include "selftest.h"
> +
> +using namespace json;
> +
> +/* class json::value.  */
> +
> +/* Dump this json::value tree to OUTF.
> +   No formatting is done.  There are no guarantees about the order
> +   in which the key/value pairs of json::objects are printed.  */
> +
> +void
> +value::dump (FILE *outf) const
> +{
> +  pretty_printer pp;
> +  pp_buffer (&pp)->stream = outf;
> +  print (&pp);
> +  pp_flush (&pp);
> +}
> +
> +/* class json::object, a subclass of json::value, representing
> +   an unordered collection of key/value pairs.  */
> +
> +/* json:object's dtor.  */
> +
> +object::~object ()
> +{
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      free (const_cast <char *>((*it).first));
> +      delete ((*it).second);
> +    }
> +}
> +
> +/* Implementation of json::value::print for json::object.  */
> +
> +void
> +object::print (pretty_printer *pp) const
> +{
> +  /* Note that the order is not guaranteed.  */
> +  pp_character (pp, '{');
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      if (it != m_map.begin ())
> +       pp_string (pp, ", ");
> +      const char *key = const_cast <char *>((*it).first);
> +      value *value = (*it).second;
> +      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
> +      value->print (pp);
> +    }
> +  pp_character (pp, '}');
> +}
> +
> +/* Set the json::value * for KEY, taking ownership of VALUE
> +   (and taking a copy of KEY if necessary).  */
> +
> +void
> +object::set (const char *key, value *v)
> +{
> +  value **ptr = m_map.get (key);
> +  if (ptr)
> +    {
> +      /* If the key is already present, delete the existing value
> +        and overwrite it.  */
> +      delete *ptr;
> +      *ptr = v;
> +    }
> +  else
> +    /* If the key wasn't already present, take a copy of the key,
> +       and store the value.  */
> +    m_map.put (xstrdup (key), v);
> +}
> +
> +/* class json::array, a subclass of json::value, representing
> +   an ordered collection of values.  */
> +
> +/* json::array's dtor.  */
> +
> +array::~array ()
> +{
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    delete v;
> +}
> +
> +/* Implementation of json::value::print for json::array.  */
> +
> +void
> +array::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '[');
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    {
> +      if (i)
> +       pp_string (pp, ", ");
> +      v->print (pp);
> +    }
> +  pp_character (pp, ']');
> +}
> +
> +/* class json::number, a subclass of json::value, wrapping a double.  */
> +
> +/* Implementation of json::value::print for json::number.  */
> +
> +void
> +number::print (pretty_printer *pp) const
> +{
> +  char tmp[1024];
> +  snprintf (tmp, sizeof (tmp), "%g", m_value);
> +  pp_string (pp, tmp);
> +}
> +
> +/* class json::string, a subclass of json::value.  */
> +
> +void
> +string::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '"');
> +  for (const char *ptr = m_utf8; *ptr; ptr++)
> +    {
> +      char ch = *ptr;
> +      switch (ch)
> +       {
> +       case '"':
> +         pp_string (pp, "\\\"");
> +         break;
> +       case '\\':
> +         pp_string (pp, "\\n");
> +         break;
> +       case '\b':
> +         pp_string (pp, "\\b");
> +         break;
> +       case '\f':
> +         pp_string (pp, "\\f");
> +         break;
> +       case '\n':
> +         pp_string (pp, "\\n");
> +         break;
> +       case '\r':
> +         pp_string (pp, "\\r");
> +         break;
> +       case '\t':
> +         pp_string (pp, "\\t");
> +         break;
> +
> +       default:
> +         pp_character (pp, ch);
> +       }
> +    }
> +  pp_character (pp, '"');
> +}
> +
> +/* class json::literal, a subclass of json::value.  */
> +
> +/* Implementation of json::value::print for json::literal.  */
> +
> +void
> +literal::print (pretty_printer *pp) const
> +{
> +  switch (m_kind)
> +    {
> +    case JSON_TRUE:
> +      pp_string (pp, "true");
> +      break;
> +    case JSON_FALSE:
> +      pp_string (pp, "false");
> +      break;
> +    case JSON_NULL:
> +      pp_string (pp, "null");
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Selftests.  */
> +
> +/* Verify that JV->print () prints EXPECTED_JSON.  */
> +
> +static void
> +assert_print_eq (const json::value &jv, const char *expected_json)
> +{
> +  pretty_printer pp;
> +  jv.print (&pp);
> +  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
> +}
> +
> +/* Verify that JSON objects are written correctly.  We can't test more than
> +   one key/value pair, as we don't impose a guaranteed ordering.  */
> +
> +static void
> +test_writing_objects ()
> +{
> +  object obj;
> +  obj.set ("foo", new json::string ("bar"));
> +  assert_print_eq (obj, "{\"foo\": \"bar\"}");
> +}
> +
> +/* Verify that JSON arrays are written correctly.  */
> +
> +static void
> +test_writing_arrays ()
> +{
> +  array arr;
> +  assert_print_eq (arr, "[]");
> +
> +  arr.append (new json::string ("foo"));
> +  assert_print_eq (arr, "[\"foo\"]");
> +
> +  arr.append (new json::string ("bar"));
> +  assert_print_eq (arr, "[\"foo\", \"bar\"]");
> +}
> +
> +/* Verify that JSON numbers are written correctly.  */
> +
> +static void
> +test_writing_numbers ()
> +{
> +  assert_print_eq (number (0), "0");
> +  assert_print_eq (number (42), "42");
> +  assert_print_eq (number (-100), "-100");
> +}
> +
> +/* Verify that JSON strings are written correctly.  */
> +
> +static void
> +test_writing_strings ()
> +{
> +  string foo ("foo");
> +  assert_print_eq (foo, "\"foo\"");
> +
> +  string contains_quotes ("before \"quoted\" after");
> +  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\"");
> +}
> +
> +/* Verify that JSON strings are written correctly.  */
> +
> +static void
> +test_writing_literals ()
> +{
> +  assert_print_eq (literal (JSON_TRUE), "true");
> +  assert_print_eq (literal (JSON_FALSE), "false");
> +  assert_print_eq (literal (JSON_NULL), "null");
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +json_cc_tests ()
> +{
> +  test_writing_objects ();
> +  test_writing_arrays ();
> +  test_writing_numbers ();
> +  test_writing_strings ();
> +  test_writing_literals ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* #if CHECKING_P */
> diff --git a/gcc/json.h b/gcc/json.h
> new file mode 100644
> index 0000000..5c3274c
> --- /dev/null
> +++ b/gcc/json.h
> @@ -0,0 +1,166 @@
> +/* JSON trees
> +   Copyright (C) 2017-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_JSON_H
> +#define GCC_JSON_H
> +
> +/* Implementation of JSON, a lightweight data-interchange format.
> +
> +   See http://www.json.org/
> +   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
> +   and https://tools.ietf.org/html/rfc7159
> +
> +   Supports creating a DOM-like tree of json::value *, and then dumping
> +   json::value * to text.  */
> +
> +namespace json
> +{
> +
> +/* Forward decls of json::value and its subclasses (using indentation
> +   to denote inheritance.  */
> +
> +class value;
> +  class object;
> +  class array;
> +  class number;
> +  class string;
> +  class literal;
> +
> +/* An enum for discriminating the subclasses of json::value.  */
> +
> +enum kind
> +{
> +  /* class json::object.  */
> +  JSON_OBJECT,
> +
> +  /* class json::array.  */
> +  JSON_ARRAY,
> +
> +  /* class json::number.  */
> +  JSON_NUMBER,
> +
> +  /* class json::string.  */
> +  JSON_STRING,
> +
> +  /* class json::literal uses these three values to identify the
> +     particular literal.  */
> +  JSON_TRUE,
> +  JSON_FALSE,
> +  JSON_NULL
> +};
> +
> +/* Base class of JSON value.  */
> +
> +class value
> +{
> + public:
> +  virtual ~value () {}
> +  virtual enum kind get_kind () const = 0;
> +  virtual void print (pretty_printer *pp) const = 0;
> +
> +  void dump (FILE *) const;
> +};
> +
> +/* Subclass of value for objects: an unordered collection of
> +   key/value pairs.  */
> +
> +class object : public value
> +{
> + public:
> +  ~object ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  void set (const char *key, value *v);
> +
> + private:
> +  typedef hash_map <char *, value *,
> +    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
> +  map_t m_map;
> +};
> +
> +/* Subclass of value for arrays.  */
> +
> +class array : public value
> +{
> + public:
> +  ~array ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  void append (value *v) { m_elements.safe_push (v); }
> +
> + private:
> +  auto_vec<value *> m_elements;
> +};
> +
> +/* Subclass of value for numbers.  */
> +
> +class number : public value
> +{
> + public:
> +  number (double value) : m_value (value) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  double get () const { return m_value; }
> +
> + private:
> +  double m_value;
> +};
> +
> +/* Subclass of value for strings.  */
> +
> +class string : public value
> +{
> + public:
> +  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
> +  ~string () { free (m_utf8); }
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  const char *get_string () const { return m_utf8; }
> +
> + private:
> +  char *m_utf8;
> +};
> +
> +/* Subclass of value for the three JSON literals "true", "false",
> +   and "null".  */
> +
> +class literal : public value
> +{
> + public:
> +  literal (enum kind kind) : m_kind (kind) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> + private:
> +  enum kind m_kind;
> +};
> +
> +} // namespace json
> +
> +#endif  /* GCC_JSON_H  */
> diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> new file mode 100644
> index 0000000..bf1172a
> --- /dev/null
> +++ b/gcc/optinfo-emit-json.cc
> @@ -0,0 +1,568 @@
> +/* Emit optimization information as JSON files.
> +   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/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +#include "diagnostic-core.h"
> +
> +#include "profile.h"
> +#include "output.h"
> +#include "tree-pass.h"
> +
> +#include "optinfo.h"
> +#include "optinfo-emit-json.h"
> +#include "json.h"
> +#include "pretty-print.h"
> +#include "tree-pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +
> +#include "langhooks.h"
> +#include "version.h"
> +#include "context.h"
> +#include "pass_manager.h"
> +#include "selftest.h"
> +#include "dump-context.h"
> +
> +/* A class for writing out optimization records in JSON format.  */
> +
> +class optrecord_json_writer
> +{
> +public:
> +  optrecord_json_writer ();
> +  ~optrecord_json_writer ();
> +  void write () const;
> +  void add_record (const optinfo *optinfo);
> +  void pop_scope ();
> +
> +  void add_record (json::object *obj);
> +  json::object *impl_location_to_json (dump_impl_location_t loc);
> +  json::object *location_to_json (location_t loc);
> +  json::object *profile_count_to_json (profile_count count);
> +  json::string *get_id_value_for_pass (opt_pass *pass);
> +  json::object *pass_to_json (opt_pass *pass);
> +  json::value *inlining_chain_to_json (location_t loc);
> +  json::object *optinfo_to_json (const optinfo *optinfo);
> +  void add_pass_list (json::array *arr, opt_pass *pass);
> +
> +private:
> +  /* The root value for the JSON file.
> +     Currently the JSON values are stored in memory, and flushed when the
> +     compiler exits.  It would probably be better to simply write out
> +     the JSON as we go.  */
> +  json::array *m_root_tuple;
> +
> +  /* The currently open scopes, for expressing nested optimization records.  */
> +  vec<json::array *> m_scopes;
> +};
> +
> +/* optrecord_json_writer's ctor.  Populate the top-level parts of the
> +   in-memory JSON representation.  */
> +
> +optrecord_json_writer::optrecord_json_writer ()
> +  : m_root_tuple (NULL), m_scopes ()
> +{
> +  m_root_tuple = new json::array ();
> +
> +  /* Populate with metadata; compare with toplev.c: print_version.  */
> +  json::object *metadata = new json::object ();
> +  m_root_tuple->append (metadata);
> +  metadata->set ("format", new json::string ("1"));
> +  json::object *generator = new json::object ();
> +  metadata->set ("generator", generator);
> +  generator->set ("name", new json::string (lang_hooks.name));
> +  generator->set ("pkgversion", new json::string (pkgversion_string));
> +  generator->set ("version", new json::string (version_string));
> +  /* TARGET_NAME is passed in by the Makefile.  */
> +  generator->set ("target", new json::string (TARGET_NAME));
> +
> +  /* TODO: capture command-line?
> +     see gen_producer_string in dwarf2out.c (currently static).  */
> +
> +  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
> +
> +  json::array *passes = new json::array ();
> +  m_root_tuple->append (passes);
> +
> +  /* Call add_pass_list for all of the pass lists.  */
> +  {
> +#define DEF_PASS_LIST(LIST) \
> +    add_pass_list (passes, g->get_passes ()->LIST);
> +    GCC_PASS_LISTS
> +#undef DEF_PASS_LIST
> +  }
> +
> +  json::array *records = new json::array ();
> +  m_root_tuple->append (records);
> +
> +  m_scopes.safe_push (records);
> +}
> +
> +/* optrecord_json_writer's ctor.
> +   Delete the in-memory JSON representation.  */
> +
> +optrecord_json_writer::~optrecord_json_writer ()
> +{
> +  delete m_root_tuple;
> +}
> +
> +/* Choose an appropriate filename, and write the saved records to it.  */
> +
> +void
> +optrecord_json_writer::write () const
> +{
> +  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
> +  FILE *outfile = fopen (filename, "w");
> +  if (outfile)
> +    {
> +      m_root_tuple->dump (outfile);
> +      fclose (outfile);
> +    }
> +  else
> +    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
> +             filename); // FIXME: more info?
> +  free (filename);
> +}
> +
> +/* Add a record for OPTINFO to the queue of records to be written.  */
> +
> +void
> +optrecord_json_writer::add_record (const optinfo *optinfo)
> +{
> +  json::object *obj = optinfo_to_json (optinfo);
> +
> +  add_record (obj);
> +
> +  /* Potentially push the scope.  */
> +  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
> +    {
> +      json::array *children = new json::array ();
> +      obj->set ("children", children);
> +      m_scopes.safe_push (children);
> +    }
> +}
> +
> +/* Private methods of optrecord_json_writer.  */
> +
> +/* Add record OBJ to the the innermost scope.  */
> +
> +void
> +optrecord_json_writer::add_record (json::object *obj)
> +{
> +  /* Add to innermost scope.  */
> +  gcc_assert (m_scopes.length () > 0);
> +  m_scopes[m_scopes.length () - 1]->append (obj);
> +}
> +
> +/* Pop the innermost scope.  */
> +
> +void
> +optrecord_json_writer::pop_scope ()
> +{
> +  m_scopes.pop ();
> +}
> +
> +/* Create a JSON object representing LOC.  */
> +
> +json::object *
> +optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("file", new json::string (loc.m_file));
> +  obj->set ("line", new json::number (loc.m_line));
> +  if (loc.m_function)
> +    obj->set ("function", new json::string (loc.m_function));
> +  return obj;
> +}
> +
> +/* Create a JSON object representing LOC.  */
> +
> +json::object *
> +optrecord_json_writer::location_to_json (location_t loc)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("file", new json::string (LOCATION_FILE (loc)));
> +  obj->set ("line", new json::number (LOCATION_LINE (loc)));
> +  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
> +  return obj;
> +}
> +
> +/* Create a JSON object representing COUNT.  */
> +
> +json::object *
> +optrecord_json_writer::profile_count_to_json (profile_count count)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("value", new json::number (count.to_gcov_type ()));
> +  obj->set ("quality",
> +           new json::string (profile_quality_as_string (count.quality ())));
> +  return obj;
> +}
> +
> +/* Get a string for use when referring to PASS in the saved optimization
> +   records.  */
> +
> +json::string *
> +optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
> +{
> +  pretty_printer pp;
> +  /* this is host-dependent, but will be consistent for a given host.  */
> +  pp_pointer (&pp, static_cast<void *> (pass));
> +  return new json::string (pp_formatted_text (&pp));
> +}
> +
> +/* Create a JSON object representing PASS.  */
> +
> +json::object *
> +optrecord_json_writer::pass_to_json (opt_pass *pass)
> +{
> +  json::object *obj = new json::object ();
> +  const char *type = NULL;
> +  switch (pass->type)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case GIMPLE_PASS:
> +      type = "gimple";
> +      break;
> +    case RTL_PASS:
> +      type = "rtl";
> +      break;
> +    case SIMPLE_IPA_PASS:
> +      type = "simple_ipa";
> +      break;
> +    case IPA_PASS:
> +      type = "ipa";
> +      break;
> +    }
> +  obj->set ("id", get_id_value_for_pass (pass));
> +  obj->set ("type", new json::string (type));
> +  obj->set ("name", new json::string (pass->name));
> +  /* Represent the optgroup flags as an array.  */
> +  {
> +    json::array *optgroups = new json::array ();
> +    obj->set ("optgroups", optgroups);
> +    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
> +        optgroup->name != NULL; optgroup++)
> +      if (optgroup->value != OPTGROUP_ALL
> +         && (pass->optinfo_flags & optgroup->value))
> +       optgroups->append (new json::string (optgroup->name));
> +  }
> +  obj->set ("num", new json::number (pass->static_pass_number));
> +  return obj;
> +}
> +
> +/* Create a JSON array for LOC representing the chain of inlining
> +   locations.
> +   Compare with lhd_print_error_function and cp_print_error_function.  */
> +
> +json::value *
> +optrecord_json_writer::inlining_chain_to_json (location_t loc)
> +{
> +  json::array *array = new json::array ();
> +
> +  tree abstract_origin = LOCATION_BLOCK (loc);
> +
> +  while (abstract_origin)
> +    {
> +      location_t *locus;
> +      tree block = abstract_origin;
> +
> +      locus = &BLOCK_SOURCE_LOCATION (block);
> +      tree fndecl = NULL;
> +      block = BLOCK_SUPERCONTEXT (block);
> +      while (block && TREE_CODE (block) == BLOCK
> +            && BLOCK_ABSTRACT_ORIGIN (block))
> +       {
> +         tree ao = BLOCK_ABSTRACT_ORIGIN (block);
> +
> +         while (TREE_CODE (ao) == BLOCK
> +                && BLOCK_ABSTRACT_ORIGIN (ao)
> +                && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
> +           ao = BLOCK_ABSTRACT_ORIGIN (ao);
> +
> +         if (TREE_CODE (ao) == FUNCTION_DECL)
> +           {
> +             fndecl = ao;
> +             break;
> +           }
> +         else if (TREE_CODE (ao) != BLOCK)
> +           break;
> +
> +         block = BLOCK_SUPERCONTEXT (block);
> +       }
> +      if (fndecl)
> +       abstract_origin = block;
> +      else
> +       {
> +         while (block && TREE_CODE (block) == BLOCK)
> +           block = BLOCK_SUPERCONTEXT (block);
> +
> +         if (block && TREE_CODE (block) == FUNCTION_DECL)
> +           fndecl = block;
> +         abstract_origin = NULL;
> +       }
> +      if (fndecl)
> +       {
> +         json::object *obj = new json::object ();
> +         const char *printable_name
> +           = lang_hooks.decl_printable_name (fndecl, 2);
> +         obj->set ("fndecl", new json::string (printable_name));
> +         if (*locus != UNKNOWN_LOCATION)
> +           obj->set ("site", location_to_json (*locus));
> +         array->append (obj);
> +       }
> +    }
> +
> +  return array;
> +}
> +
> +/* Create a JSON object representing OPTINFO.  */
> +
> +json::object *
> +optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
> +{
> +  json::object *obj = new json::object ();
> +
> +  obj->set ("impl_location",
> +           impl_location_to_json (optinfo->get_impl_location ()));
> +
> +  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
> +  obj->set ("kind", new json::string (kind_str));
> +  json::array *message = new json::array ();
> +  obj->set ("message", message);
> +  for (unsigned i = 0; i < optinfo->num_items (); i++)
> +    {
> +      const optinfo_item *item = optinfo->get_item (i);
> +      switch (item->get_kind ())
> +       {
> +       default:
> +         gcc_unreachable ();
> +       case OPTINFO_ITEM_KIND_TEXT:
> +         {
> +           message->append (new json::string (item->get_text ()));
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_TREE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("expr", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the node.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +
> +           message->append (json_item);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_GIMPLE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("stmt", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the stmt.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +
> +           message->append (json_item);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_SYMTAB_NODE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("symtab_node", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the node.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +           message->append (json_item);
> +         }
> +         break;
> +       }
> +   }
> +
> +  if (optinfo->get_pass ())
> +    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
> +
> +  profile_count count = optinfo->get_count ();
> +  if (count.initialized_p ())
> +    obj->set ("count", profile_count_to_json (count));
> +
> +  /* Record any location, handling the case where of an UNKNOWN_LOCATION
> +     within an inlined block.  */
> +  location_t loc = optinfo->get_location_t ();
> +  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
> +    {
> +      // TOOD: record the location (just caret for now)
> +      // TODO: start/finish also?
> +      obj->set ("location", location_to_json (loc));
> +    }
> +
> +  if (current_function_decl)
> +    {
> +      const char *fnname = get_fnname_from_decl (current_function_decl);
> +      obj->set ("function", new json::string (fnname));
> +    }
> +
> +  if (loc != UNKNOWN_LOCATION)
> +    obj->set ("inlining_chain", inlining_chain_to_json (loc));
> +
> +  return obj;
> +}
> +
> +/* Add a json description of PASS and its siblings to ARR, recursing into
> +   child passes (adding their descriptions within a "children" array).  */
> +
> +void
> +optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
> +{
> +  do
> +    {
> +      json::object *pass_obj = pass_to_json (pass);
> +      arr->append (pass_obj);
> +      if (pass->sub)
> +       {
> +         json::array *sub = new json::array ();
> +         pass_obj->set ("children", sub);
> +         add_pass_list (sub, pass->sub);
> +       }
> +      pass = pass->next;
> +    }
> +  while (pass);
> +}
> +
> +/* File-level interface to rest of compiler (to avoid exposing
> +   class optrecord_json_writer outside of this file).  */
> +
> +static optrecord_json_writer *the_json_writer;
> +
> +/* Perform startup activity for -fsave-optimization-record.  */
> +
> +void
> +optimization_records_start ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!flag_save_optimization_record)
> +    return;
> +
> +  the_json_writer = new optrecord_json_writer ();
> +}
> +
> +/* Perform cleanup activity for -fsave-optimization-record.
> +
> +   Currently, the file is written out here in one go, before cleaning
> +   up.  */
> +
> +void
> +optimization_records_finish ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->write ();
> +
> +  delete the_json_writer;
> +  the_json_writer = NULL;
> +}
> +
> +/* Did the user request optimization records to be written out?  */
> +
> +bool
> +optimization_records_enabled_p ()
> +{
> +  return the_json_writer != NULL;
> +}
> +
> +/* If optimization records were requested, then add a record for OPTINFO
> +   to the queue of records to be written.  */
> +
> +void
> +optimization_records_maybe_record_optinfo (const optinfo *optinfo)
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->add_record (optinfo);
> +}
> +
> +/* Handling for the end of a dump scope for the
> +   optimization records sink.  */
> +
> +void
> +optimization_records_maybe_pop_dump_scope ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->pop_scope ();
> +}
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Verify that we can build a JSON optimization record from dump_*
> +   calls.  */
> +
> +static void
> +test_building_json_from_dump_calls ()
> +{
> +  temp_dump_context tmp (true);
> +  dump_location_t loc;
> +  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +  optinfo *info = tmp.get_pending_optinfo ();
> +  ASSERT_TRUE (info != NULL);
> +  ASSERT_EQ (info->num_items (), 2);
> +
> +  optrecord_json_writer writer;
> +  json::object *json_obj = writer.optinfo_to_json (info);
> +  ASSERT_TRUE (json_obj != NULL);
> +
> +  /* Verify that the json is sane.  */
> +  pretty_printer pp;
> +  json_obj->print (&pp);
> +  const char *json_str = pp_formatted_text (&pp);
> +  ASSERT_STR_CONTAINS (json_str, "impl_location");
> +  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
> +  ASSERT_STR_CONTAINS (json_str,
> +                      "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
> +  delete json_obj;
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +optinfo_emit_json_cc_tests ()
> +{
> +  test_building_json_from_dump_calls ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* CHECKING_P */
> diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
> new file mode 100644
> index 0000000..3628d56
> --- /dev/null
> +++ b/gcc/optinfo-emit-json.h
> @@ -0,0 +1,36 @@
> +/* Emit optimization information as JSON files.
> +   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_OPTINFO_EMIT_JSON_H
> +#define GCC_OPTINFO_EMIT_JSON_H
> +
> +class optinfo;
> +struct opt_pass;
> +
> +extern void optimization_records_start ();
> +extern void optimization_records_finish ();
> +
> +extern bool optimization_records_enabled_p ();
> +
> +extern void optimization_records_maybe_record_optinfo (const optinfo *);
> +extern void optimization_records_maybe_pop_dump_scope ();
> +
> +
> +#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> index 6f224bc..93de9d9 100644
> --- a/gcc/optinfo.cc
> +++ b/gcc/optinfo.cc
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h"
>
>  #include "optinfo.h"
> +#include "optinfo-emit-json.h"
>  #include "dump-context.h"
>  #include "pretty-print.h"
>  #include "gimple-pretty-print.h"
> @@ -85,7 +86,8 @@ optinfo::~optinfo ()
>  void
>  optinfo::emit ()
>  {
> -  /* currently this is a no-op.  */
> +  /* -fsave-optimization-record.  */
> +  optimization_records_maybe_record_optinfo (this);
>  }
>
>  /* Update the optinfo's kind based on DUMP_KIND.  */
> @@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn)
>
>  bool optinfo_enabled_p ()
>  {
> -  /* Currently no destinations are implemented, just a hook for
> -     selftests.  */
> -  return dump_context::get ().forcibly_enable_optinfo_p ();
> +  return (dump_context::get ().forcibly_enable_optinfo_p ()
> +         || optimization_records_enabled_p ());
>  }
>
>  /* Return true if any of the active optinfo destinations make use
> @@ -232,5 +233,5 @@ bool optinfo_enabled_p ()
>
>  bool optinfo_wants_inlining_info_p ()
>  {
> -  return false;
> +  return optimization_records_enabled_p ();
>  }
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> index 5bdb9eb..5f09022 100644
> --- a/gcc/optinfo.h
> +++ b/gcc/optinfo.h
> @@ -25,12 +25,8 @@ along with GCC; see the file COPYING3.  If not see
>     optimization, which can be emitted to zero or more of several
>     destinations, such as:
>
> -   * as a "remark" through the diagnostics subsystem
> -
>     * saved to a file as an "optimization record"
>
> -   Currently no such destinations are implemented.
> -
>     They are generated in response to calls to the "dump_*" API in
>     dumpfile.h; repeated calls to the "dump_*" API are consolidated
>     into a pending optinfo instance, with a "dump_*_loc" starting a new
> diff --git a/gcc/profile-count.c b/gcc/profile-count.c
> index 3d411cf..6a17f5e 100644
> --- a/gcc/profile-count.c
> +++ b/gcc/profile-count.c
> @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
>  #include "wide-int.h"
>  #include "sreal.h"
>
> +/* Get a string describing QUALITY.  */
> +
> +const char *
> +profile_quality_as_string (enum profile_quality quality)
> +{
> +  switch (quality)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case profile_uninitialized:
> +      return "uninitialized";
> +    case profile_guessed_local:
> +      return "guessed_local";
> +    case profile_guessed_global0:
> +      return "guessed_global0";
> +    case profile_guessed_global0adjusted:
> +      return "guessed_global0adjusted";
> +    case profile_guessed:
> +      return "guessed";
> +    case profile_afdo:
> +      return "afdo";
> +    case profile_adjusted:
> +      return "adjusted";
> +    case profile_precise:
> +      return "precise";
> +    }
> +}
> +
>  /* Dump THIS to F.  */
>
>  void
> diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> index c83fa3b..f4d0c340 100644
> --- a/gcc/profile-count.h
> +++ b/gcc/profile-count.h
> @@ -59,6 +59,8 @@ enum profile_quality {
>    profile_precise
>  };
>
> +extern const char *profile_quality_as_string (enum profile_quality);
> +
>  /* The base value for branch probability notes and edge probabilities.  */
>  #define REG_BR_PROB_BASE  10000
>
> @@ -721,6 +723,9 @@ public:
>        return m_quality == profile_precise;
>      }
>
> +  /* Get the quality of the count.  */
> +  enum profile_quality quality () const { return m_quality; }
> +
>    /* When merging basic blocks, the two different profile counts are unified.
>       Return true if this can be done without losing info about profile.
>       The only case we care about here is when first BB contains something
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index 7f4d6f3..5adb033 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -72,6 +72,8 @@ selftest::run_tests ()
>    typed_splay_tree_c_tests ();
>    unique_ptr_tests_cc_tests ();
>    opt_proposer_c_tests ();
> +  json_cc_tests ();
> +  optinfo_emit_json_cc_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index 54fc488..ede7732 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -228,6 +228,8 @@ extern void gimple_c_tests ();
>  extern void hash_map_tests_c_tests ();
>  extern void hash_set_tests_c_tests ();
>  extern void input_c_tests ();
> +extern void json_cc_tests ();
> +extern void optinfo_emit_json_cc_tests ();
>  extern void predict_c_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index d108096..a047390 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tree-pass.h"
>  #include "dumpfile.h"
>  #include "ipa-fnsummary.h"
> +#include "optinfo-emit-json.h"
>
>  #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
>  #include "dbxout.h"
> @@ -487,6 +488,8 @@ compile_file (void)
>    if (lang_hooks.decls.post_compilation_parsing_cleanups)
>      lang_hooks.decls.post_compilation_parsing_cleanups ();
>
> +  optimization_records_finish ();
> +
>    if (seen_error ())
>      return;
>
> @@ -2048,6 +2051,8 @@ do_compile ()
>
>        timevar_start (TV_PHASE_SETUP);
>
> +      optimization_records_start ();
> +
>        /* This must be run always, because it is needed to compute the FP
>          predefined macros, such as __LDBL_MAX__, for targets using non
>          default FP formats.  */
> diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
> index 7447f7a..2623d9b 100644
> --- a/gcc/tree-ssa-live.c
> +++ b/gcc/tree-ssa-live.c
> @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cfgloop.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "optinfo.h"
>
>  static void verify_live_on_entry (tree_live_info_p);
>
> @@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
>       ;
>     /* When not generating debug info we can eliminate info on unused
>        variables.  */
> -   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
> +   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
> +           && !optinfo_wants_inlining_info_p ())
>       {
>         /* Even for -g0 don't prune outer scopes from artificial
>           functions, otherwise diagnostics using tree_nonartificial_location
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/2] Add "-fsave-optimization-record"
  2018-07-19 12:39                                   ` Richard Biener
@ 2018-07-20 15:45                                     ` David Malcolm
  0 siblings, 0 replies; 80+ messages in thread
From: David Malcolm @ 2018-07-20 15:45 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Thu, 2018-07-19 at 14:39 +0200, Richard Biener wrote:
> On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This patch implements a -fsave-optimization-record option, which
> > leads to a JSON file being written out, recording the dump_* calls
> > made (via the optinfo infrastructure in the previous patch).
> > 
> > The patch includes a minimal version of the JSON patch I posted
> > last
> > year, with just enough support needed for optimization records (I
> > removed all of the parser code, leaving just the code for building
> > in-memory JSON trees and writing them to a pretty_printer).
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > 
> > OK for trunk?
> 
> +@item -fsave-optimization-record
> +@opindex fsave-optimization-record
> +Write a SRCFILE.opt-record.json file detailing what optimizations
> +were performed.
> +
> 
> I guess this should note that it is experimental and in no way
> complete.  Maybe list areas where reports will be generated,
> like vectorization?

Indeed. I also noticed that for some reason my patch had this option
between
  @item -frounding-math
and
  @item -fsignaling-nans
in the section on floating-point arithmetic options, so I've moved it
to the "GCC Developer Options" section for now, immediately after the
documentation of -fopt-info.

Here's what I've committed to invoke.texi (r262905):

BEGIN QUOTE:

@item -fsave-optimization-record
@opindex fsave-optimization-record
Write a SRCFILE.opt-record.json file detailing what optimizations
were performed, for those optimizations that support @option{-fopt-info}.

This option is experimental and the format of the data within the JSON
file is subject to change.

It is roughly equivalent to a machine-readable version of
@option{-fopt-info-all}, as a collection of messages with source file,
line number and column number, with the following additional data for
each message:

@itemize @bullet

@item
the execution count of the code being optimized, along with metadata about
whether this was from actual profile data, or just an estimate, allowing
consumers to prioritize messages by code hotness,

@item
the function name of the code being optimized, where applicable,

@item
the ``inlining chain'' for the code being optimized, so that when
a function is inlined into several different places (which might
themselves be inlined), the reader can distinguish between the copies,

@item
objects identifying those parts of the message that refer to expressions,
statements or symbol-table nodes, which of these categories they are, and,
when available, their source code location,

@item
the GCC pass that emitted the message, and

@item
the location in GCC's own code from which the message was emitted

@end itemize

Additionally, some messages are logically nested within other
messages, reflecting implementation details of the optimization
passes.

END QUOTE


> Did you check what happens with -flto -fsave-optimization-record?
> Will the compile-phase emit a json file for each source (expected,
> like with early inlining decisions)?  Will the WPA phase emit one
> (IPA decisions?) or will IPA decisions be recorded in the LTRANS
> one?  How will the LTRANS ones be named and where can they
> be found?  You don't need to solve all the issues with this patch
> but they should be eventually addressed somehow.

I'm looking at that now.

> I don't question the use or implementation of JSON, I'll just
> approve it.
> 
> The rest looks obvious enough, thus OK.

> 
> Some overall blurb in the documentation or changes.html
> on how to use this would be nice of course.

Thanks; will do.

Dave

> Thanks,
> Richard.
> 
> 
> > gcc/ChangeLog:
> >         * Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
> >         (CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
> >         * common.opt (fsave-optimization-record): New option.
> >         * coretypes.h (struct kv_pair): Move here from dumpfile.c.
> >         * doc/invoke.texi (-fsave-optimization-record): New option.
> >         * dumpfile.c: Include "optinfo-emit-json.h".
> >         (struct kv_pair): Move to coretypes.h.
> >         (optgroup_options): Make non-static.
> >         (dump_context::end_scope): Call
> >         optimization_records_maybe_pop_dump_scope.
> >         * dumpfile.h (optgroup_options): New decl.
> >         * json.cc: New file.
> >         * json.h: New file.
> >         * optinfo-emit-json.cc: New file.
> >         * optinfo-emit-json.h: New file.
> >         * optinfo.cc: Include "optinfo-emit-json.h".
> >         (optinfo::emit): Call
> > optimization_records_maybe_record_optinfo.
> >         (optinfo_enabled_p): Check optimization_records_enabled_p.
> >         (optinfo_wants_inlining_info_p): Likewise.
> >         * optinfo.h: Update comment.
> >         * profile-count.c (profile_quality_as_string): New
> > function.
> >         * profile-count.h (profile_quality_as_string): New decl.
> >         (profile_count::quality): New accessor.
> >         * selftest-run-tests.c (selftest::run_tests): Call
> > json_cc_tests
> >         and optinfo_emit_json_cc_tests.
> >         * selftest.h (selftest::json_cc_tests): New decl.
> >         (selftest::optinfo_emit_json_cc_tests): New decl.
> >         * toplev.c: Include "optinfo-emit-json.h".
> >         (compile_file): Call optimization_records_finish.
> >         (do_compile): Call optimization_records_start.
> >         * tree-ssa-live.c: Include optinfo.h.
> >         (remove_unused_scope_block_p): Retain inlining information
> > if
> >         optinfo_wants_inlining_info_p returns true.
> > ---
> >  gcc/Makefile.in          |   3 +
> >  gcc/common.opt           |   4 +
> >  gcc/coretypes.h          |   8 +
> >  gcc/doc/invoke.texi      |   8 +-
> >  gcc/dumpfile.c           |  15 +-
> >  gcc/dumpfile.h           |   3 +
> >  gcc/json.cc              | 293 ++++++++++++++++++++++++
> >  gcc/json.h               | 166 ++++++++++++++
> >  gcc/optinfo-emit-json.cc | 568
> > +++++++++++++++++++++++++++++++++++++++++++++++
> >  gcc/optinfo-emit-json.h  |  36 +++
> >  gcc/optinfo.cc           |  11 +-
> >  gcc/optinfo.h            |   4 -
> >  gcc/profile-count.c      |  28 +++
> >  gcc/profile-count.h      |   5 +
> >  gcc/selftest-run-tests.c |   2 +
> >  gcc/selftest.h           |   2 +
> >  gcc/toplev.c             |   5 +
> >  gcc/tree-ssa-live.c      |   4 +-
> >  18 files changed, 1143 insertions(+), 22 deletions(-)
> >  create mode 100644 gcc/json.cc
> >  create mode 100644 gcc/json.h
> >  create mode 100644 gcc/optinfo-emit-json.cc
> >  create mode 100644 gcc/optinfo-emit-json.h
> > 
> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > index dd1dfc1..b871640 100644
> > --- a/gcc/Makefile.in
> > +++ b/gcc/Makefile.in
> > @@ -1387,6 +1387,7 @@ OBJS = \
> >         ira-color.o \
> >         ira-emit.o \
> >         ira-lives.o \
> > +       json.o \
> >         jump.o \
> >         langhooks.o \
> >         lcm.o \
> > @@ -1428,6 +1429,7 @@ OBJS = \
> >         optabs-query.o \
> >         optabs-tree.o \
> >         optinfo.o \
> > +       optinfo-emit-json.o \
> >         options-save.o \
> >         opts-global.o \
> >         passes.o \
> > @@ -2251,6 +2253,7 @@ s-bversion: BASE-VER
> >         $(STAMP) s-bversion
> > 
> >  CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
> > +CFLAGS-optinfo-emit-json.o +=
> > -DTARGET_NAME=\"$(target_noncanonical)\"
> > 
> >  pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
> >                     $(srcdir)/gen-pass-instances.awk
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 5a50bc27..a13c709 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -1950,6 +1950,10 @@ fopt-info-
> >  Common Joined RejectNegative Var(common_deferred_options) Defer
> >  -fopt-info[-<type>=filename]   Dump compiler optimization details.
> > 
> > +fsave-optimization-record
> > +Common Report Var(flag_save_optimization_record) Optimization
> > +Write a SRCFILE.opt-record.json file detailing what optimizations
> > were performed.
> > +
> >  foptimize-register-move
> >  Common Ignore
> >  Does nothing. Preserved for backward compatibility.
> > diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> > index ed0e825..2fd20e4 100644
> > --- a/gcc/coretypes.h
> > +++ b/gcc/coretypes.h
> > @@ -332,6 +332,14 @@ namespace gcc {
> > 
> >  typedef std::pair <tree, tree> tree_pair;
> > 
> > +/* Define a name->value mapping.  */
> > +template <typename ValueType>
> > +struct kv_pair
> > +{
> > +  const char *const name;      /* the name of the value */
> > +  const ValueType value;       /* the value of the name */
> > +};
> > +
> >  #else
> > 
> >  struct _dont_use_rtx_here_;
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 56cd122..c239c53 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
> >  -freorder-blocks-algorithm=@var{algorithm} @gol
> >  -freorder-blocks-and-partition  -freorder-functions @gol
> >  -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
> > --frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
> > +-frounding-math  -fsave-optimization-record @gol
> > +-fsched2-use-superblocks  -fsched-pressure @gol
> >  -fsched-spec-load  -fsched-spec-load-dangerous @gol
> >  -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n
> > }] @gol
> >  -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
> > @@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer
> > control of this setting
> >  using C99's @code{FENV_ACCESS} pragma.  This command-line option
> >  will be used to specify the default state for @code{FENV_ACCESS}.
> > 
> > +@item -fsave-optimization-record
> > +@opindex fsave-optimization-record
> > +Write a SRCFILE.opt-record.json file detailing what optimizations
> > +were performed.
> > +
> >  @item -fsignaling-nans
> >  @opindex fsignaling-nans
> >  Compile code assuming that IEEE signaling NaNs may generate user-
> > visible
> > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > index 38f9539..331b7fb 100644
> > --- a/gcc/dumpfile.c
> > +++ b/gcc/dumpfile.c
> > @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "dump-context.h"
> >  #include "cgraph.h"
> >  #include "tree-pass.h" /* for "current_pass".  */
> > +#include "optinfo-emit-json.h"
> > 
> >  /* If non-NULL, return one past-the-end of the matching SUBPART of
> >     the WHOLE string.  */
> > @@ -118,14 +119,6 @@ static struct dump_file_info
> > dump_files[TDI_end] =
> >    DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
> >  };
> > 
> > -/* Define a name->number mapping for a dump flag value.  */
> > -template <typename ValueType>
> > -struct kv_pair
> > -{
> > -  const char *const name;      /* the name of the value */
> > -  const ValueType value;       /* the value of the name */
> > -};
> > -
> >  /* Table of dump options. This must be consistent with the TDF_*
> > flags
> >     in dumpfile.h and opt_info_options below. */
> >  static const kv_pair<dump_flags_t> dump_options[] =
> > @@ -176,7 +169,7 @@ static const kv_pair<dump_flags_t>
> > optinfo_verbosity_options[] =
> >  };
> > 
> >  /* Flags used for -fopt-info groups.  */
> > -static const kv_pair<optgroup_flags_t> optgroup_options[] =
> > +const kv_pair<optgroup_flags_t> optgroup_options[] =
> >  {
> >    {"ipa", OPTGROUP_IPA},
> >    {"loop", OPTGROUP_LOOP},
> > @@ -794,6 +787,7 @@ dump_context::end_scope ()
> >  {
> >    end_any_optinfo ();
> >    m_scope_depth--;
> > +  optimization_records_maybe_pop_dump_scope ();
> >  }
> > 
> >  /* Return the optinfo currently being accumulated, creating one if
> > @@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const
> > dump_location_t &loc)
> >  }
> > 
> >  /* End any optinfo that has been accumulated within this context;
> > emitting
> > -   it to any destinations as appropriate - though none have
> > currently been
> > -   implemented.  */
> > +   it to any destinations as appropriate, such as optimization
> > records.  */
> > 
> >  void
> >  dump_context::end_any_optinfo ()
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 3096a89..e4823f8 100644
> > --- a/gcc/dumpfile.h
> > +++ b/gcc/dumpfile.h
> > @@ -420,6 +420,7 @@ extern FILE *dump_begin (int, dump_flags_t *);
> >  extern void dump_end (int, FILE *);
> >  extern int opt_info_switch_p (const char *);
> >  extern const char *dump_flag_name (int);
> > +extern const kv_pair<optgroup_flags_t> optgroup_options[];
> > 
> >  /* Global variables used to communicate with passes.  */
> >  extern FILE *dump_file;
> > @@ -442,6 +443,7 @@ dump_enabled_p (void)
> >     (a) the active dump_file, if any
> >     (b) the -fopt-info destination, if any
> >     (c) to the "optinfo" destinations, if any:
> > +       (c.1) as optimization records
> > 
> >     dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> >                                     |
> > @@ -449,6 +451,7 @@ dump_enabled_p (void)
> >                                     |
> >                                     `--> (c) optinfo
> >                                              `---> optinfo
> > destinations
> > +                                                  (c.1)
> > optimization records
> > 
> >     For optinfos, the dump_*_loc mark the beginning of an optinfo
> >     instance: all subsequent dump_* calls are consolidated into
> > diff --git a/gcc/json.cc b/gcc/json.cc
> > new file mode 100644
> > index 0000000..3c2aa77
> > --- /dev/null
> > +++ b/gcc/json.cc
> > @@ -0,0 +1,293 @@
> > +/* JSON trees
> > +   Copyright (C) 2017-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/>.  */
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +#include "json.h"
> > +#include "pretty-print.h"
> > +#include "math.h"
> > +#include "selftest.h"
> > +
> > +using namespace json;
> > +
> > +/* class json::value.  */
> > +
> > +/* Dump this json::value tree to OUTF.
> > +   No formatting is done.  There are no guarantees about the order
> > +   in which the key/value pairs of json::objects are printed.  */
> > +
> > +void
> > +value::dump (FILE *outf) const
> > +{
> > +  pretty_printer pp;
> > +  pp_buffer (&pp)->stream = outf;
> > +  print (&pp);
> > +  pp_flush (&pp);
> > +}
> > +
> > +/* class json::object, a subclass of json::value, representing
> > +   an unordered collection of key/value pairs.  */
> > +
> > +/* json:object's dtor.  */
> > +
> > +object::~object ()
> > +{
> > +  for (map_t::iterator it = m_map.begin (); it != m_map.end ();
> > ++it)
> > +    {
> > +      free (const_cast <char *>((*it).first));
> > +      delete ((*it).second);
> > +    }
> > +}
> > +
> > +/* Implementation of json::value::print for json::object.  */
> > +
> > +void
> > +object::print (pretty_printer *pp) const
> > +{
> > +  /* Note that the order is not guaranteed.  */
> > +  pp_character (pp, '{');
> > +  for (map_t::iterator it = m_map.begin (); it != m_map.end ();
> > ++it)
> > +    {
> > +      if (it != m_map.begin ())
> > +       pp_string (pp, ", ");
> > +      const char *key = const_cast <char *>((*it).first);
> > +      value *value = (*it).second;
> > +      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
> > +      value->print (pp);
> > +    }
> > +  pp_character (pp, '}');
> > +}
> > +
> > +/* Set the json::value * for KEY, taking ownership of VALUE
> > +   (and taking a copy of KEY if necessary).  */
> > +
> > +void
> > +object::set (const char *key, value *v)
> > +{
> > +  value **ptr = m_map.get (key);
> > +  if (ptr)
> > +    {
> > +      /* If the key is already present, delete the existing value
> > +        and overwrite it.  */
> > +      delete *ptr;
> > +      *ptr = v;
> > +    }
> > +  else
> > +    /* If the key wasn't already present, take a copy of the key,
> > +       and store the value.  */
> > +    m_map.put (xstrdup (key), v);
> > +}
> > +
> > +/* class json::array, a subclass of json::value, representing
> > +   an ordered collection of values.  */
> > +
> > +/* json::array's dtor.  */
> > +
> > +array::~array ()
> > +{
> > +  unsigned i;
> > +  value *v;
> > +  FOR_EACH_VEC_ELT (m_elements, i, v)
> > +    delete v;
> > +}
> > +
> > +/* Implementation of json::value::print for json::array.  */
> > +
> > +void
> > +array::print (pretty_printer *pp) const
> > +{
> > +  pp_character (pp, '[');
> > +  unsigned i;
> > +  value *v;
> > +  FOR_EACH_VEC_ELT (m_elements, i, v)
> > +    {
> > +      if (i)
> > +       pp_string (pp, ", ");
> > +      v->print (pp);
> > +    }
> > +  pp_character (pp, ']');
> > +}
> > +
> > +/* class json::number, a subclass of json::value, wrapping a
> > double.  */
> > +
> > +/* Implementation of json::value::print for json::number.  */
> > +
> > +void
> > +number::print (pretty_printer *pp) const
> > +{
> > +  char tmp[1024];
> > +  snprintf (tmp, sizeof (tmp), "%g", m_value);
> > +  pp_string (pp, tmp);
> > +}
> > +
> > +/* class json::string, a subclass of json::value.  */
> > +
> > +void
> > +string::print (pretty_printer *pp) const
> > +{
> > +  pp_character (pp, '"');
> > +  for (const char *ptr = m_utf8; *ptr; ptr++)
> > +    {
> > +      char ch = *ptr;
> > +      switch (ch)
> > +       {
> > +       case '"':
> > +         pp_string (pp, "\\\"");
> > +         break;
> > +       case '\\':
> > +         pp_string (pp, "\\n");
> > +         break;
> > +       case '\b':
> > +         pp_string (pp, "\\b");
> > +         break;
> > +       case '\f':
> > +         pp_string (pp, "\\f");
> > +         break;
> > +       case '\n':
> > +         pp_string (pp, "\\n");
> > +         break;
> > +       case '\r':
> > +         pp_string (pp, "\\r");
> > +         break;
> > +       case '\t':
> > +         pp_string (pp, "\\t");
> > +         break;
> > +
> > +       default:
> > +         pp_character (pp, ch);
> > +       }
> > +    }
> > +  pp_character (pp, '"');
> > +}
> > +
> > +/* class json::literal, a subclass of json::value.  */
> > +
> > +/* Implementation of json::value::print for json::literal.  */
> > +
> > +void
> > +literal::print (pretty_printer *pp) const
> > +{
> > +  switch (m_kind)
> > +    {
> > +    case JSON_TRUE:
> > +      pp_string (pp, "true");
> > +      break;
> > +    case JSON_FALSE:
> > +      pp_string (pp, "false");
> > +      break;
> > +    case JSON_NULL:
> > +      pp_string (pp, "null");
> > +      break;
> > +    default:
> > +      gcc_unreachable ();
> > +    }
> > +}
> > +
> > +
> > +#if CHECKING_P
> > +
> > +namespace selftest {
> > +
> > +/* Selftests.  */
> > +
> > +/* Verify that JV->print () prints EXPECTED_JSON.  */
> > +
> > +static void
> > +assert_print_eq (const json::value &jv, const char *expected_json)
> > +{
> > +  pretty_printer pp;
> > +  jv.print (&pp);
> > +  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
> > +}
> > +
> > +/* Verify that JSON objects are written correctly.  We can't test
> > more than
> > +   one key/value pair, as we don't impose a guaranteed
> > ordering.  */
> > +
> > +static void
> > +test_writing_objects ()
> > +{
> > +  object obj;
> > +  obj.set ("foo", new json::string ("bar"));
> > +  assert_print_eq (obj, "{\"foo\": \"bar\"}");
> > +}
> > +
> > +/* Verify that JSON arrays are written correctly.  */
> > +
> > +static void
> > +test_writing_arrays ()
> > +{
> > +  array arr;
> > +  assert_print_eq (arr, "[]");
> > +
> > +  arr.append (new json::string ("foo"));
> > +  assert_print_eq (arr, "[\"foo\"]");
> > +
> > +  arr.append (new json::string ("bar"));
> > +  assert_print_eq (arr, "[\"foo\", \"bar\"]");
> > +}
> > +
> > +/* Verify that JSON numbers are written correctly.  */
> > +
> > +static void
> > +test_writing_numbers ()
> > +{
> > +  assert_print_eq (number (0), "0");
> > +  assert_print_eq (number (42), "42");
> > +  assert_print_eq (number (-100), "-100");
> > +}
> > +
> > +/* Verify that JSON strings are written correctly.  */
> > +
> > +static void
> > +test_writing_strings ()
> > +{
> > +  string foo ("foo");
> > +  assert_print_eq (foo, "\"foo\"");
> > +
> > +  string contains_quotes ("before \"quoted\" after");
> > +  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\"
> > after\"");
> > +}
> > +
> > +/* Verify that JSON strings are written correctly.  */
> > +
> > +static void
> > +test_writing_literals ()
> > +{
> > +  assert_print_eq (literal (JSON_TRUE), "true");
> > +  assert_print_eq (literal (JSON_FALSE), "false");
> > +  assert_print_eq (literal (JSON_NULL), "null");
> > +}
> > +
> > +/* Run all of the selftests within this file.  */
> > +
> > +void
> > +json_cc_tests ()
> > +{
> > +  test_writing_objects ();
> > +  test_writing_arrays ();
> > +  test_writing_numbers ();
> > +  test_writing_strings ();
> > +  test_writing_literals ();
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* #if CHECKING_P */
> > diff --git a/gcc/json.h b/gcc/json.h
> > new file mode 100644
> > index 0000000..5c3274c
> > --- /dev/null
> > +++ b/gcc/json.h
> > @@ -0,0 +1,166 @@
> > +/* JSON trees
> > +   Copyright (C) 2017-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_JSON_H
> > +#define GCC_JSON_H
> > +
> > +/* Implementation of JSON, a lightweight data-interchange format.
> > +
> > +   See http://www.json.org/
> > +   and http://www.ecma-international.org/publications/files/ECMA-S
> > T/ECMA-404.pdf
> > +   and https://tools.ietf.org/html/rfc7159
> > +
> > +   Supports creating a DOM-like tree of json::value *, and then
> > dumping
> > +   json::value * to text.  */
> > +
> > +namespace json
> > +{
> > +
> > +/* Forward decls of json::value and its subclasses (using
> > indentation
> > +   to denote inheritance.  */
> > +
> > +class value;
> > +  class object;
> > +  class array;
> > +  class number;
> > +  class string;
> > +  class literal;
> > +
> > +/* An enum for discriminating the subclasses of json::value.  */
> > +
> > +enum kind
> > +{
> > +  /* class json::object.  */
> > +  JSON_OBJECT,
> > +
> > +  /* class json::array.  */
> > +  JSON_ARRAY,
> > +
> > +  /* class json::number.  */
> > +  JSON_NUMBER,
> > +
> > +  /* class json::string.  */
> > +  JSON_STRING,
> > +
> > +  /* class json::literal uses these three values to identify the
> > +     particular literal.  */
> > +  JSON_TRUE,
> > +  JSON_FALSE,
> > +  JSON_NULL
> > +};
> > +
> > +/* Base class of JSON value.  */
> > +
> > +class value
> > +{
> > + public:
> > +  virtual ~value () {}
> > +  virtual enum kind get_kind () const = 0;
> > +  virtual void print (pretty_printer *pp) const = 0;
> > +
> > +  void dump (FILE *) const;
> > +};
> > +
> > +/* Subclass of value for objects: an unordered collection of
> > +   key/value pairs.  */
> > +
> > +class object : public value
> > +{
> > + public:
> > +  ~object ();
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  void set (const char *key, value *v);
> > +
> > + private:
> > +  typedef hash_map <char *, value *,
> > +    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
> > +  map_t m_map;
> > +};
> > +
> > +/* Subclass of value for arrays.  */
> > +
> > +class array : public value
> > +{
> > + public:
> > +  ~array ();
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  void append (value *v) { m_elements.safe_push (v); }
> > +
> > + private:
> > +  auto_vec<value *> m_elements;
> > +};
> > +
> > +/* Subclass of value for numbers.  */
> > +
> > +class number : public value
> > +{
> > + public:
> > +  number (double value) : m_value (value) {}
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  double get () const { return m_value; }
> > +
> > + private:
> > +  double m_value;
> > +};
> > +
> > +/* Subclass of value for strings.  */
> > +
> > +class string : public value
> > +{
> > + public:
> > +  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
> > +  ~string () { free (m_utf8); }
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  const char *get_string () const { return m_utf8; }
> > +
> > + private:
> > +  char *m_utf8;
> > +};
> > +
> > +/* Subclass of value for the three JSON literals "true", "false",
> > +   and "null".  */
> > +
> > +class literal : public value
> > +{
> > + public:
> > +  literal (enum kind kind) : m_kind (kind) {}
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > + private:
> > +  enum kind m_kind;
> > +};
> > +
> > +} // namespace json
> > +
> > +#endif  /* GCC_JSON_H  */
> > diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> > new file mode 100644
> > index 0000000..bf1172a
> > --- /dev/null
> > +++ b/gcc/optinfo-emit-json.cc
> > @@ -0,0 +1,568 @@
> > +/* Emit optimization information as JSON files.
> > +   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/>.  */
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +
> > +#include "backend.h"
> > +#include "tree.h"
> > +#include "gimple.h"
> > +#include "diagnostic-core.h"
> > +
> > +#include "profile.h"
> > +#include "output.h"
> > +#include "tree-pass.h"
> > +
> > +#include "optinfo.h"
> > +#include "optinfo-emit-json.h"
> > +#include "json.h"
> > +#include "pretty-print.h"
> > +#include "tree-pretty-print.h"
> > +#include "gimple-pretty-print.h"
> > +#include "cgraph.h"
> > +
> > +#include "langhooks.h"
> > +#include "version.h"
> > +#include "context.h"
> > +#include "pass_manager.h"
> > +#include "selftest.h"
> > +#include "dump-context.h"
> > +
> > +/* A class for writing out optimization records in JSON
> > format.  */
> > +
> > +class optrecord_json_writer
> > +{
> > +public:
> > +  optrecord_json_writer ();
> > +  ~optrecord_json_writer ();
> > +  void write () const;
> > +  void add_record (const optinfo *optinfo);
> > +  void pop_scope ();
> > +
> > +  void add_record (json::object *obj);
> > +  json::object *impl_location_to_json (dump_impl_location_t loc);
> > +  json::object *location_to_json (location_t loc);
> > +  json::object *profile_count_to_json (profile_count count);
> > +  json::string *get_id_value_for_pass (opt_pass *pass);
> > +  json::object *pass_to_json (opt_pass *pass);
> > +  json::value *inlining_chain_to_json (location_t loc);
> > +  json::object *optinfo_to_json (const optinfo *optinfo);
> > +  void add_pass_list (json::array *arr, opt_pass *pass);
> > +
> > +private:
> > +  /* The root value for the JSON file.
> > +     Currently the JSON values are stored in memory, and flushed
> > when the
> > +     compiler exits.  It would probably be better to simply write
> > out
> > +     the JSON as we go.  */
> > +  json::array *m_root_tuple;
> > +
> > +  /* The currently open scopes, for expressing nested optimization
> > records.  */
> > +  vec<json::array *> m_scopes;
> > +};
> > +
> > +/* optrecord_json_writer's ctor.  Populate the top-level parts of
> > the
> > +   in-memory JSON representation.  */
> > +
> > +optrecord_json_writer::optrecord_json_writer ()
> > +  : m_root_tuple (NULL), m_scopes ()
> > +{
> > +  m_root_tuple = new json::array ();
> > +
> > +  /* Populate with metadata; compare with toplev.c:
> > print_version.  */
> > +  json::object *metadata = new json::object ();
> > +  m_root_tuple->append (metadata);
> > +  metadata->set ("format", new json::string ("1"));
> > +  json::object *generator = new json::object ();
> > +  metadata->set ("generator", generator);
> > +  generator->set ("name", new json::string (lang_hooks.name));
> > +  generator->set ("pkgversion", new json::string
> > (pkgversion_string));
> > +  generator->set ("version", new json::string (version_string));
> > +  /* TARGET_NAME is passed in by the Makefile.  */
> > +  generator->set ("target", new json::string (TARGET_NAME));
> > +
> > +  /* TODO: capture command-line?
> > +     see gen_producer_string in dwarf2out.c (currently
> > static).  */
> > +
> > +  /* TODO: capture "any plugins?" flag (or the plugins
> > themselves).  */
> > +
> > +  json::array *passes = new json::array ();
> > +  m_root_tuple->append (passes);
> > +
> > +  /* Call add_pass_list for all of the pass lists.  */
> > +  {
> > +#define DEF_PASS_LIST(LIST) \
> > +    add_pass_list (passes, g->get_passes ()->LIST);
> > +    GCC_PASS_LISTS
> > +#undef DEF_PASS_LIST
> > +  }
> > +
> > +  json::array *records = new json::array ();
> > +  m_root_tuple->append (records);
> > +
> > +  m_scopes.safe_push (records);
> > +}
> > +
> > +/* optrecord_json_writer's ctor.
> > +   Delete the in-memory JSON representation.  */
> > +
> > +optrecord_json_writer::~optrecord_json_writer ()
> > +{
> > +  delete m_root_tuple;
> > +}
> > +
> > +/* Choose an appropriate filename, and write the saved records to
> > it.  */
> > +
> > +void
> > +optrecord_json_writer::write () const
> > +{
> > +  char *filename = concat (dump_base_name, ".opt-record.json",
> > NULL);
> > +  FILE *outfile = fopen (filename, "w");
> > +  if (outfile)
> > +    {
> > +      m_root_tuple->dump (outfile);
> > +      fclose (outfile);
> > +    }
> > +  else
> > +    error_at (UNKNOWN_LOCATION, "unable to write optimization
> > records to %qs",
> > +             filename); // FIXME: more info?
> > +  free (filename);
> > +}
> > +
> > +/* Add a record for OPTINFO to the queue of records to be
> > written.  */
> > +
> > +void
> > +optrecord_json_writer::add_record (const optinfo *optinfo)
> > +{
> > +  json::object *obj = optinfo_to_json (optinfo);
> > +
> > +  add_record (obj);
> > +
> > +  /* Potentially push the scope.  */
> > +  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
> > +    {
> > +      json::array *children = new json::array ();
> > +      obj->set ("children", children);
> > +      m_scopes.safe_push (children);
> > +    }
> > +}
> > +
> > +/* Private methods of optrecord_json_writer.  */
> > +
> > +/* Add record OBJ to the the innermost scope.  */
> > +
> > +void
> > +optrecord_json_writer::add_record (json::object *obj)
> > +{
> > +  /* Add to innermost scope.  */
> > +  gcc_assert (m_scopes.length () > 0);
> > +  m_scopes[m_scopes.length () - 1]->append (obj);
> > +}
> > +
> > +/* Pop the innermost scope.  */
> > +
> > +void
> > +optrecord_json_writer::pop_scope ()
> > +{
> > +  m_scopes.pop ();
> > +}
> > +
> > +/* Create a JSON object representing LOC.  */
> > +
> > +json::object *
> > +optrecord_json_writer::impl_location_to_json (dump_impl_location_t
> > loc)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("file", new json::string (loc.m_file));
> > +  obj->set ("line", new json::number (loc.m_line));
> > +  if (loc.m_function)
> > +    obj->set ("function", new json::string (loc.m_function));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON object representing LOC.  */
> > +
> > +json::object *
> > +optrecord_json_writer::location_to_json (location_t loc)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("file", new json::string (LOCATION_FILE (loc)));
> > +  obj->set ("line", new json::number (LOCATION_LINE (loc)));
> > +  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON object representing COUNT.  */
> > +
> > +json::object *
> > +optrecord_json_writer::profile_count_to_json (profile_count count)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("value", new json::number (count.to_gcov_type ()));
> > +  obj->set ("quality",
> > +           new json::string (profile_quality_as_string
> > (count.quality ())));
> > +  return obj;
> > +}
> > +
> > +/* Get a string for use when referring to PASS in the saved
> > optimization
> > +   records.  */
> > +
> > +json::string *
> > +optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
> > +{
> > +  pretty_printer pp;
> > +  /* this is host-dependent, but will be consistent for a given
> > host.  */
> > +  pp_pointer (&pp, static_cast<void *> (pass));
> > +  return new json::string (pp_formatted_text (&pp));
> > +}
> > +
> > +/* Create a JSON object representing PASS.  */
> > +
> > +json::object *
> > +optrecord_json_writer::pass_to_json (opt_pass *pass)
> > +{
> > +  json::object *obj = new json::object ();
> > +  const char *type = NULL;
> > +  switch (pass->type)
> > +    {
> > +    default:
> > +      gcc_unreachable ();
> > +    case GIMPLE_PASS:
> > +      type = "gimple";
> > +      break;
> > +    case RTL_PASS:
> > +      type = "rtl";
> > +      break;
> > +    case SIMPLE_IPA_PASS:
> > +      type = "simple_ipa";
> > +      break;
> > +    case IPA_PASS:
> > +      type = "ipa";
> > +      break;
> > +    }
> > +  obj->set ("id", get_id_value_for_pass (pass));
> > +  obj->set ("type", new json::string (type));
> > +  obj->set ("name", new json::string (pass->name));
> > +  /* Represent the optgroup flags as an array.  */
> > +  {
> > +    json::array *optgroups = new json::array ();
> > +    obj->set ("optgroups", optgroups);
> > +    for (const kv_pair<optgroup_flags_t> *optgroup =
> > optgroup_options;
> > +        optgroup->name != NULL; optgroup++)
> > +      if (optgroup->value != OPTGROUP_ALL
> > +         && (pass->optinfo_flags & optgroup->value))
> > +       optgroups->append (new json::string (optgroup->name));
> > +  }
> > +  obj->set ("num", new json::number (pass->static_pass_number));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON array for LOC representing the chain of inlining
> > +   locations.
> > +   Compare with lhd_print_error_function and
> > cp_print_error_function.  */
> > +
> > +json::value *
> > +optrecord_json_writer::inlining_chain_to_json (location_t loc)
> > +{
> > +  json::array *array = new json::array ();
> > +
> > +  tree abstract_origin = LOCATION_BLOCK (loc);
> > +
> > +  while (abstract_origin)
> > +    {
> > +      location_t *locus;
> > +      tree block = abstract_origin;
> > +
> > +      locus = &BLOCK_SOURCE_LOCATION (block);
> > +      tree fndecl = NULL;
> > +      block = BLOCK_SUPERCONTEXT (block);
> > +      while (block && TREE_CODE (block) == BLOCK
> > +            && BLOCK_ABSTRACT_ORIGIN (block))
> > +       {
> > +         tree ao = BLOCK_ABSTRACT_ORIGIN (block);
> > +
> > +         while (TREE_CODE (ao) == BLOCK
> > +                && BLOCK_ABSTRACT_ORIGIN (ao)
> > +                && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
> > +           ao = BLOCK_ABSTRACT_ORIGIN (ao);
> > +
> > +         if (TREE_CODE (ao) == FUNCTION_DECL)
> > +           {
> > +             fndecl = ao;
> > +             break;
> > +           }
> > +         else if (TREE_CODE (ao) != BLOCK)
> > +           break;
> > +
> > +         block = BLOCK_SUPERCONTEXT (block);
> > +       }
> > +      if (fndecl)
> > +       abstract_origin = block;
> > +      else
> > +       {
> > +         while (block && TREE_CODE (block) == BLOCK)
> > +           block = BLOCK_SUPERCONTEXT (block);
> > +
> > +         if (block && TREE_CODE (block) == FUNCTION_DECL)
> > +           fndecl = block;
> > +         abstract_origin = NULL;
> > +       }
> > +      if (fndecl)
> > +       {
> > +         json::object *obj = new json::object ();
> > +         const char *printable_name
> > +           = lang_hooks.decl_printable_name (fndecl, 2);
> > +         obj->set ("fndecl", new json::string (printable_name));
> > +         if (*locus != UNKNOWN_LOCATION)
> > +           obj->set ("site", location_to_json (*locus));
> > +         array->append (obj);
> > +       }
> > +    }
> > +
> > +  return array;
> > +}
> > +
> > +/* Create a JSON object representing OPTINFO.  */
> > +
> > +json::object *
> > +optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
> > +{
> > +  json::object *obj = new json::object ();
> > +
> > +  obj->set ("impl_location",
> > +           impl_location_to_json (optinfo->get_impl_location ()));
> > +
> > +  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind 
> > ());
> > +  obj->set ("kind", new json::string (kind_str));
> > +  json::array *message = new json::array ();
> > +  obj->set ("message", message);
> > +  for (unsigned i = 0; i < optinfo->num_items (); i++)
> > +    {
> > +      const optinfo_item *item = optinfo->get_item (i);
> > +      switch (item->get_kind ())
> > +       {
> > +       default:
> > +         gcc_unreachable ();
> > +       case OPTINFO_ITEM_KIND_TEXT:
> > +         {
> > +           message->append (new json::string (item->get_text ()));
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_TREE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("expr", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the node.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_GIMPLE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("stmt", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the stmt.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_SYMTAB_NODE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("symtab_node", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the node.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       }
> > +   }
> > +
> > +  if (optinfo->get_pass ())
> > +    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass
> > ()));
> > +
> > +  profile_count count = optinfo->get_count ();
> > +  if (count.initialized_p ())
> > +    obj->set ("count", profile_count_to_json (count));
> > +
> > +  /* Record any location, handling the case where of an
> > UNKNOWN_LOCATION
> > +     within an inlined block.  */
> > +  location_t loc = optinfo->get_location_t ();
> > +  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
> > +    {
> > +      // TOOD: record the location (just caret for now)
> > +      // TODO: start/finish also?
> > +      obj->set ("location", location_to_json (loc));
> > +    }
> > +
> > +  if (current_function_decl)
> > +    {
> > +      const char *fnname = get_fnname_from_decl
> > (current_function_decl);
> > +      obj->set ("function", new json::string (fnname));
> > +    }
> > +
> > +  if (loc != UNKNOWN_LOCATION)
> > +    obj->set ("inlining_chain", inlining_chain_to_json (loc));
> > +
> > +  return obj;
> > +}
> > +
> > +/* Add a json description of PASS and its siblings to ARR,
> > recursing into
> > +   child passes (adding their descriptions within a "children"
> > array).  */
> > +
> > +void
> > +optrecord_json_writer::add_pass_list (json::array *arr, opt_pass
> > *pass)
> > +{
> > +  do
> > +    {
> > +      json::object *pass_obj = pass_to_json (pass);
> > +      arr->append (pass_obj);
> > +      if (pass->sub)
> > +       {
> > +         json::array *sub = new json::array ();
> > +         pass_obj->set ("children", sub);
> > +         add_pass_list (sub, pass->sub);
> > +       }
> > +      pass = pass->next;
> > +    }
> > +  while (pass);
> > +}
> > +
> > +/* File-level interface to rest of compiler (to avoid exposing
> > +   class optrecord_json_writer outside of this file).  */
> > +
> > +static optrecord_json_writer *the_json_writer;
> > +
> > +/* Perform startup activity for -fsave-optimization-record.  */
> > +
> > +void
> > +optimization_records_start ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!flag_save_optimization_record)
> > +    return;
> > +
> > +  the_json_writer = new optrecord_json_writer ();
> > +}
> > +
> > +/* Perform cleanup activity for -fsave-optimization-record.
> > +
> > +   Currently, the file is written out here in one go, before
> > cleaning
> > +   up.  */
> > +
> > +void
> > +optimization_records_finish ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->write ();
> > +
> > +  delete the_json_writer;
> > +  the_json_writer = NULL;
> > +}
> > +
> > +/* Did the user request optimization records to be written
> > out?  */
> > +
> > +bool
> > +optimization_records_enabled_p ()
> > +{
> > +  return the_json_writer != NULL;
> > +}
> > +
> > +/* If optimization records were requested, then add a record for
> > OPTINFO
> > +   to the queue of records to be written.  */
> > +
> > +void
> > +optimization_records_maybe_record_optinfo (const optinfo *optinfo)
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->add_record (optinfo);
> > +}
> > +
> > +/* Handling for the end of a dump scope for the
> > +   optimization records sink.  */
> > +
> > +void
> > +optimization_records_maybe_pop_dump_scope ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->pop_scope ();
> > +}
> > +
> > +#if CHECKING_P
> > +
> > +namespace selftest {
> > +
> > +/* Verify that we can build a JSON optimization record from dump_*
> > +   calls.  */
> > +
> > +static void
> > +test_building_json_from_dump_calls ()
> > +{
> > +  temp_dump_context tmp (true);
> > +  dump_location_t loc;
> > +  dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > +  dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > +  optinfo *info = tmp.get_pending_optinfo ();
> > +  ASSERT_TRUE (info != NULL);
> > +  ASSERT_EQ (info->num_items (), 2);
> > +
> > +  optrecord_json_writer writer;
> > +  json::object *json_obj = writer.optinfo_to_json (info);
> > +  ASSERT_TRUE (json_obj != NULL);
> > +
> > +  /* Verify that the json is sane.  */
> > +  pretty_printer pp;
> > +  json_obj->print (&pp);
> > +  const char *json_str = pp_formatted_text (&pp);
> > +  ASSERT_STR_CONTAINS (json_str, "impl_location");
> > +  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
> > +  ASSERT_STR_CONTAINS (json_str,
> > +                      "\"message\": [\"test of tree: \",
> > {\"expr\": \"0\"}]");
> > +  delete json_obj;
> > +}
> > +
> > +/* Run all of the selftests within this file.  */
> > +
> > +void
> > +optinfo_emit_json_cc_tests ()
> > +{
> > +  test_building_json_from_dump_calls ();
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* CHECKING_P */
> > diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
> > new file mode 100644
> > index 0000000..3628d56
> > --- /dev/null
> > +++ b/gcc/optinfo-emit-json.h
> > @@ -0,0 +1,36 @@
> > +/* Emit optimization information as JSON files.
> > +   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_OPTINFO_EMIT_JSON_H
> > +#define GCC_OPTINFO_EMIT_JSON_H
> > +
> > +class optinfo;
> > +struct opt_pass;
> > +
> > +extern void optimization_records_start ();
> > +extern void optimization_records_finish ();
> > +
> > +extern bool optimization_records_enabled_p ();
> > +
> > +extern void optimization_records_maybe_record_optinfo (const
> > optinfo *);
> > +extern void optimization_records_maybe_pop_dump_scope ();
> > +
> > +
> > +#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
> > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > index 6f224bc..93de9d9 100644
> > --- a/gcc/optinfo.cc
> > +++ b/gcc/optinfo.cc
> > @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "gimple.h"
> > 
> >  #include "optinfo.h"
> > +#include "optinfo-emit-json.h"
> >  #include "dump-context.h"
> >  #include "pretty-print.h"
> >  #include "gimple-pretty-print.h"
> > @@ -85,7 +86,8 @@ optinfo::~optinfo ()
> >  void
> >  optinfo::emit ()
> >  {
> > -  /* currently this is a no-op.  */
> > +  /* -fsave-optimization-record.  */
> > +  optimization_records_maybe_record_optinfo (this);
> >  }
> > 
> >  /* Update the optinfo's kind based on DUMP_KIND.  */
> > @@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi,
> > signop sgn)
> > 
> >  bool optinfo_enabled_p ()
> >  {
> > -  /* Currently no destinations are implemented, just a hook for
> > -     selftests.  */
> > -  return dump_context::get ().forcibly_enable_optinfo_p ();
> > +  return (dump_context::get ().forcibly_enable_optinfo_p ()
> > +         || optimization_records_enabled_p ());
> >  }
> > 
> >  /* Return true if any of the active optinfo destinations make use
> > @@ -232,5 +233,5 @@ bool optinfo_enabled_p ()
> > 
> >  bool optinfo_wants_inlining_info_p ()
> >  {
> > -  return false;
> > +  return optimization_records_enabled_p ();
> >  }
> > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > index 5bdb9eb..5f09022 100644
> > --- a/gcc/optinfo.h
> > +++ b/gcc/optinfo.h
> > @@ -25,12 +25,8 @@ along with GCC; see the file COPYING3.  If not
> > see
> >     optimization, which can be emitted to zero or more of several
> >     destinations, such as:
> > 
> > -   * as a "remark" through the diagnostics subsystem
> > -
> >     * saved to a file as an "optimization record"
> > 
> > -   Currently no such destinations are implemented.
> > -
> >     They are generated in response to calls to the "dump_*" API in
> >     dumpfile.h; repeated calls to the "dump_*" API are consolidated
> >     into a pending optinfo instance, with a "dump_*_loc" starting a
> > new
> > diff --git a/gcc/profile-count.c b/gcc/profile-count.c
> > index 3d411cf..6a17f5e 100644
> > --- a/gcc/profile-count.c
> > +++ b/gcc/profile-count.c
> > @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "wide-int.h"
> >  #include "sreal.h"
> > 
> > +/* Get a string describing QUALITY.  */
> > +
> > +const char *
> > +profile_quality_as_string (enum profile_quality quality)
> > +{
> > +  switch (quality)
> > +    {
> > +    default:
> > +      gcc_unreachable ();
> > +    case profile_uninitialized:
> > +      return "uninitialized";
> > +    case profile_guessed_local:
> > +      return "guessed_local";
> > +    case profile_guessed_global0:
> > +      return "guessed_global0";
> > +    case profile_guessed_global0adjusted:
> > +      return "guessed_global0adjusted";
> > +    case profile_guessed:
> > +      return "guessed";
> > +    case profile_afdo:
> > +      return "afdo";
> > +    case profile_adjusted:
> > +      return "adjusted";
> > +    case profile_precise:
> > +      return "precise";
> > +    }
> > +}
> > +
> >  /* Dump THIS to F.  */
> > 
> >  void
> > diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> > index c83fa3b..f4d0c340 100644
> > --- a/gcc/profile-count.h
> > +++ b/gcc/profile-count.h
> > @@ -59,6 +59,8 @@ enum profile_quality {
> >    profile_precise
> >  };
> > 
> > +extern const char *profile_quality_as_string (enum
> > profile_quality);
> > +
> >  /* The base value for branch probability notes and edge
> > probabilities.  */
> >  #define REG_BR_PROB_BASE  10000
> > 
> > @@ -721,6 +723,9 @@ public:
> >        return m_quality == profile_precise;
> >      }
> > 
> > +  /* Get the quality of the count.  */
> > +  enum profile_quality quality () const { return m_quality; }
> > +
> >    /* When merging basic blocks, the two different profile counts
> > are unified.
> >       Return true if this can be done without losing info about
> > profile.
> >       The only case we care about here is when first BB contains
> > something
> > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> > index 7f4d6f3..5adb033 100644
> > --- a/gcc/selftest-run-tests.c
> > +++ b/gcc/selftest-run-tests.c
> > @@ -72,6 +72,8 @@ selftest::run_tests ()
> >    typed_splay_tree_c_tests ();
> >    unique_ptr_tests_cc_tests ();
> >    opt_proposer_c_tests ();
> > +  json_cc_tests ();
> > +  optinfo_emit_json_cc_tests ();
> > 
> >    /* Mid-level data structures.  */
> >    input_c_tests ();
> > diff --git a/gcc/selftest.h b/gcc/selftest.h
> > index 54fc488..ede7732 100644
> > --- a/gcc/selftest.h
> > +++ b/gcc/selftest.h
> > @@ -228,6 +228,8 @@ extern void gimple_c_tests ();
> >  extern void hash_map_tests_c_tests ();
> >  extern void hash_set_tests_c_tests ();
> >  extern void input_c_tests ();
> > +extern void json_cc_tests ();
> > +extern void optinfo_emit_json_cc_tests ();
> >  extern void predict_c_tests ();
> >  extern void pretty_print_c_tests ();
> >  extern void read_rtl_function_c_tests ();
> > diff --git a/gcc/toplev.c b/gcc/toplev.c
> > index d108096..a047390 100644
> > --- a/gcc/toplev.c
> > +++ b/gcc/toplev.c
> > @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "tree-pass.h"
> >  #include "dumpfile.h"
> >  #include "ipa-fnsummary.h"
> > +#include "optinfo-emit-json.h"
> > 
> >  #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
> >  #include "dbxout.h"
> > @@ -487,6 +488,8 @@ compile_file (void)
> >    if (lang_hooks.decls.post_compilation_parsing_cleanups)
> >      lang_hooks.decls.post_compilation_parsing_cleanups ();
> > 
> > +  optimization_records_finish ();
> > +
> >    if (seen_error ())
> >      return;
> > 
> > @@ -2048,6 +2051,8 @@ do_compile ()
> > 
> >        timevar_start (TV_PHASE_SETUP);
> > 
> > +      optimization_records_start ();
> > +
> >        /* This must be run always, because it is needed to compute
> > the FP
> >          predefined macros, such as __LDBL_MAX__, for targets using
> > non
> >          default FP formats.  */
> > diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
> > index 7447f7a..2623d9b 100644
> > --- a/gcc/tree-ssa-live.c
> > +++ b/gcc/tree-ssa-live.c
> > @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "cfgloop.h"
> >  #include "stringpool.h"
> >  #include "attribs.h"
> > +#include "optinfo.h"
> > 
> >  static void verify_live_on_entry (tree_live_info_p);
> > 
> > @@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool
> > in_ctor_dtor_block)
> >       ;
> >     /* When not generating debug info we can eliminate info on
> > unused
> >        variables.  */
> > -   else if (!flag_auto_profile && debug_info_level ==
> > DINFO_LEVEL_NONE)
> > +   else if (!flag_auto_profile && debug_info_level ==
> > DINFO_LEVEL_NONE
> > +           && !optinfo_wants_inlining_info_p ())
> >       {
> >         /* Even for -g0 don't prune outer scopes from artificial
> >           functions, otherwise diagnostics using
> > tree_nonartificial_location
> > --
> > 1.8.5.3
> > 

^ permalink raw reply	[flat|nested] 80+ messages in thread

end of thread, other threads:[~2018-07-20 15:45 UTC | newest]

Thread overview: 80+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-29 20:32 [PATCH 00/10] RFC: Prototype of compiler-assisted performance analysis David Malcolm
2018-05-29 20:32 ` [PATCH 06/10] Experiments with using optinfo for inlining David Malcolm
2018-05-29 20:32 ` [PATCH 04/10] Use indentation to show nesting for -fopt-info David Malcolm
2018-05-29 20:32 ` [PATCH 08/10] Experiment with using optinfo for devirtualization David Malcolm
2018-05-29 20:32 ` [PATCH 02/10] Add JSON implementation David Malcolm
2018-05-30 17:31   ` Eric Gallager
2018-05-30 17:32     ` David Malcolm
2018-05-30 21:23       ` Eric Gallager
2018-05-29 20:32 ` [PATCH 05/10] Experiment with using optinfo for vectorization David Malcolm
2018-05-29 20:32 ` [PATCH 10/10] Experiment with optinfo in tree-ssa-loop-im.c David Malcolm
2018-05-29 20:32 ` [PATCH 07/10] Experiment with using optinfo for loop-handling David Malcolm
2018-05-29 20:32 ` [PATCH 09/10] Experiment with using optinfo in gimple-loop-interchange.cc David Malcolm
2018-06-01  9:51   ` Richard Biener
2018-06-01 13:40     ` David Malcolm
2018-06-01 15:32       ` Richard Biener
2018-06-01 22:22         ` David Malcolm
2018-06-04 13:20           ` Richard Biener
2018-06-14 19:50             ` [PATCH 0/8] v2 of optimization records patch kit David Malcolm
2018-06-14 19:50               ` [PATCH 1/8] Add GCC_LIKELY and GCC_UNLIKELY David Malcolm
2018-06-15  6:10                 ` Alexander Monakov
2018-06-15 16:31                 ` Jeff Law
2018-06-15 16:35                   ` Jakub Jelinek
2018-06-15 16:36                     ` Jeff Law
2018-06-15 20:04                       ` David Malcolm
2018-06-14 19:50               ` [PATCH 5/8] gimple-loop-interchange.cc: use the dump API in a few places David Malcolm
2018-06-14 19:50               ` [PATCH 4/8] tree-vect-loop.c: use MSG_OPTIMIZED_LOCATIONS " David Malcolm
2018-06-14 19:50               ` [PATCH 6/8] ipa-inline.c/tree-inline.c: port from fprintf to dump API David Malcolm
2018-06-14 19:50               ` [PATCH 7/8] tree-ssa-loop-im.c port from fprintf to the " David Malcolm
2018-06-14 19:50               ` [PATCH 8/8] Add lots of pointless churn to tree-vect-*.c David Malcolm
2018-06-14 19:50               ` [PATCH 2/8] Introduce VECT_SCOPE macro David Malcolm
2018-06-15 20:11                 ` Jeff Law
2018-06-18  9:26                   ` Richard Biener
2018-06-18 19:07                   ` David Malcolm
2018-06-14 19:50               ` [PATCH 3/8] v2 of optinfo, remarks and optimization records David Malcolm
2018-06-20 16:35             ` [PATCH] v3 " David Malcolm
2018-06-25 13:35               ` Richard Biener
2018-06-26 13:54                 ` [committed] Introduce dump_location_t David Malcolm
2018-06-28 11:29                   ` Richard Biener
2018-06-28 14:29                     ` David Malcolm
2018-06-29  7:14                       ` Richard Biener
2018-07-02 20:51                         ` [PATCH 0/2] v4: optinfo framework and remarks David Malcolm
2018-07-02 20:51                           ` [PATCH 2/2] optinfo: add diagnostic remarks David Malcolm
2018-07-09 13:05                             ` Richard Biener
2018-07-11 10:53                               ` [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
2018-07-11 10:53                                 ` [PATCH 2/2] Add "-fsave-optimization-record" David Malcolm
2018-07-19 12:39                                   ` Richard Biener
2018-07-20 15:45                                     ` David Malcolm
2018-07-18 20:20                                 ` [PING] Re: [PATCH 1/2] v5: Add "optinfo" framework David Malcolm
2018-07-19 12:18                                 ` Richard Biener
2018-07-02 20:51                           ` [PATCH 1/2] " David Malcolm
2018-07-09 13:01                             ` Richard Biener
2018-07-10 11:01                               ` David Malcolm
2018-07-10 11:25                                 ` Richard Biener
2018-06-26 15:43                 ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE David Malcolm
2018-06-29  8:13                   ` Richard Biener
2018-07-02 12:25                     ` Christophe Lyon
2018-07-02 17:00                       ` David Malcolm
2018-07-02 17:09                         ` Christophe Lyon
2018-07-03  7:37                         ` Richard Biener
2018-07-03 13:52                           ` David Malcolm
2018-07-03 13:53                             ` Richard Biener
2018-07-03 14:10                               ` [PATCH] Remove "note: " prefix from some scan-tree-dump directives David Malcolm
2018-07-03 14:11                                 ` Richard Biener
2018-07-09  9:03                                   ` Christophe Lyon
2018-07-05  8:42                               ` [PATCH] -fopt-info: add indentation via DUMP_VECT_SCOPE Christophe Lyon
2018-07-05  9:03                                 ` Richard Biener
2018-06-26 20:27                 ` [PATCH] Hide alt_dump_file within dumpfile.c David Malcolm
2018-06-28  9:50                   ` Richard Biener
2018-05-29 20:32 ` [PATCH 03/10] Add optinfo, remarks and optimization records David Malcolm
2018-05-29 20:35 ` [PATCH 01/10] Convert dump and optgroup flags to enums David Malcolm
2018-06-01 10:00   ` Richard Biener
2018-06-05  8:44     ` Trevor Saunders
2018-06-05 14:49       ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) David Malcolm
2018-06-05 16:13         ` Pedro Alves
2018-06-05 17:13           ` [PATCH (for gdb)] enum-flags.h: Add trailing semicolon to example in comment David Malcolm
2018-06-05 17:32             ` Pedro Alves
2018-06-06 12:54               ` David Malcolm
2018-06-06  0:48             ` Eric Gallager
2018-06-05 16:17         ` Sharing gdb's enum-flags.h with gcc? (was Re: [PATCH 01/10] Convert dump and optgroup flags to enums) Richard Biener
2018-06-08 12:59           ` [committed] v2: Convert dump and optgroup flags to enums David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).