From e3ac7d170f25ba76b5574382bb52e8fda3bee8da Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 10 Sep 2018 22:33:47 +0100
Subject: [PATCH 4/9] FIXME: Add diagram.h/cc

---
 gcc/Makefile.in          |   1 +
 gcc/diagram.cc           | 739 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/diagram.h            | 287 ++++++++++++++++++
 gcc/json.h               |   5 +
 gcc/selftest-diagram.h   |  67 +++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 7 files changed, 1101 insertions(+)
 create mode 100644 gcc/diagram.cc
 create mode 100644 gcc/diagram.h
 create mode 100644 gcc/selftest-diagram.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index d2da9ff..4547bdf 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,7 @@ OBJS = \
 	data-streamer.o \
 	data-streamer-in.o \
 	data-streamer-out.o \
+	diagram.o \
 	dbxout.o \
 	dbgcnt.o \
 	dce.o \
diff --git a/gcc/diagram.cc b/gcc/diagram.cc
new file mode 100644
index 0000000..1a11520
--- /dev/null
+++ b/gcc/diagram.cc
@@ -0,0 +1,739 @@
+/* Classes for building diagrams.
+   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 "pretty-print.h"
+#include "diagram.h"
+#include "selftest.h"
+#include "selftest-diagram.h"
+
+/* class text_buffer.  */
+
+/* text_buffer's ctor.  The buffer is initialized to spaces.  */
+
+text_buffer::text_buffer (size wh)
+  : m_wh (wh),
+    m_area (wh.get_area ()),
+    m_buffer (new cell[m_area])
+{
+  for (int i = 0; i < m_area; i++)
+    m_buffer[i] = cell (' ');
+}
+
+/* Print the content of this text_buffer to PP, eliding trailing
+   whitespace on each line.  */
+
+void
+text_buffer::print (pretty_printer *pp) const
+{
+  for (int y = 0; y < m_wh.h; y++)
+    {
+      int x;
+      for (x = m_wh.w - 1; x >= 0; x--)
+	if (get_cell (point (x, y)).ch != ' ')
+	  break;
+      int rightmost_non_ws = x;
+      for (x = 0; x <= rightmost_non_ws; x++)
+	pp_character (pp, get_cell (point (x,y)).ch);
+      pp_newline (pp);
+    }
+}
+
+/* Get the content of XY.  */
+
+cell
+text_buffer::get_cell (point xy) const
+{
+  return m_buffer[xy_to_index (xy)];
+}
+
+/* Set the content of XY to CH.  */
+
+void
+text_buffer::set_cell (point xy, char ch)
+{
+  m_buffer[xy_to_index (xy)] = cell (ch);
+}
+
+/* Write STR as horizontal left-aligned text at XY, so that the first
+   character of STR is at XY.  The buffer must be large enough.  */
+
+void
+text_buffer::left_aligned_text_at (point xy, const char *str)
+{
+  while (char ch = *(str++))
+    {
+      set_cell (xy, ch);
+      ++xy.x;
+    }
+}
+
+/* Write STR as horizontal right-aligned text at XY, so that the final
+   character of STR is at XY.  The buffer must be large enough.  */
+
+void
+text_buffer::right_aligned_text_at (point xy, const char *str)
+{
+  size_t len = strlen (str);
+  left_aligned_text_at (point (xy.x + 1 - len, xy.y), str);
+}
+
+/* Convert XY to an index within the buffer.  */
+
+int
+text_buffer::xy_to_index (point xy) const
+{
+  gcc_assert (xy.x >= 0);
+  gcc_assert (xy.y >= 0);
+  gcc_assert (xy.x < m_wh.w);
+  gcc_assert (xy.y < m_wh.h);
+  return (xy.y * m_wh.w) + xy.x;
+}
+
+/* class element.  */
+
+/* Print this element to a TEXT_BUFFER that's exactly big enough to hold it,
+   and print the result to PP, eliding trailing whitespace on each line.  */
+
+void
+element::print_to_pp (pretty_printer *pp)
+{
+  size wh = get_requisition ();
+  set_allocation (wh);
+
+  text_buffer buf (wh);
+  print (&buf, point (0,0));
+  buf.print (pp);
+}
+
+/* class text_element : public element.  */
+
+/* text_element's ctor.  */
+
+text_element::text_element (const char *str)
+: m_str (xstrdup (str)),
+  m_len (strlen (str))
+{}
+
+/* text_element's dtor.  */
+
+text_element::~text_element ()
+{
+  free (m_str);
+}
+
+/* Implementation of element::print for text_element.  */
+
+void
+text_element::print (text_buffer *buf, point xy)
+{
+  buf->left_aligned_text_at (xy, m_str);
+}
+
+/* Implementation of element::get_requisition for text_element.  */
+
+size
+text_element::get_requisition ()
+{
+  return size (m_len, 1);
+}
+
+/* Implementation of element::handle_allocation for text_element.  */
+
+void
+text_element::handle_allocation ()
+{
+  /* Empty.  */
+}
+
+/* Implementation of element::to_json for text_element.  */
+
+json::value *
+text_element::to_json () const
+{
+  return new json::string (m_str);
+}
+
+/* class grid_layout : public element.  */
+
+/* grid_layout's ctor.  */
+
+grid_layout::grid_layout (int w, int h)
+  : m_w (w), m_h (h), m_children ()
+{
+  for (int i = 0; i < w * h; i++)
+    m_children.safe_push (NULL);
+  for (int x = 0; x < w; x++)
+    m_col_widths.safe_push (0);
+  for (int y = 0; y < h; y++)
+    m_row_heights.safe_push (0);
+}
+
+/* grid_layout's dtor.  */
+
+grid_layout::~grid_layout ()
+{
+  element *child;
+  unsigned i;
+  FOR_EACH_VEC_ELT (m_children, i, child)
+    delete child;
+}
+
+/* Set the child at X, Y to element.  */
+
+void
+grid_layout::set_child (int x, int y, element *element)
+{
+  delete m_children[child_xy_to_index (x, y)];
+  m_children[child_xy_to_index (x, y)] = element;
+}
+
+/* Implementation of element::print for grid_layout.  */
+
+void
+grid_layout::print (text_buffer *buf, point xy)
+{
+  point row_start = xy;
+  for (int y = 0; y < m_h; y++)
+    {
+      point child_xy = row_start;
+      for (int x = 0; x < m_w; x++)
+	{
+	  element *child = get_child (x, y);
+	  if (child)
+	    child->print (buf, child_xy);
+	  child_xy.x += m_col_widths[x];
+	}
+      row_start.y += m_row_heights[y];
+    }
+}
+
+/* Implementation of element::get_requisition for grid_layout.  */
+
+size
+grid_layout::get_requisition ()
+{
+  /* Get child requisitions.  */
+  auto_vec<size> child_reqs (get_num_children ());
+  for (int i = 0; i < get_num_children (); i++)
+    {
+      element *child = m_children[i];
+      if (child)
+	child_reqs.quick_push (child->get_requisition ());
+      else
+	child_reqs.quick_push (size (0, 0));
+    }
+
+  /* Get column widths.  */
+  int width = 0;
+  for (int x = 0; x < m_w; x++)
+    {
+      /* Get maximum width within column.  */
+      int max_width = 0;
+      for (int y = 0; y < m_h; y++)
+	max_width = MAX (max_width,
+			 child_reqs[child_xy_to_index(x, y)].w);
+      width += max_width;
+      m_col_widths[x] = max_width;
+    }
+
+  /* Get row heights.  */
+  int height = 0;
+  for (int y = 0; y < m_h; y++)
+    {
+      /* Get maximum height within row.  */
+      int max_height = 0;
+      for (int x = 0; x < m_w; x++)
+	max_height = MAX (max_height,
+			  child_reqs[child_xy_to_index(x, y)].h);
+      height += max_height;
+      m_row_heights[y] = max_height;
+    }
+
+  return size (width, height);
+}
+
+/* Implementation of element::handle_allocation for grid_layout.  */
+
+void
+grid_layout::handle_allocation ()
+{
+  // TODO: hand out surplus allocation
+  for (int y = 0; y < m_h; y++)
+    for (int x = 0; x < m_w; x++)
+      {
+	element *child = get_child (x, y);
+	if (child)
+	  child->set_allocation (size (m_col_widths[x],
+				       m_row_heights[y]));
+      }
+}
+
+/* Implementation of element::to_json for grid_layout.  */
+
+json::value *
+grid_layout::to_json () const
+{
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("grid"));
+  obj->set ("w", new json::number (m_w));
+  obj->set ("h", new json::number (m_h));
+  json::array *rows = new json::array ();
+  for (int y = 0; y < m_h; y++)
+    {
+      json::array *row = new json::array ();
+      for (int x = 0; x < m_w; x++)
+	{
+	  element *child = get_child (x, y);
+	  if (child)
+	    row->append (child->to_json ());
+	  else
+	    row->append (new json::literal (json::JSON_NULL));
+	}
+      rows->append (row);
+    }
+  obj->set ("rows", rows);
+  return obj;
+}
+
+/* class wrapper_element : public element.  */
+
+/* wrapper_element's dtor.  */
+
+wrapper_element::~wrapper_element ()
+{
+  delete m_child;
+}
+
+/* Implementation of element::print for wrapper_element.  */
+
+void
+wrapper_element::print (text_buffer *buf, point xy)
+{
+  if (m_child)
+    return m_child->print (buf, xy);
+}
+
+/* Implementation of element::get_requisition for wrapper_element.  */
+
+size
+wrapper_element::get_requisition ()
+{
+  size child_req (0, 0);
+  if (m_child)
+    return m_child->get_requisition ();
+  else
+    return size (0,0);
+}
+
+/* Implementation of element::handle_allocation for wrapper_element.  */
+
+void
+wrapper_element::handle_allocation ()
+{
+  if (m_child)
+    m_child->set_allocation (get_size ());
+}
+
+/* Implementation of element::to_json for wrapper_element.  */
+
+json::value *
+wrapper_element::to_json () const
+{
+  if (m_child)
+    return m_child->to_json ();
+  else
+    return new json::literal (json::JSON_NULL);
+}
+
+/* Set the child element of this wrapper.  */
+
+void
+wrapper_element::set_child (element *child)
+{
+  delete m_child;
+  m_child = child;
+}
+
+/* class box_element : public wrapper_element.  */
+
+/* box_element's ctor.  */
+
+box_element::box_element (element *child)
+: wrapper_element (child)
+{}
+
+/* Implementation of element::print for box_element.  */
+
+void
+box_element::print (text_buffer *buf, point xy)
+{
+  horizontal_border (buf, xy);
+  vertical_line (buf, point (xy.x, xy.y + 1), get_height () - 2);
+  vertical_line (buf, point (xy.x + get_width () -1 , xy.y + 1),
+		 get_height () - 2);
+  horizontal_border (buf, point (xy.x, xy.y + get_height () - 1));
+
+  if (get_child ())
+    get_child ()->print (buf, point (xy.x + 1, xy.y + 1));
+}
+
+/* Implementation of element::get_requisition for box_element.  */
+
+size
+box_element::get_requisition ()
+{
+  size child_req = wrapper_element::get_requisition ();
+  return size (child_req.w + 2, child_req.h + 2);
+}
+
+/* Implementation of element::handle_allocation for box_element.  */
+
+void
+box_element::handle_allocation ()
+{
+  if (get_child ())
+    get_child ()->set_allocation (size (get_width () - 2, get_height () - 2));
+}
+
+/* Implementation of element::to_json for box_element.  */
+
+json::value *
+box_element::to_json () const
+{
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("box"));
+  obj->set ("content", wrapper_element::to_json ());
+  return obj;
+}
+
+/* Subroutine of box_element::print.  */
+
+void
+box_element::horizontal_border (text_buffer *buf, point xy)
+{
+  int w = get_width ();
+  buf->set_cell (xy, '+');
+  for (int x = 1; x < w - 1; x++)
+    buf->set_cell (point (xy.x + x, xy.y), '-');
+  if (w > 0)
+    buf->set_cell (point (xy.x + w - 1, xy.y), '+');
+}
+
+/* Subroutine of box_element::print.  */
+
+void
+box_element::vertical_line (text_buffer *buf, point xy, int height)
+{
+  for (int y = 0; y < height; y++)
+    buf->set_cell (point (xy.x, xy.y + y), '|');
+}
+
+/* class wiring_element : public element.  */
+
+/* wiring_element's ctor.  */
+
+wiring_element::wiring_element (bool north, bool south, bool east, bool west)
+  : m_north (north), m_south (south), m_east (east), m_west (west),
+    m_vert (m_north || m_south),
+    m_horiz (m_west || m_east)
+{
+}
+
+/* Implementation of element::print for wiring_element.  */
+
+void
+wiring_element::print (text_buffer *buf, point xy)
+{
+  point centre = point (xy.x + get_width () / 2,
+			xy.y + get_height () / 2);
+
+  /* Draw centre cell.  */
+  buf->set_cell (centre,
+		 (m_vert && m_horiz)
+		 ? '+'
+		 : (m_vert
+		    ? '|'
+		    :  (m_horiz ? '-' : ' ')));
+
+  /* Draw lines heading from center in each cardinal direction,
+     as requested.  */
+
+  if (m_north)
+    for (int y = xy.y; y < centre.y; y++)
+      buf->set_cell (point (centre.x, y), '|');
+  if (m_south)
+    for (int y = centre.y + 1; y < xy.y + get_height (); y++)
+      buf->set_cell (point (centre.x, y), '|');
+
+  if (m_west)
+    for (int x = xy.x; x < centre.x; x++)
+      buf->set_cell (point (x, centre.y), '-');
+  if (m_east)
+    for (int x = centre.x + 1; x < xy.x + get_width (); x++)
+      buf->set_cell (point (x, centre.y), '-');
+}
+
+/* Implementation of element::get_requisition for wiring_element.  */
+
+size
+wiring_element::get_requisition ()
+{
+  if (m_vert && m_horiz)
+    return size (3, 3);
+  else
+    return size (1, 1);
+}
+
+/* Implementation of element::handle_allocation for wiring_element.  */
+
+void
+wiring_element::handle_allocation ()
+{
+}
+
+/* Implementation of element::to_json for wiring_element.  */
+
+json::value *
+wiring_element::to_json () const
+{
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("wiring"));
+  obj->set ("north", json::literal::from_bool (m_north));
+  obj->set ("south", json::literal::from_bool (m_south));
+  obj->set ("east", json::literal::from_bool (m_east));
+  obj->set ("west", json::literal::from_bool (m_west));
+  return obj;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Implementation detail of ASSERT_TEXT_BUFFER_EQ.
+
+   Assert that BUF->print (&pp) is EXPECTED_TEXT, using LOC as the effective
+   location on any failure.  */
+
+void
+verify_text_buffer (const location &loc,
+		    text_buffer *buf,
+		    const char *expected_text)
+{
+  pretty_printer pp;
+  buf->print (&pp);
+  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_text);
+}
+
+/* Implementation detail of ASSERT_ELEMENT_PRINT.
+
+   Assert that ELEMENT->print_to_pp (&pp) is EXPECTED_TEXT, using LOC as
+   the effective location on any failure.  */
+
+void
+verify_element_print (const location &loc,
+		      element *element,
+		      const char *expected_text)
+{
+  pretty_printer pp;
+  element->print_to_pp (&pp);
+  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_text);
+}
+
+/* Verify that text_buffer works.  */
+
+static void
+test_text_buffer ()
+{
+  text_buffer buf (size (30, 3));
+  buf.left_aligned_text_at (point (0, 0), "012345678901234567890123456789");
+  buf.left_aligned_text_at (point (15, 1), "left-aligned");
+  buf.right_aligned_text_at (point (15, 2), "right-aligned");
+
+  ASSERT_TEXT_BUFFER_EQ (&buf,
+			 "012345678901234567890123456789\n"
+			 "               left-aligned\n"
+			 "   right-aligned\n");
+}
+
+/* Verify that text_element sizing and printing works.  */
+
+static void
+test_text_element ()
+{
+  text_element el ("hello world");
+  size wh = el.get_requisition ();
+  ASSERT_EQ (wh.w, 11);
+  ASSERT_EQ (wh.h, 1);
+
+  ASSERT_ELEMENT_PRINT (&el, "hello world\n");
+}
+
+/* Verify that grid_layout sizing and printing works.  */
+
+static void
+test_grid_layout ()
+{
+  grid_layout grid (3, 3);
+  grid.set_child (0, 0, new text_element ("top left"));
+  grid.set_child (1, 0, new text_element ("top middle"));
+  grid.set_child (2, 0, new text_element ("top right"));
+  grid.set_child (0, 1, new text_element ("middle left"));
+  grid.set_child (1, 1, new text_element ("middle middle"));
+  grid.set_child (2, 1, new text_element ("middle right"));
+  grid.set_child (0, 2, new text_element ("bottom left"));
+  grid.set_child (1, 2, new text_element ("bottom middle"));
+  grid.set_child (2, 2, new text_element ("bottom right"));
+
+  size wh = grid.get_requisition ();
+  ASSERT_EQ (wh.w, 36);
+  ASSERT_EQ (wh.h, 3);
+
+  ASSERT_ELEMENT_PRINT (&grid,
+			"top left   top middle   top right\n"
+			"middle leftmiddle middlemiddle right\n"
+			"bottom leftbottom middlebottom right\n");
+}
+
+/* Verify that box_element sizing and printing works.  */
+
+static void
+test_box_element ()
+{
+  box_element box (new text_element ("label"));
+  size wh = box.get_requisition ();
+  ASSERT_EQ (wh.w, 7);
+  ASSERT_EQ (wh.h, 3);
+
+  ASSERT_ELEMENT_PRINT (&box,
+			"+-----+\n"
+			"|label|\n"
+			"+-----+\n");
+}
+
+/* Implementation detail of ASSERT_WIRING_ELEMENT.  */
+
+static void
+verify_wiring_element (const location &loc,
+		       bool north, bool south, bool east, bool west,
+		       const char *expected_text)
+{
+  wiring_element el (north, south, east, west);
+  verify_element_print (loc, &el, expected_text);
+}
+
+/* Verify that the given wiring_element prints as EXPECTED_TEXT.  */
+
+#define ASSERT_WIRING_ELEMENT(NORTH, SOUTH, EAST, WEST, EXPECTED_TEXT)	\
+  SELFTEST_BEGIN_STMT							\
+  verify_wiring_element (SELFTEST_LOCATION, \
+			 (NORTH), (SOUTH), (EAST), (WEST), \
+			 (EXPECTED_TEXT));		   \
+  SELFTEST_END_STMT
+
+/* Verify that wiring_element sizing and printing works.  */
+
+static void
+test_wiring_elements ()
+{
+  /* Vertical.  */
+  ASSERT_WIRING_ELEMENT(true, true, false, false,
+			"|\n");
+  /* Horizontal.  */
+  ASSERT_WIRING_ELEMENT(false, false, true, true,
+			"-\n");
+  /* T junction.  */
+  ASSERT_WIRING_ELEMENT(true, false, true, true,
+			" |\n"
+			"-+-\n"
+			"\n");
+
+  /* Corner, exits to S and E.  */
+  ASSERT_WIRING_ELEMENT(false, true, true, false,
+			"\n"
+			" +-\n"
+			" |\n");
+}
+
+/* Test of printing a non-trivial diagram out of the above components.  */
+
+static void
+test_complex_diagram ()
+{
+  grid_layout grid (3, 3);
+  grid.set_child (0, 0, wiring_element::make_se ());
+  grid.set_child (1, 0, wiring_element::make_new ());
+  grid.set_child (2, 0, wiring_element::make_sw ());
+  grid.set_child (0, 1, new box_element (new text_element ("scalar")));
+  {
+    grid_layout *vect = new grid_layout (1, 5);
+    grid.set_child (2, 1, vect);
+    vect->set_child (0, 0, new box_element (new text_element ("prologue")));
+    vect->set_child (0, 1, wiring_element::make_vert ());
+    vect->set_child (0, 2,
+		     new box_element (new text_element ("vectorized loop")));
+    vect->set_child (0, 3, wiring_element::make_vert ());
+    vect->set_child (0, 4, new box_element (new text_element ("epilogue")));
+  }
+  grid.set_child (0, 2, wiring_element::make_ne ());
+  grid.set_child (1, 2, wiring_element::make_sew ());
+  grid.set_child (2, 2, wiring_element::make_nw ());
+
+  ASSERT_ELEMENT_PRINT (&grid,
+			"         |\n"
+			"    +----+---------+\n"
+			"    |              |\n"
+			"+------+   +---------------+\n"
+			"|scalar|   |prologue       |\n"
+			"|      |   +---------------+\n"
+			"|      |           |\n"
+			"|      |   +---------------+\n"
+			"|      |   |vectorized loop|\n"
+			"|      |   +---------------+\n"
+			"|      |           |\n"
+			"|      |   +---------------+\n"
+			"|      |   |epilogue       |\n"
+			"+------+   +---------------+\n"
+			"    |              |\n"
+			"    +----+---------+\n"
+			"         |\n");
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+diagram_cc_tests ()
+{
+  test_text_buffer ();
+
+  /* Tests of individual element subclasses.  */
+  test_text_element ();
+  test_grid_layout ();
+  test_box_element ();
+  test_wiring_elements ();
+
+  test_complex_diagram ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/diagram.h b/gcc/diagram.h
new file mode 100644
index 0000000..cbd4679
--- /dev/null
+++ b/gcc/diagram.h
@@ -0,0 +1,287 @@
+/* Classes for building diagrams.
+   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_DIAGRAM_H
+#define GCC_DIAGRAM_H
+
+#include "json.h"
+
+/* The size of an element within a diagram, or of the diagram itself.  */
+
+struct size
+{
+  int w;
+  int h;
+
+  size (int width, int height) : w (width), h (height) {}
+
+  int get_area () const { return w * h; }
+};
+
+/* A coordinate within a diagram.  */
+
+struct point
+{
+  int x;
+  int y;
+
+  point (int x_, int y_) : x (x_) , y (y_) {}
+};
+
+/* A cell within a text buffer.  Currently just a "char", but could be
+   extended to represent a unicode code point and color information.  */
+
+struct cell
+{
+  char ch;
+
+  cell () : ch (' ') {}
+  cell (char ch_) : ch (ch_) {}
+};
+
+/* A text buffer, to which a diagram can be rendered.  */
+
+class text_buffer
+{
+ public:
+  text_buffer (size wh);
+  ~text_buffer () { delete m_buffer; }
+
+  void print (pretty_printer *pp) const;
+
+  cell get_cell (point xy) const;
+  void set_cell (point xy, char ch);
+
+  void left_aligned_text_at (point xy, const char *str);
+  void right_aligned_text_at (point xy, const char *str);
+
+ private:
+  int xy_to_index (point xy) const;
+
+ private:
+  size m_wh;
+  int m_area;
+  cell *m_buffer;
+};
+
+/* A base class representing an element within a diagram (or the
+   whole diagram), which can be printed to a location within a
+   text_buffer via the "print" virtual function.
+
+   Elements can also be serialized to JSON, and thus saved
+   in optimization records.
+   TODO: generate HTML and/or SVG from them in (I have this mostly
+   working, but the wiring_element subclass is problematic).
+
+   Elements support two-phase size negotiation:
+
+   (i) client code uses "get_requisition" to ask the element how
+   big it needs to be.  This recurses through children, generating
+   a requested size that's big enough for them all
+
+   (ii) client code calls "set_allocation" on the element, which sets
+   the size, and calls "handle_allocation" to perform any recursive
+   size-setting on the children.  */
+
+class element
+{
+ public:
+  element () : m_wh (0, 0) {}
+  virtual ~element () {}
+
+  void set_allocation (size wh)
+  {
+    m_wh = wh;
+    handle_allocation ();
+  }
+
+  int get_width () const { return m_wh.w; }
+  int get_height () const { return m_wh.h; }
+  size get_size () const { return m_wh; }
+
+  virtual void print (text_buffer *buf, point xy) = 0;
+  virtual size get_requisition () = 0;
+  virtual void handle_allocation () = 0;
+  virtual json::value *to_json () const = 0;
+
+  void print_to_pp (pretty_printer *pp);
+
+ private:
+  size m_wh;
+};
+
+/* Subclass of element, representing a single line of text.  */
+
+class text_element : public element
+{
+ public:
+  text_element (const char *str);
+  ~text_element ();
+
+  void print (text_buffer *buf, point xy) OVERRIDE;
+  size get_requisition () OVERRIDE;
+  void handle_allocation () OVERRIDE;
+  json::value *to_json () const OVERRIDE;
+
+ private:
+  char *m_str;
+  size_t m_len;
+};
+
+/* Subclass of element, representing a WxH grid of child elements
+   (any of which can be NULL).  */
+
+class grid_layout : public element
+{
+public:
+  grid_layout (int w, int h);
+  ~grid_layout ();
+
+  void set_child (int x, int y, element *element);
+
+  void print (text_buffer *buf, point xy) OVERRIDE;
+  size get_requisition () OVERRIDE;
+  void handle_allocation () OVERRIDE;
+  json::value *to_json () const OVERRIDE;
+
+private:
+  int get_num_children () const { return m_children.length (); }
+
+  element *
+  get_child (int x, int y) const
+  {
+    return m_children[child_xy_to_index (x, y)];
+  }
+
+  int
+  child_xy_to_index (int x, int y) const
+  {
+    gcc_assert (x >= 0);
+    gcc_assert (x < m_w);
+    gcc_assert (y >= 0);
+    gcc_assert (y < m_h);
+    return (m_w * y) + x;
+  }
+
+private:
+  int m_w;
+  int m_h;
+  auto_vec<element *> m_children;
+
+  auto_vec<int> m_col_widths;
+  auto_vec<int> m_row_heights;
+};
+
+/* Subclass of element: a decorator for a child element (which can
+   be NULL).  */
+
+class wrapper_element : public element
+{
+ public:
+  wrapper_element (element *child) : m_child (child) {}
+  ~wrapper_element ();
+
+  void print (text_buffer *buf, point xy) OVERRIDE;
+  size get_requisition () OVERRIDE;
+  void handle_allocation () OVERRIDE;
+  json::value *to_json () const OVERRIDE;
+
+  element *get_child () const { return m_child; }
+  void set_child (element *child);
+
+private:
+  element *m_child;
+};
+
+/* Subclass of element: a decorator that prints a box around
+   a child element.  */
+
+class box_element : public wrapper_element
+{
+public:
+  box_element (element *child);
+
+  void print (text_buffer *buf, point xy) OVERRIDE;
+  size get_requisition () OVERRIDE;
+  void handle_allocation () OVERRIDE;
+  json::value *to_json () const OVERRIDE;
+
+private:
+  void horizontal_border (text_buffer *buf, point xy);
+  void vertical_line (text_buffer *buf, point xy, int height);
+};
+
+/* Subclass of element: a rectangular area with lines running
+   from the center to the centre of the edges.  Each of the lines
+   running north, south, east and west can be independently
+   turned on and off, allowing for simple flow diagrams.  */
+
+class wiring_element : public element
+{
+ public:
+  wiring_element (bool north, bool south, bool east, bool west);
+
+  static wiring_element *make_vert ()
+  {
+    return new wiring_element (true, true, false, false);
+  }
+  static wiring_element *make_horiz ()
+  {
+    return new wiring_element (false, false, true, true);
+  }
+  static wiring_element *make_ne ()
+  {
+    return new wiring_element (true, false, true, false);
+  }
+  static wiring_element *make_nw ()
+  {
+    return new wiring_element (true, false, false, true);
+  }
+  static wiring_element *make_new ()
+  {
+    return new wiring_element (true, false, true, true);
+  }
+  static wiring_element *make_se ()
+  {
+    return new wiring_element (false, true, true, false);
+  }
+  static wiring_element *make_sw ()
+  {
+    return new wiring_element (false, true, false, true);
+  }
+  static wiring_element *make_sew ()
+  {
+    return new wiring_element (false, true, true, true);
+  }
+  void print (text_buffer *buf, point xy) OVERRIDE;
+  size get_requisition () OVERRIDE;
+  void handle_allocation () OVERRIDE;
+  json::value *to_json () const OVERRIDE;
+
+ private:
+  bool m_north;
+  bool m_south;
+  bool m_east;
+  bool m_west;
+  bool m_vert;
+  bool m_horiz;
+};
+
+#endif /* #ifndef GCC_DIAGRAM_H */
diff --git a/gcc/json.h b/gcc/json.h
index 154d9e1..32f9b61 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -157,6 +157,11 @@ class literal : public value
   enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
   void print (pretty_printer *pp) const FINAL OVERRIDE;
 
+  static literal *from_bool (bool value)
+  {
+    return new literal (value ? JSON_TRUE : JSON_FALSE);
+  }
+
  private:
   enum kind m_kind;
 };
diff --git a/gcc/selftest-diagram.h b/gcc/selftest-diagram.h
new file mode 100644
index 0000000..8c176b4
--- /dev/null
+++ b/gcc/selftest-diagram.h
@@ -0,0 +1,67 @@
+/* Selftest support for diagram.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_SELFTEST_DIAGRAM_H
+#define GCC_SELFTEST_DIAGRAM_H
+
+/* The selftest code should entirely disappear in a production
+   configuration, hence we guard all of it with #if CHECKING_P.  */
+
+#if CHECKING_P
+
+class text_buffer;
+class element;
+
+#include "selftest.h"
+
+namespace selftest {
+
+/* Implementation detail of ASSERT_TEXT_BUFFER_EQ.  */
+
+extern void verify_text_buffer (const location &loc,
+				text_buffer *buf,
+				const char *expected_text);
+
+/* Assert that BUF->print is EXPECTED_TEXT.  */
+
+#define ASSERT_TEXT_BUFFER_EQ(BUF, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						\
+  verify_text_buffer (SELFTEST_LOCATION, (BUF), (EXPECTED_TEXT));	\
+  SELFTEST_END_STMT
+
+/* Implementation detail of ASSERT_ELEMENT_PRINT.  */
+
+extern void verify_element_print (const location &loc,
+				  element *element,
+				  const char *expected_text);
+
+/* Assert that ELEMENT->print_to_pp is EXPECTED_TEXT.  */
+
+#define ASSERT_ELEMENT_PRINT(ELEMENT, EXPECTED_TEXT)	\
+  SELFTEST_BEGIN_STMT					\
+  verify_element_print (SELFTEST_LOCATION, (ELEMENT),	\
+			(EXPECTED_TEXT));		\
+  SELFTEST_END_STMT
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_SELFTEST_DIAGRAM_H */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 562ada7..20754db 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -77,6 +77,7 @@ selftest::run_tests ()
   opt_problem_cc_tests ();
 
   /* Mid-level data structures.  */
+  diagram_cc_tests ();
   input_c_tests ();
   vec_perm_indices_c_tests ();
   tree_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 8da7c4a..2ce3e27 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -217,6 +217,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 diagram_cc_tests ();
 extern void dumpfile_c_tests ();
 extern void edit_context_c_tests ();
 extern void et_forest_c_tests ();
-- 
1.8.5.3

