diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f675e07..0283a46 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1214,6 +1214,7 @@ OBJS = \
 	auto-profile.o \
 	bb-reorder.o \
 	bitmap.o \
+	blt.o \
 	bt-load.o \
 	builtins.o \
 	caller-save.o \
@@ -1326,6 +1327,7 @@ OBJS = \
 	hsa-regalloc.o \
 	hsa-brig.o \
 	hsa-dump.o \
+	http-server.o \
 	hw-doloop.o \
 	hwint.o \
 	ifcvt.o \
@@ -1361,6 +1363,8 @@ OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
+	json-rpc.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
@@ -1371,6 +1375,8 @@ OBJS = \
 	loop-iv.o \
 	loop-unroll.o \
 	lower-subreg.o \
+	lsp.o \
+	lsp-main.o \
 	lra.o \
 	lra-assigns.o \
 	lra-coalesce.o \
@@ -1445,6 +1451,7 @@ OBJS = \
 	sel-sched.o \
 	selftest-rtl.o \
 	selftest-run-tests.o \
+	server.o \
 	sese.o \
 	shrink-wrap.o \
 	simplify-rtx.o \
diff --git a/gcc/blt.c b/gcc/blt.c
new file mode 100644
index 0000000..d92149b
--- /dev/null
+++ b/gcc/blt.c
@@ -0,0 +1,512 @@
+/* Extra location information.
+   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 "tree.h"
+#include "pretty-print.h"
+#include "diagnostic.h"
+#include "diagnostic-color.h"
+#include "blt.h"
+
+typedef hash_map <tree, blt_node *> tree_to_blt_map_t;
+static tree_to_blt_map_t *tree_to_blt_map;
+
+static const char *blt_kind_to_name (enum blt_kind kind);
+
+/* FIXME.  */
+
+blt_node::blt_node (enum blt_kind kind, location_t start)
+: m_kind (kind), m_parent (NULL), m_first_child (NULL), m_last_child (NULL),
+  m_prev_sibling (NULL), m_next_sibling (NULL),
+  m_start (start), m_finish (UNKNOWN_LOCATION), m_tree (NULL_TREE)
+{
+}
+
+/* FIXME.  */
+
+void
+blt_node::add_child (blt_node *child)
+{
+  gcc_assert (child->m_prev_sibling == NULL);
+  gcc_assert (child->m_next_sibling == NULL);
+  gcc_assert (child->m_parent == NULL);
+
+  if (m_last_child)
+    {
+      m_last_child->m_next_sibling = child;
+      child->m_prev_sibling = m_last_child;
+    }
+  else
+    {
+      gcc_assert (m_first_child == NULL);
+      m_first_child = child;
+    }
+
+  m_last_child = child;
+  child->m_parent = this;
+}
+
+
+/* Convert OLD to an orphan, and take over parenthood of NEW,
+   putting NEW in OLD's place.  */
+// FIXME: motivated by the fixup in C++ parser of
+// block-declaration => function-definition
+
+void
+blt_node::replace_child (blt_node *old, blt_node *new_)
+{
+  gcc_assert (old);
+  gcc_assert (old->m_parent == this);
+  gcc_assert (new_);
+  assert_invariants ();
+  old->assert_invariants ();
+  new_->assert_invariants ();
+
+  blt_node *old_prev_sibling = old->m_prev_sibling;
+  blt_node *old_next_sibling = old->m_next_sibling;
+
+  old->make_orphan ();
+  new_->make_orphan ();
+
+  new_->m_prev_sibling = old_prev_sibling;
+  new_->m_next_sibling = old_next_sibling;
+
+  if (old_prev_sibling == NULL)
+    m_first_child = new_;
+  if (old_next_sibling == NULL)
+    m_last_child = new_;
+
+  assert_invariants ();
+  old->assert_invariants ();
+  new_->assert_invariants ();
+}
+
+/* FIXME.  */
+
+void
+blt_node::make_orphan ()
+{
+  assert_invariants ();
+
+  if (m_parent)
+    {
+      if (m_prev_sibling)
+	{
+	  m_prev_sibling->m_next_sibling = m_next_sibling;
+	  m_prev_sibling = NULL;
+	}
+      else
+	m_parent->m_first_child = m_next_sibling;
+      if (m_next_sibling)
+	{
+	  m_next_sibling->m_prev_sibling = m_prev_sibling;
+	  m_next_sibling = NULL;
+	}
+      else
+	m_parent->m_last_child = m_prev_sibling;
+    }
+  m_parent = NULL;
+
+  assert_invariants ();
+}
+
+/* FIXME.  */
+
+void
+blt_node::set_tree (tree t)
+{
+  m_tree = t;
+
+  /* Add to mapping.  */
+  if (!t)
+    return;
+
+  if (!tree_to_blt_map)
+    tree_to_blt_map = new tree_to_blt_map_t ();
+  tree_to_blt_map->put (t, this);
+}
+
+/* FIXME.  */
+
+void
+blt_node::dump (FILE *file) const
+{
+  pretty_printer pp;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp_format_decoder (&pp) = pp_format_decoder (global_dc->printer);
+  pp.buffer->stream = file;
+  dump (&pp, "", NULL, true);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+void
+blt_node::dump (pretty_printer *pp, const char *prefix,
+		const blt_node *parent, bool is_last_child) const
+{
+  if (parent)
+    {
+      // FIXME: colorize hierachical ASCII art
+      const char *tail = is_last_child ? "`-" : "|-";
+      pp_printf (pp, "%r%s%s%R", "note", prefix, tail); // FIXME: dedicated color code?
+    }
+  pp_printf (pp, "%r%s%R %p", "error", blt_kind_to_name (m_kind),
+	     (const void *)this); // FIXME: dedicated color code?
+
+  location_t parent_start_loc = parent ? parent->m_start : UNKNOWN_LOCATION;
+  location_t parent_finish_loc = parent ? parent->m_finish : UNKNOWN_LOCATION;
+
+  /* Dump location information, eliding commonality with parent.  */
+  {
+    pp_printf (pp, " %r<", "warning"); // FIXME: dedicated color code?
+
+    expanded_location el_start = expand_location (m_start);
+    expanded_location el_parent = expand_location (parent_start_loc);
+
+    if (el_start.file != el_parent.file)
+      /* We don't use %qs or %< and %> here, to avoid affecting
+         colorization.  */
+      pp_printf (pp, "%s:", el_start.file);
+    pp_printf (pp, "%i:%i", el_start.line, el_start.column);
+    if (m_finish != m_start)
+      {
+        pp_printf (pp, "-");
+        expanded_location el_finish = expand_location (m_finish);
+        if (el_finish.file && el_finish.file != el_start.file)
+          pp_printf (pp, "%s:", el_finish.file);
+        pp_printf (pp, "%i:%i", el_finish.line, el_finish.column);
+      }
+
+    pp_printf (pp, ">%R");
+  }
+
+  if (m_tree)
+    // FIXME: format code:
+    // FIXME: add get_tree_code_name to both
+#if 1
+    pp_printf (pp, " tree: %p %r<%s>%R", (void *)m_tree,
+	       "warning", // FIXME: dedicated color code?
+	       get_tree_code_name (TREE_CODE (m_tree)));
+#else
+    // seems to work for C++:
+    pp_printf (pp, " tree: %p %r<%s>%R %qE", (void *)m_tree,
+	       "warning", // FIXME: dedicated color code?
+	       get_tree_code_name (TREE_CODE (m_tree)),
+	       m_tree);
+#endif
+
+  const char *new_prefix;
+  if (parent)
+    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
+  else
+    new_prefix = prefix;
+
+  /* Show source code.  */
+  if (m_start != parent_start_loc || m_finish != parent_finish_loc)
+    {
+      location_t range = get_range ();
+      rich_location richloc (line_table, range);
+      diagnostic_context dc;
+      memset (&dc, 0, sizeof (dc));
+      dc.printer = pp;
+      dc.show_caret = true;
+      dc.caret_chars[0] = '^';
+      diagnostic_set_caret_max_width (&dc, pp_line_cutoff (pp));
+      const char *saved_prefix = pp->prefix;
+      const char *source_prefix;
+      const char *begin_color = colorize_start (pp_show_color (pp), "note");
+      const char *end_color = colorize_stop (pp_show_color (pp));
+      source_prefix = ACONCAT ((begin_color, new_prefix, "|:", end_color, NULL));
+      pp_set_prefix (pp, source_prefix);
+      pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+      diagnostic_show_locus (&dc, &richloc, DK_NOTE);
+      pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_NEVER;
+      pp_set_prefix (pp, saved_prefix);
+      /* diagnostic_show_locus expects to add a newline at the start
+	 and ends with a newline.  */
+    }
+  else
+    pp_newline (pp);
+
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      bool is_last_child = child == m_last_child;
+      child->dump (pp, new_prefix, this, is_last_child);
+    }
+}
+
+/* FIXME.  */
+
+const char *
+blt_node::get_name () const
+{
+  return blt_kind_to_name (m_kind);
+}
+
+/* FIXME.  */
+
+location_t
+blt_node::get_range () const
+{
+  return make_location (m_start, m_start, m_finish);
+}
+
+/* FIXME.  */
+
+blt_node *
+blt_node::get_first_child_of_kind (enum blt_kind kind) const
+{
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    if (kind == child->m_kind)
+      return child;
+  return NULL;
+}
+
+void
+blt_node::get_children_of_kind (auto_vec<blt_node *> &out,
+				enum blt_kind kind) const
+{
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    if (kind == child->m_kind)
+      out.safe_push (child);
+}
+
+int
+blt_node::get_index_of_child (blt_node *needle) const
+{
+  int idx = 0;
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      if (child == needle)
+	return idx;
+      idx++;
+    }
+  return -1;
+}
+
+/* FIXME.  */
+
+blt_node *
+blt_node::get_ancestor_of_kind (enum blt_kind kind) const
+{
+  for (blt_node *iter = m_parent; iter; iter = iter->m_parent)
+    if (kind == iter->m_kind)
+      return iter;
+  return NULL;
+}
+
+/* FIXME.  */
+
+blt_node *
+blt_node::find_descendant_satisfying (blt_predicate &pred)
+{
+  /* First, try to find within children, so that we get the deepest node
+     matching the criterion.  */
+  for (blt_node *child = m_first_child; child; child = child->m_next_sibling)
+    {
+      blt_node *within_child = child->find_descendant_satisfying (pred);
+      if (within_child)
+	return within_child;
+    }
+
+  /* Try within this node.  */
+  if (pred.satisfied_by_node_p (*this))
+    return this;
+
+  return NULL;
+}
+
+class contains_location_predicate : public blt_predicate
+{
+ public:
+  contains_location_predicate (const char *filename, int line, int character)
+  : m_filename (filename), m_line (line), m_character (character) {}
+
+  bool satisfied_by_node_p (const blt_node &node) FINAL OVERRIDE
+  {
+    return node.contains_location_p (m_filename, m_line, m_character);
+  }
+
+ private:
+  const char *m_filename;
+  int m_line;
+  int m_character;
+};
+
+/* FIXME.  */
+
+blt_node *
+blt_node::get_descendant_at_location (const char *filename, int line,
+				      int character)
+{
+  contains_location_predicate pred (filename, line, character);
+  return find_descendant_satisfying (pred);
+}
+
+/* FIXME.  */
+
+bool
+blt_node::contains_location_p (const char *filename, int row, int column) const
+{
+  expanded_location el_start = expand_location (m_start);
+  expanded_location el_finish = expand_location (m_finish);
+
+  /* FIXME: share this with diagnostic-show-locus.c:layout_range.  */
+
+  gcc_assert (el_start.line <= el_finish.line);
+  /* ...but the equivalent isn't true for the columns;
+     consider example B in the comment above.  */
+
+  if (row < el_start.line)
+    /* Points before the first line of the range are
+       outside it (corresponding to line 01 in example A
+       and lines 01 and 02 in example B above).  */
+    return false;
+
+  if (row == el_start.line)
+    /* On same line as start of range (corresponding
+       to line 02 in example A and line 03 in example B).  */
+    {
+      if (column < el_start.column)
+	/* Points on the starting line of the range, but
+	   before the column in which it begins.  */
+	return false;
+
+      if (row < el_finish.line)
+	/* This is a multiline range; the point
+	   is within it (corresponds to line 03 in example B
+	   from column 14 onwards) */
+	return true;
+      else
+	{
+	  /* This is a single-line range.  */
+	  gcc_assert (row == el_finish.line);
+	  return column <= el_finish.column;
+	}
+    }
+
+  /* The point is in a line beyond that containing the
+     start of the range: lines 03 onwards in example A,
+     and lines 04 onwards in example B.  */
+  gcc_assert (row > el_start.line);
+
+  if (row > el_finish.line)
+    /* The point is beyond the final line of the range
+       (lines 03 onwards in example A, and lines 06 onwards
+       in example B).  */
+    return false;
+
+  if (row < el_finish.line)
+    {
+      /* The point is in a line that's fully within a multiline
+	 range (e.g. line 04 in example B).  */
+      gcc_assert (el_start.line < el_finish.line);
+      return true;
+    }
+
+  gcc_assert (row ==  el_finish.line);
+
+  return column <= el_finish.column;
+}
+
+/* FIXME.  */
+
+void
+blt_node::assert_invariants () const
+{
+  if (m_parent)
+    {
+      if (m_next_sibling)
+	gcc_assert (m_parent);
+      else
+	gcc_assert (m_parent->m_last_child == this);
+      if (m_prev_sibling)
+	gcc_assert (m_parent);
+      else
+	gcc_assert (m_parent->m_first_child == this);
+    }
+  else
+    {
+      gcc_assert (m_next_sibling == NULL);
+      gcc_assert (m_prev_sibling == NULL);
+    }
+}
+
+/* FIXME.  */
+
+blt_node *
+blt_get_node_for_tree (tree t)
+{
+  if (!t)
+    return NULL;
+  if (!tree_to_blt_map)
+    return NULL;
+  blt_node **slot = tree_to_blt_map->get (t);
+  if (!slot)
+    return NULL;
+  return *slot;
+}
+
+/* FIXME.  */
+
+void
+blt_set_node_for_tree (tree t, blt_node *asnode)
+{
+  if (!t)
+    return;
+  if (!asnode)
+    return;
+
+  if (asnode->get_tree () == NULL)
+    asnode->set_tree (t);
+  else
+    {
+      /* Don't attempt to change; multiple tree nodes can
+	 reference an blt_node *, but an blt_node * references
+	 at most one tree node (e.g. template instantiations).  */
+      gcc_assert (tree_to_blt_map);
+      tree_to_blt_map->get_or_insert (t) = asnode;
+    }
+}
+
+
+static const char * const blt_kind_names[] = {
+#define DEF_BLT_NODE(ENUM_NAME, PRETTY_NAME) PRETTY_NAME,
+#include "blt.def"
+#undef DEF_BLT_NODE
+};
+
+static const char *
+blt_kind_to_name (enum blt_kind kind)
+{
+  return blt_kind_names[kind];
+}
+
+/* FIXME.  */
+
+void
+debug (blt_node *node)
+{
+  node->dump (stderr);
+}
+
+blt_node *the_blt_root_node;
diff --git a/gcc/blt.def b/gcc/blt.def
new file mode 100644
index 0000000..84b0c17
--- /dev/null
+++ b/gcc/blt.def
@@ -0,0 +1,86 @@
+/* Bonus location trees.
+   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/>.  */
+
+/* The first set of DEF_BLT_NODE corresponds to enum cpp_ttype.  */
+
+#ifndef TTYPE_TABLE
+#error TTYPE_TABLE not defined
+#endif
+
+#define OP(e, s) DEF_BLT_NODE (BLT_TOKEN_OP_##e, s "-token")
+#define TK(e, s) DEF_BLT_NODE (BLT_TOKEN_TK_##e, #e "-token")
+
+TTYPE_TABLE
+
+#undef OP
+#undef TK
+
+DEF_BLT_NODE (BLT_KEYWORD, "keyword")
+
+/* Additional nodes kinds, beyond enum cpp_ttype.
+   These are named after non-terminals within the C and C++ grammar.
+   They're just IDs; they don't need to mean anything for other languages.  */
+
+DEF_BLT_NODE (BLT_ASM_SPECIFICATION, "asm-specification")
+DEF_BLT_NODE (BLT_ASSIGNMENT_EXPRESSION, "assignment-expression")
+DEF_BLT_NODE (BLT_BLOCK_DECLARATION, "block-declaration")
+DEF_BLT_NODE (BLT_CLASS_HEAD, "class-head")
+DEF_BLT_NODE (BLT_CLASS_KEY, "class-key")
+DEF_BLT_NODE (BLT_CLASS_SPECIFIER, "class-specifier")
+DEF_BLT_NODE (BLT_CV_QUALIFIER, "cv-qualifier")
+DEF_BLT_NODE (BLT_CV_QUALIFIER_SEQ, "cv-qualifier-seq")
+DEF_BLT_NODE (BLT_DECLARATION, "declaration")
+DEF_BLT_NODE (BLT_DECLARATION_SEQ, "declaration-seq")
+DEF_BLT_NODE (BLT_DECLARATION_SPECIFIERS, "declaration-specifiers")
+DEF_BLT_NODE (BLT_DECLARATOR, "declarator")
+DEF_BLT_NODE (BLT_DECL_SPECIFIER, "decl-specifier")
+DEF_BLT_NODE (BLT_DECL_SPECIFIER_SEQ, "decl-specifier-seq")
+DEF_BLT_NODE (BLT_DIRECT_DECLARATOR, "direct-declarator")
+DEF_BLT_NODE (BLT_EXCEPTION_DECLARATION, "exception-declaration")
+DEF_BLT_NODE (BLT_EXPLICIT_INSTANTIATION, "explicit-instantiation")
+DEF_BLT_NODE (BLT_EXPLICIT_SPECIALIZATION, "explicit-specialization")
+DEF_BLT_NODE (BLT_EXPRESSION, "expression")
+DEF_BLT_NODE (BLT_EXPRESSION_LIST, "expression-list")
+DEF_BLT_NODE (BLT_EXTERNAL_DECLARATION, "external-declaration")
+DEF_BLT_NODE (BLT_FUNCTION_DEFINITION, "function-definition")
+DEF_BLT_NODE (BLT_FUNCTION_TRY_BLOCK, "function-try-block")
+DEF_BLT_NODE (BLT_HANDLER, "handler")
+DEF_BLT_NODE (BLT_HANDLER_SEQ, "handler-seq")
+DEF_BLT_NODE (BLT_IDENTIFIER, "identifier")
+DEF_BLT_NODE (BLT_ID_EXPRESSION, "id-expression")
+DEF_BLT_NODE (BLT_MEMBER_DECLARATION, "member-declaration")
+DEF_BLT_NODE (BLT_NONEMPTY_EXPR_LIST, "nonempty-expr-list")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION, "parameter-declaration")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION_CLAUSE, "parameter-declaration-clause")
+DEF_BLT_NODE (BLT_PARAMETER_DECLARATION_LIST, "parameter-declaration-list")
+DEF_BLT_NODE (BLT_PARAMETER_LIST, "parameter-list")
+DEF_BLT_NODE (BLT_POSTFIX_EXPRESSION, "postfix-expression")
+DEF_BLT_NODE (BLT_PRIMARY_EXPRESSION, "primary-expression")
+DEF_BLT_NODE (BLT_SIMPLE_DECLARATION, "simple-declaration")
+DEF_BLT_NODE (BLT_STRUCT_CONTENTS, "struct-contents")
+DEF_BLT_NODE (BLT_STRUCT_OR_UNION_SPECIFIER, "struct-or-union-specifier")
+DEF_BLT_NODE (BLT_TEMPLATE_DECLARATION, "template-declaration")
+DEF_BLT_NODE (BLT_TEMPLATE_PARAMETER_LIST, "template-parameter-list")
+DEF_BLT_NODE (BLT_THROW_EXPRESSION, "throw-expression")
+DEF_BLT_NODE (BLT_TRANSLATION_UNIT, "translation-unit")
+DEF_BLT_NODE (BLT_TRY_BLOCK, "try-block")
+DEF_BLT_NODE (BLT_TYPE_ID_LIST, "type-id-list")
+DEF_BLT_NODE (BLT_TYPE_SPECIFIER, "type-specifier")
+DEF_BLT_NODE (BLT_UNARY_EXPRESSION, "unary-expression")
+DEF_BLT_NODE (BLT_UNQUALIFIED_ID, "unqualified-id")
diff --git a/gcc/blt.h b/gcc/blt.h
new file mode 100644
index 0000000..b253938
--- /dev/null
+++ b/gcc/blt.h
@@ -0,0 +1,145 @@
+/* Bonus location tree information.
+   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_BLT_H
+#define GCC_BLT_H
+
+/* Sometimes we need additional location information.
+
+   The "tree" type represents a node within an abstract syntax tree,
+   but this is sometimes too abstract - sometimes we want the locations
+   of the clauses and tokens that are abstracted away by the frontends.
+
+   In theory we could generate the full parse tree ("concrete syntax tree"),
+   showing every production followed to parse the input, but it is likely
+   to be unwieldy: large and difficult to navigate.
+
+   So this file implements a middle-ground: an additional tree of parse
+   information, more concrete than "tree", but not the fully parse tree.
+
+   There is a partial mapping between "tree" and blt_node: a blt_node
+   can reference a tree, and a tree can reference a blt_node (though
+   typically the mapping is very sparse; most don't).  This allows up
+   to go from e.g. a function_decl in the tree world and navigate
+   pertinent parts of the syntax that was used to declare it.
+
+   Working title = "BLT" as a backronym for "bonus location tree" (but
+   actually a reference to a sandwich) - doesn't clash with anything else
+   in the source tree.  */
+
+#include "cpplib.h"
+
+/* An ID for a kind of node within the tree.  */
+
+enum blt_kind
+{
+#define DEF_BLT_NODE(ENUM_NAME, PRETTY_NAME) ENUM_NAME,
+#include "blt.def"
+#undef DEF_BLT_NODE
+};
+
+class blt_node;
+
+/* Would use a lambda.  */
+
+class blt_predicate
+{
+ public:
+  virtual bool satisfied_by_node_p (const blt_node &node) = 0;
+};
+
+/* A syntax node: either a token, a non-terminal, or a simplified set of
+   non-terminals.  */
+
+class blt_node
+{
+public:
+  blt_node (enum blt_kind kind, location_t start);
+
+  /* Structural manipulation.  */
+  void add_child (blt_node *child);
+  void replace_child (blt_node *old, blt_node *new_);
+  void make_orphan ();
+
+  /* Modification.  */
+
+  void set_finish (location_t loc) { m_finish = loc; }
+  void set_tree (tree t);
+  void set_kind (enum blt_kind kind) { m_kind = kind; }
+
+  /* Dumping.  */
+  void dump (FILE *) const;
+  void dump (pretty_printer *pp, const char *prefix,
+	     const blt_node *parent, bool is_last_child) const;
+
+  /* Accessors.  */
+  enum blt_kind get_kind () const { return m_kind; }
+  const char *get_name () const;
+  blt_node *get_parent () const { return m_parent; }
+  location_t get_start () const { return m_start; }
+  location_t get_finish () const { return m_finish; }
+  location_t get_range () const;
+  tree get_tree () const { return m_tree; }
+
+  blt_node *get_first_child_of_kind (enum blt_kind kind) const;
+  void get_children_of_kind (auto_vec<blt_node *> &out,
+			     enum blt_kind kind) const;
+
+  int get_index_of_child (blt_node *needle) const;
+
+  blt_node *get_ancestor_of_kind (enum blt_kind kind) const;
+
+  blt_node *find_descendant_satisfying (blt_predicate &pred);
+
+  blt_node *get_descendant_at_location (const char *filename, int line,
+					int character);
+
+  bool contains_location_p (const char *filename, int line,
+			    int character) const;
+
+private:
+  void assert_invariants () const;
+
+private:
+  enum blt_kind m_kind;
+  blt_node *m_parent;
+  blt_node *m_first_child;
+  blt_node *m_last_child;
+  blt_node *m_prev_sibling;
+  blt_node *m_next_sibling;
+#if 1
+  location_t m_start;
+  location_t m_finish;
+#else
+  // Tokens are currently released after lexing...
+  cp_token *m_first_token;
+  cp_token *m_last_token;
+#endif
+  tree m_tree;
+};
+
+extern blt_node *blt_get_node_for_tree (tree);
+extern void blt_set_node_for_tree (tree, blt_node *);
+
+extern void DEBUG_FUNCTION debug (blt_node *);
+
+/* FIXME.  */
+extern blt_node *the_blt_root_node;
+
+#endif  /* GCC_BLT_H  */
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index ea0e01b..c2802ed 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -165,11 +165,11 @@ static void
 c_diagnostic_finalizer (diagnostic_context *context,
 			diagnostic_info *diagnostic)
 {
+  pp_destroy_prefix (context->printer);
   diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
   /* By default print macro expansion contexts in the diagnostic
      finalizer -- for tokens resulting from macro expansion.  */
   virt_loc_aware_diagnostic_finalizer (context, diagnostic);
-  pp_destroy_prefix (context->printer);
   pp_flush (context->printer);
 }
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 6ecbfca..4d27efd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1189,6 +1189,10 @@ fasm
 C ObjC C++ ObjC++ Var(flag_no_asm, 0)
 Recognize the \"asm\" keyword.
 
+fblt
+C ObjC C++ ObjC++ Var(flag_blt)
+Capture additional location information.  FIXME: name.
+
 ; Define extra predefined macros for use in libgcc.
 fbuilding-libgcc
 C ObjC C++ ObjC++ Undocumented Var(flag_building_libgcc)
@@ -1358,6 +1362,10 @@ fdump-ada-spec-slim
 C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec_slim)
 Write all declarations as Ada code for the given file only.
 
+fdump-blt
+C ObjC C++ ObjC++ Var(flag_dump_blt)
+Dump the extra location information from -fblt.  FIXME: name.
+
 felide-constructors
 C++ ObjC++ Var(flag_elide_constructors) Init(1)
 
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 64a1107..e75c29a 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "blt.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
@@ -4523,6 +4524,7 @@ build_array_declarator (location_t loc,
 	}
       current_scope->had_vla_unspec = true;
     }
+  declarator->bltnode = NULL;
   return declarator;
 }
 
@@ -4647,6 +4649,8 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
 			 deprecated_state);
   if (!decl || decl == error_mark_node)
     return NULL_TREE;
+  if (declarator->bltnode)
+    declarator->bltnode->set_tree (decl);
 
   if (expr)
     add_stmt (fold_convert (void_type_node, expr));
@@ -8542,6 +8546,8 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
 
   decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, true, NULL,
 			  &attributes, NULL, NULL, DEPRECATED_NORMAL);
+  if (declarator->bltnode)
+    declarator->bltnode->set_tree (decl1);
   invoke_plugin_callbacks (PLUGIN_START_PARSE_FUNCTION, decl1);
 
   /* If the declarator is not suitable for a function definition,
@@ -9659,6 +9665,7 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
   ret->kind = cdk_attrs;
   ret->declarator = target;
   ret->u.attrs = attrs;
+  ret->bltnode = NULL;
   return ret;
 }
 
@@ -9667,12 +9674,14 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
 
 struct c_declarator *
 build_function_declarator (struct c_arg_info *args,
-			   struct c_declarator *target)
+			   struct c_declarator *target,
+			   blt_node *bltnode)
 {
   struct c_declarator *ret = XOBNEW (&parser_obstack, struct c_declarator);
   ret->kind = cdk_function;
   ret->declarator = target;
   ret->u.arg_info = args;
+  ret->bltnode = bltnode;
   return ret;
 }
 
@@ -9688,6 +9697,7 @@ build_id_declarator (tree ident)
   ret->u.id = ident;
   /* Default value - may get reset to a more precise location. */
   ret->id_loc = input_location;
+  ret->bltnode = NULL;
   return ret;
 }
 
@@ -9714,6 +9724,7 @@ make_pointer_declarator (struct c_declspecs *type_quals_attrs,
   ret->kind = cdk_pointer;
   ret->declarator = itarget;
   ret->u.pointer_quals = quals;
+  ret->bltnode = NULL;
   return ret;
 }
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 9398652..0150a6a 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "read-rtl-function.h"
 #include "run-rtl-passes.h"
 #include "intl.h"
+#include "blt.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -206,8 +207,112 @@ struct GTY(()) c_parser {
   /* Buffer to hold all the tokens from parsing the vector attribute for the
      SIMD-enabled functions (formerly known as elemental functions).  */
   vec <c_token, va_gc> *cilk_simd_fn_tokens;
+
+  location_t last_token_location;
+  blt_node * GTY((skip)) blt_root_node;
+  blt_node * GTY((skip)) blt_current_node;
+};
+
+/* FIXME.  */
+// RAII-style highlighting of the AST.
+
+/* FIXME.  */
+
+class auto_blt_node
+{
+public:
+  auto_blt_node (c_parser* parser, enum blt_kind kind);
+  ~auto_blt_node ();
+
+  void set_tree (tree node);
+
+private:
+  c_parser *m_parser;
 };
 
+/* FIXME.  */
+
+#define AUTO_BLT_NODE(PARSER, KIND) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND))
+
+#define CURRENT_BLT_NODE (parser->blt_current_node)
+
+#define BLT_ADD_NEXT_TOKEN(PARSER) \
+  add_next_token (PARSER)
+
+#define BLT_SET_TREE(TREE_NODE) \
+  tmp_blt_node.set_tree (TREE_NODE);
+
+#if 0
+static void
+add_next_token (c_parser *parser)
+{
+  if (!flag_blt)
+    return;
+
+  c_token *token = c_parser_peek_token (parser);
+  blt_node *parent = parser->blt_current_node;
+  blt_node *node = new blt_node (BLT_TOKEN, token->location);
+  parent->add_child (node);
+  // FIXME: get a meaningful string for the token
+  node->set_finish (token->location);
+}
+#endif
+
+/* FIXME.  */
+
+auto_blt_node::auto_blt_node (c_parser *parser, enum blt_kind kind)
+: m_parser (parser)
+{
+  if (!flag_blt)
+    return;
+
+  c_token *first_token = c_parser_peek_token (parser);
+  blt_node *parent = parser->blt_current_node;
+  blt_node *node = new blt_node (kind, first_token->location);
+  parser->blt_current_node = node;
+  if (parent)
+    parent->add_child (node);
+  else
+    parser->blt_root_node = node;
+}
+
+/* FIXME.  */
+
+auto_blt_node::~auto_blt_node ()
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_finish (m_parser->last_token_location);
+
+  if (0)
+    {
+      location_t start = node->get_start ();
+      location_t finish = node->get_finish ();
+      location_t range = make_location (start, start, finish);
+      inform (range, "%qs", node->get_name ());
+    }
+
+#if 0
+  if (m_expr_ptr)
+    node->set_tree (m_expr_ptr->get_value ());
+#endif
+
+  m_parser->blt_current_node = node->get_parent ();
+}
+
+void
+auto_blt_node::set_tree (tree tree_node)
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_tree (tree_node);
+}
+
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
 
 c_token *
@@ -770,6 +875,7 @@ c_parser_consume_token (c_parser *parser)
   gcc_assert (parser->tokens[0].type != CPP_EOF);
   gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL);
   gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA);
+  parser->last_token_location = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
   else if (parser->tokens_avail == 2)
@@ -1334,6 +1440,8 @@ static void c_parser_parse_rtl_body (c_parser *parser, char *start_with_pass);
 static void
 c_parser_translation_unit (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TRANSLATION_UNIT);
+
   if (c_parser_next_token_is (parser, CPP_EOF))
     {
       pedwarn (c_parser_peek_token (parser)->location, OPT_Wpedantic,
@@ -1386,6 +1494,8 @@ c_parser_translation_unit (c_parser *parser)
 static void
 c_parser_external_declaration (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXTERNAL_DECLARATION);
+
   int ext;
   switch (c_parser_peek_token (parser)->type)
     {
@@ -2376,6 +2486,8 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 		    bool alignspec_ok, bool auto_type_ok,
 		    enum c_lookahead_kind la)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION_SPECIFIERS);
+
   bool attrs_ok = start_attr_ok;
   bool seen_type = specs->typespec_kind != ctsk_none;
 
@@ -2861,6 +2973,8 @@ c_parser_enum_specifier (c_parser *parser)
 static struct c_typespec
 c_parser_struct_or_union_specifier (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_STRUCT_OR_UNION_SPECIFIER);
+
   struct c_typespec ret;
   tree attrs;
   tree ident = NULL_TREE;
@@ -2896,6 +3010,8 @@ c_parser_struct_or_union_specifier (c_parser *parser)
     {
       /* Parse a struct or union definition.  Start the scope of the
 	 tag before parsing components.  */
+      AUTO_BLT_NODE (parser, BLT_STRUCT_CONTENTS);
+
       struct c_struct_parse_info *struct_info;
       tree type = start_struct (struct_loc, code, ident, &struct_info);
       tree postfix_attrs;
@@ -3004,6 +3120,10 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
       timevar_pop (TV_PARSE_STRUCT);
+
+      if (CURRENT_BLT_NODE)
+	CURRENT_BLT_NODE->set_tree (ret.spec);
+
       return ret;
     }
   else if (!ident)
@@ -3015,7 +3135,12 @@ c_parser_struct_or_union_specifier (c_parser *parser)
       ret.expr_const_operands = true;
       return ret;
     }
+
   ret = parser_xref_tag (ident_loc, code, ident);
+
+  if (CURRENT_BLT_NODE)
+    CURRENT_BLT_NODE->set_tree (ret.spec);
+
   return ret;
 }
 
@@ -3391,6 +3516,8 @@ struct c_declarator *
 c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 		     bool *seen_id)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATOR);
+
   /* Parse any initial pointer part.  */
   if (c_parser_next_token_is (parser, CPP_MULT))
     {
@@ -3417,6 +3544,8 @@ static struct c_declarator *
 c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 			    bool *seen_id)
 {
+  AUTO_BLT_NODE (parser, BLT_DIRECT_DECLARATOR);
+
   /* The direct declarator must start with an identifier (possibly
      omitted) or a parenthesized declarator (possibly abstract).  In
      an ordinary declarator, initial parentheses must start a
@@ -3460,6 +3589,7 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
     {
       struct c_declarator *inner
 	= build_id_declarator (c_parser_peek_token (parser)->value);
+      inner->bltnode = CURRENT_BLT_NODE;
       *seen_id = true;
       inner->id_loc = c_parser_peek_token (parser)->location;
       c_parser_consume_token (parser);
@@ -3496,7 +3626,8 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 	    {
 	      inner
 		= build_function_declarator (args,
-					     build_id_declarator (NULL_TREE));
+					     build_id_declarator (NULL_TREE),
+					     CURRENT_BLT_NODE);
 	      return c_parser_direct_declarator_inner (parser, *seen_id,
 						       inner);
 	    }
@@ -3644,7 +3775,7 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 	return NULL;
       else
 	{
-	  inner = build_function_declarator (args, inner);
+	  inner = build_function_declarator (args, inner, CURRENT_BLT_NODE);
 	  return c_parser_direct_declarator_inner (parser, id_present, inner);
 	}
     }
@@ -3659,6 +3790,8 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 static struct c_arg_info *
 c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_LIST);
+
   push_scope ();
   declare_parm_level ();
   /* If the list starts with an identifier, it is an identifier list.
@@ -3830,6 +3963,8 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 static struct c_parm *
 c_parser_parameter_declaration (c_parser *parser, tree attrs)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION);
+
   struct c_declspecs *specs;
   struct c_declarator *declarator;
   tree prefix_attrs;
@@ -6385,6 +6520,8 @@ static struct c_expr
 c_parser_expr_no_commas (c_parser *parser, struct c_expr *after,
 			 tree omp_atomic_lhs)
 {
+  AUTO_BLT_NODE (parser, BLT_ASSIGNMENT_EXPRESSION);
+
   struct c_expr lhs, rhs, ret;
   enum tree_code code;
   location_t op_location, exp_location;
@@ -8588,6 +8725,8 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 static struct c_expr
 c_parser_expression (c_parser *parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPRESSION);
+
   location_t tloc = c_parser_peek_token (parser)->location;
   struct c_expr expr;
   expr = c_parser_expr_no_commas (parser, NULL);
@@ -8675,6 +8814,8 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
 		    vec<location_t> *locations,
 		    unsigned int *literal_zero_mask)
 {
+  AUTO_BLT_NODE (parser, BLT_NONEMPTY_EXPR_LIST);
+
   vec<tree, va_gc> *ret;
   vec<tree, va_gc> *orig_types;
   struct c_expr expr;
@@ -18179,6 +18320,12 @@ c_parse_file (void)
     using_eh_for_cleanups ();
 
   c_parser_translation_unit (the_parser);
+
+  if (flag_blt && flag_dump_blt)
+    the_parser->blt_root_node->dump (stderr);
+
+  the_blt_root_node = the_parser->blt_root_node;
+
   the_parser = NULL;
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 5fa32a4..e60d473 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+class blt_node;
+
 /* struct lang_identifier is private to c-decl.c, but langhooks.c needs to
    know how big it is.  This is sanity-checked in c-decl.c.  */
 #define C_SIZEOF_STRUCT_LANG_IDENTIFIER \
@@ -142,6 +144,11 @@ struct c_expr
      of this expression.  */
   location_t get_start () const { return src_range.m_start; }
   location_t get_finish () const { return src_range.m_finish; }
+  location_t get_location () const
+  {
+    return make_location (src_range.m_start, src_range.m_start,
+			  src_range.m_finish);
+  }
 
   /* Set the value to error_mark_node whilst ensuring that src_range
      is initialized.  */
@@ -447,6 +454,7 @@ struct c_declarator {
     /* For attributes.  */
     tree attrs;
   } u;
+  blt_node *bltnode;
 };
 
 /* A type name.  */
@@ -572,7 +580,8 @@ extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
 extern struct c_declarator *build_attrs_declarator (tree,
 						    struct c_declarator *);
 extern struct c_declarator *build_function_declarator (struct c_arg_info *,
-						       struct c_declarator *);
+						       struct c_declarator *,
+						       blt_node *);
 extern struct c_declarator *build_id_declarator (tree);
 extern struct c_declarator *make_pointer_declarator (struct c_declspecs *,
 						     struct c_declarator *);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index ff239e2..a439355 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gomp-constants.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "blt.h"
 
 /* Possible cases of implicit bad conversions.  Used to select
    diagnostic messages in convert_for_assignment.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index 4021622..05a47a4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1742,6 +1742,10 @@ flra-remat
 Common Report Var(flag_lra_remat) Optimization
 Do CFG-sensitive rematerialization in LRA.
 
+flsp=
+Common Joined RejectNegative UInteger Var(flag_lsp) Init(-1)
+-flsp=<port-number>  FIXME.
+
 flto
 Common
 Enable link-time optimization.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c15b8e4..5297f0b 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "c-family/c-objc.h"
 #include "internal-fn.h"
+#include "blt.h"
 
 /* The various kinds of conversion.  */
 
@@ -6550,6 +6551,74 @@ maybe_print_user_conv_context (conversion *convs)
 	}
 }
 
+/* FIXME.  */
+
+static blt_node *
+get_blt_node_for_function_decl (tree fndecl)
+{
+  if (!flag_blt)
+    return NULL;
+
+  blt_node *node = blt_get_node_for_tree (fndecl);
+  if (node)
+    return node;
+
+  tree template_decl = DECL_TI_TEMPLATE (fndecl);
+  if (template_decl)
+    {
+      node = blt_get_node_for_tree (template_decl);
+      if (node)
+        return node;
+    }
+
+  return NULL;
+}
+
+/* FIXME.  */
+
+static location_t
+get_fndecl_argument_location (tree fndecl, int argnum)
+{
+  location_t loc = DECL_SOURCE_LOCATION (fndecl);
+  blt_node *node = get_blt_node_for_function_decl (fndecl);
+  if (!node)
+    return loc;
+
+  /* For "member-declaration", expect the "direct-declarator" to be
+     inside a "declarator".  */
+  if (node->get_kind () == BLT_MEMBER_DECLARATION)
+    {
+      node = node->get_first_child_of_kind (BLT_DECLARATOR);
+      if (!node)
+        return loc;
+      node = node->get_first_child_of_kind (BLT_DIRECT_DECLARATOR);
+      if (!node)
+        return loc;
+    }
+
+  if (node->get_kind () != BLT_DIRECT_DECLARATOR)
+    return loc;
+
+  blt_node *pdc
+    = node->get_first_child_of_kind (BLT_PARAMETER_DECLARATION_CLAUSE);
+  if (!pdc)
+    return loc;
+
+  blt_node *pdl = pdc->get_first_child_of_kind (BLT_PARAMETER_DECLARATION_LIST);
+  if (!pdl)
+    return loc;
+
+  auto_vec<blt_node *> params;
+  pdl->get_children_of_kind (params, BLT_PARAMETER_DECLARATION);
+
+  if (argnum >= (int)params.length ())
+    return loc;
+
+  blt_node *param = params[argnum];
+  return param->get_range ();
+}
+
+
 /* Perform the conversions in CONVS on the expression EXPR.  FN and
    ARGNUM are used for diagnostics.  ARGNUM is zero based, -1
    indicates the `this' argument of a method.  INNER is nonzero when
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1d53e69..1479f64 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "hard-reg-set.h"
 #include "function.h"
 
+class blt_node;
+
 /* In order for the format checking to accept the C++ front end
    diagnostic framework extensions, you must include this file before
    diagnostic-core.h, not after.  We override the definition of GCC_DIAG_STYLE
@@ -5455,6 +5457,8 @@ struct cp_parameter_declarator {
   tree default_argument;
   /* True iff this is a template parameter pack.  */
   bool template_parameter_pack_p;
+  /* FIXME.  */
+  blt_node *asnode;
 };
 
 /* A declarator.  */
@@ -5530,6 +5534,7 @@ struct cp_declarator {
       bool rvalue_ref;
     } reference;
   } u;
+  blt_node *asnode;
 };
 
 /* A level of template instantiation.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 8c8cf84..fd68731 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cilk.h"
 #include "builtins.h"
 #include "gimplify.h"
+#include "blt.h"
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
@@ -9940,12 +9941,12 @@ name_unnamed_type (tree type, tree decl)
    declarator, in cases like "struct S;"), or the ERROR_MARK_NODE if an
    error occurs. */
 
-tree
-grokdeclarator (const cp_declarator *declarator,
-		cp_decl_specifier_seq *declspecs,
-		enum decl_context decl_context,
-		int initialized,
-		tree* attrlist)
+static tree
+grokdeclarator_1 (const cp_declarator *declarator,
+		  cp_decl_specifier_seq *declspecs,
+		  enum decl_context decl_context,
+		  int initialized,
+		  tree* attrlist)
 {
   tree type = NULL_TREE;
   int longlong = 0;
@@ -12419,6 +12420,20 @@ grokdeclarator (const cp_declarator *declarator,
     return decl;
   }
 }
+
+tree
+grokdeclarator (const cp_declarator *declarator,
+		cp_decl_specifier_seq *declspecs,
+		enum decl_context decl_context,
+		int initialized,
+		tree* attrlist)
+{
+  tree result = grokdeclarator_1 (declarator, declspecs, decl_context,
+				  initialized, attrlist);
+  if (declarator && declarator->asnode)
+    declarator->asnode->set_tree (result);
+  return result;
+}
 
 /* Subroutine of start_function.  Ensure that each of the parameter
    types (as listed in PARMS) is complete, as is required for a
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4714bc6..1700a4e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cp-cilkplus.h"
 #include "gcc-rich-location.h"
 #include "tree-iterator.h"
+#include "blt.h"
 
 
 /* The lexer.  */
@@ -1348,6 +1349,139 @@ cp_token_cache_new (cp_token *first, cp_token *last)
   return cache;
 }
 
+
+
+/* FIXME.  */
+// RAII-style highlighting of the AST.
+
+/* FIXME.  */
+
+class auto_blt_node
+{
+public:
+  auto_blt_node (cp_parser* parser, enum blt_kind kind,
+                 cp_expr *expr_ptr = NULL);
+  ~auto_blt_node ();
+
+  void set_tree (tree node);
+
+private:
+  cp_parser *m_parser;
+  cp_expr *m_expr_ptr;
+  blt_node *m_parent;
+};
+
+/* FIXME.  */
+
+#define AUTO_BLT_NODE(PARSER, KIND) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND))
+
+#define AUTO_BLT_NODE_WITH_RETURN(PARSER, KIND, ADDR_OF_CP_EXPR) \
+  auto_blt_node tmp_blt_node ((PARSER), (KIND), (ADDR_OF_CP_EXPR))
+
+#define CURRENT_BLT_NODE (parser->blt_current_node)
+
+#define BLT_ADD_NEXT_TOKEN(PARSER) \
+  add_node_for_next_token (PARSER)
+
+#define ADD_NODE_FOR_NEXT_TOKEN(KIND) \
+  add_node_for_next_token (parser, (KIND))
+
+#define BLT_SET_TREE(TREE_NODE) \
+  tmp_blt_node.set_tree (TREE_NODE);
+
+/* FIXME.  */
+
+static blt_node *
+add_node_for_next_token (cp_parser *parser)
+{
+  if (!flag_blt)
+    return NULL;
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  blt_node *parent = parser->blt_current_node;
+  enum blt_kind kind = (enum blt_kind)token->type;
+  blt_node *node = new blt_node (kind, token->location);
+  parent->add_child (node);
+  node->set_finish (token->location);
+  return node;
+}
+
+/* FIXME.  */
+
+static blt_node *
+add_node_for_next_token (cp_parser *parser, enum blt_kind kind)
+{
+  if (!flag_blt)
+    return NULL;
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  blt_node *parent = parser->blt_current_node;
+  blt_node *node = new blt_node (kind, token->location);
+  parent->add_child (node);
+  node->set_finish (token->location);
+  return node;
+}
+
+/* FIXME.  */
+
+auto_blt_node::auto_blt_node (cp_parser *parser, enum blt_kind kind,
+                              cp_expr *expr_ptr)
+: m_parser (parser), m_expr_ptr (expr_ptr),
+  m_parent (parser->blt_current_node)
+{
+  if (!flag_blt)
+    return;
+
+  cp_token *first_token = cp_lexer_peek_token (parser->lexer);
+  blt_node *node = new blt_node (kind, first_token->location);
+  parser->blt_current_node = node;
+  if (m_parent)
+    m_parent->add_child (node);
+  else
+    parser->blt_root_node = node;
+}
+
+/* FIXME.  */
+
+auto_blt_node::~auto_blt_node ()
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+
+  cp_token_position prev
+    = cp_lexer_previous_token_position (m_parser->lexer);
+  node->set_finish (cp_lexer_token_at (m_parser->lexer, prev)->location);
+
+  if (0)
+    {
+      location_t start = node->get_start ();
+      location_t finish = node->get_finish ();
+      location_t range = make_location (start, start, finish);
+      inform (range, "%qs", node->get_name ());
+    }
+
+  if (m_expr_ptr)
+    node->set_tree (m_expr_ptr->get_value ());
+
+  /* Use stashed parent, rather than the curent node's parent
+     to allow for reparenting within the tree.  */
+  m_parser->blt_current_node = m_parent;
+}
+
+void
+auto_blt_node::set_tree (tree tree_node)
+{
+  if (!flag_blt)
+    return;
+
+  blt_node *node = m_parser->blt_current_node;
+  node->set_tree (tree_node);
+}
+
+
 /* Diagnose if #pragma omp declare simd isn't followed immediately
    by function declaration or definition.  */
 
@@ -1456,6 +1590,7 @@ make_declarator (cp_declarator_kind kind)
   declarator->declarator = NULL;
   declarator->parameter_pack_p = false;
   declarator->id_loc = UNKNOWN_LOCATION;
+  declarator->asnode = NULL;
 
   return declarator;
 }
@@ -1691,7 +1826,8 @@ cp_parameter_declarator *
 make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers,
 			   cp_declarator *declarator,
 			   tree default_argument,
-			   bool template_parameter_pack_p = false)
+			   bool template_parameter_pack_p = false,
+                           blt_node *asnode = NULL)
 {
   cp_parameter_declarator *parameter;
 
@@ -1705,6 +1841,7 @@ make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers,
   parameter->declarator = declarator;
   parameter->default_argument = default_argument;
   parameter->template_parameter_pack_p = template_parameter_pack_p;
+  parameter->asnode = asnode;
 
   return parameter;
 }
@@ -3846,6 +3983,10 @@ cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
 
+  AUTO_BLT_NODE (parser, BLT_IDENTIFIER);
+  if (0)
+    BLT_ADD_NEXT_TOKEN (parser);
+
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
@@ -4351,19 +4492,23 @@ cp_parser_translation_unit (cp_parser* parser)
 
   bool success;
 
-  /* Create the declarator obstack, if necessary.  */
-  if (!cp_error_declarator)
-    {
-      gcc_obstack_init (&declarator_obstack);
-      /* Create the error declarator.  */
-      cp_error_declarator = make_declarator (cdk_error);
-      /* Create the empty parameter list.  */
-      no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE);
-      /* Remember where the base of the declarator obstack lies.  */
-      declarator_obstack_base = obstack_next_free (&declarator_obstack);
-    }
+  {
+    AUTO_BLT_NODE (parser, BLT_TRANSLATION_UNIT);
 
-  cp_parser_declaration_seq_opt (parser);
+    /* Create the declarator obstack, if necessary.  */
+    if (!cp_error_declarator)
+      {
+        gcc_obstack_init (&declarator_obstack);
+        /* Create the error declarator.  */
+        cp_error_declarator = make_declarator (cdk_error);
+        /* Create the empty parameter list.  */
+        no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE);
+        /* Remember where the base of the declarator obstack lies.  */
+        declarator_obstack_base = obstack_next_free (&declarator_obstack);
+      }
+
+    cp_parser_declaration_seq_opt (parser);
+  }
 
   /* If there are no tokens left then all went well.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_EOF))
@@ -4767,6 +4912,8 @@ cp_parser_primary_expression (cp_parser *parser,
 			      bool decltype_p,
 			      cp_id_kind *idk)
 {
+  AUTO_BLT_NODE (parser, BLT_PRIMARY_EXPRESSION);
+
   cp_token *token = NULL;
 
   /* Assume the primary expression is not an id-expression.  */
@@ -5397,6 +5544,8 @@ cp_parser_id_expression (cp_parser *parser,
 			 bool declarator_p,
 			 bool optional_p)
 {
+  AUTO_BLT_NODE (parser, BLT_ID_EXPRESSION);
+
   bool global_scope_p;
   bool nested_name_specifier_p;
 
@@ -5531,6 +5680,8 @@ cp_parser_unqualified_id (cp_parser* parser,
 			  bool declarator_p,
 			  bool optional_p)
 {
+  AUTO_BLT_NODE (parser, BLT_UNQUALIFIED_ID);
+
   cp_token *token;
 
   /* Peek at the next token.  */
@@ -6391,6 +6542,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   bool is_member_access = false;
   int saved_in_statement = -1;
 
+  AUTO_BLT_NODE_WITH_RETURN (parser, BLT_POSTFIX_EXPRESSION, &postfix_expression);
+
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
@@ -6410,6 +6563,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
+        BLT_ADD_NEXT_TOKEN (parser);
+
 	/* All of these can be handled in the same way from the point
 	   of view of parsing.  Begin by consuming the token
 	   identifying the cast.  */
@@ -6496,6 +6651,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	bool saved_in_type_id_in_expr_p;
 
 	/* Consume the `typeid' token.  */
+        BLT_ADD_NEXT_TOKEN (parser);
 	cp_lexer_consume_token (parser->lexer);
 	/* Look for the `(' token.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
@@ -7510,6 +7666,14 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool *non_constant_p,
 					 location_t *close_paren_loc)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPRESSION_LIST);
+
+  // FIXME:
+#if 0
+  if (out_asnode)
+    *out_asnode = CURRENT_BLT_NODE;
+#endif
+
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
   tree identifier = NULL_TREE;
@@ -7788,6 +7952,8 @@ static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
+  AUTO_BLT_NODE (parser, BLT_UNARY_EXPRESSION);
+
   cp_token *token;
   enum tree_code unary_operator;
 
@@ -9166,6 +9332,7 @@ static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
+  AUTO_BLT_NODE (parser, BLT_ASSIGNMENT_EXPRESSION);
   cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
@@ -12339,6 +12506,8 @@ cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p,
 static void
 cp_parser_declaration_seq_opt (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION_SEQ);
+
   while (true)
     {
       cp_token *token;
@@ -12410,6 +12579,8 @@ cp_parser_declaration_seq_opt (cp_parser* parser)
 static void
 cp_parser_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATION);
+
   cp_token token1;
   cp_token token2;
   int saved_pedantic;
@@ -12554,6 +12725,8 @@ cp_parser_block_declaration (cp_parser *parser,
       return;
     }
 
+  AUTO_BLT_NODE (parser, BLT_BLOCK_DECLARATION);
+
   /* Peek at the next token to figure out which kind of declaration is
      present.  */
   token1 = cp_lexer_peek_token (parser->lexer);
@@ -12638,6 +12811,8 @@ cp_parser_simple_declaration (cp_parser* parser,
 			      bool function_definition_allowed_p,
 			      tree *maybe_range_for_decl)
 {
+  AUTO_BLT_NODE (parser, BLT_SIMPLE_DECLARATION);
+
   cp_decl_specifier_seq decl_specifiers;
   int declares_class_or_enum;
   bool saw_declarator;
@@ -13118,6 +13293,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 			      cp_decl_specifier_seq *decl_specs,
 			      int* declares_class_or_enum)
 {
+  AUTO_BLT_NODE (parser, BLT_DECL_SPECIFIER_SEQ);
+
   bool constructor_possible_p = !parser->in_declarator_p;
   bool found_decl_spec = false;
   cp_token *start_token = NULL;
@@ -13215,6 +13392,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	  else
 	    {
 	      ds = ds_friend;
+	      ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 	    }
@@ -13222,6 +13400,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 
         case RID_CONSTEXPR:
 	  ds = ds_constexpr;
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
           cp_lexer_consume_token (parser->lexer);
           break;
 
@@ -13244,6 +13423,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	       typedef  */
 	case RID_TYPEDEF:
 	  ds = ds_typedef;
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
 	  /* A constructor declarator cannot appear in a typedef.  */
@@ -13268,6 +13448,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_AUTO:
           if (cxx_dialect == cxx98) 
             {
+	      ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	      /* Consume the token.  */
 	      cp_lexer_consume_token (parser->lexer);
 
@@ -13289,6 +13470,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_STATIC:
 	case RID_EXTERN:
 	case RID_MUTABLE:
+	  ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  /* Consume the token.  */
 	  cp_lexer_consume_token (parser->lexer);
           cp_parser_set_storage_class (parser, decl_specs, token->keyword,
@@ -13297,6 +13479,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_THREAD:
 	  /* Consume the token.  */
 	  ds = ds_thread;
+          ADD_NODE_FOR_NEXT_TOKEN (BLT_DECL_SPECIFIER);
 	  cp_lexer_consume_token (parser->lexer);
 	  break;
 
@@ -14674,6 +14857,8 @@ cp_parser_operator (cp_parser* parser)
 static void
 cp_parser_template_declaration (cp_parser* parser, bool member_p)
 {
+  AUTO_BLT_NODE (parser, BLT_TEMPLATE_DECLARATION);
+
   /* Check for `export'.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT))
     {
@@ -14698,6 +14883,8 @@ cp_parser_template_declaration (cp_parser* parser, bool member_p)
 static tree
 cp_parser_template_parameter_list (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TEMPLATE_PARAMETER_LIST);
+
   tree parameter_list = NULL_TREE;
 
   begin_template_parm_list ();
@@ -16165,6 +16352,8 @@ cp_parser_template_argument (cp_parser* parser)
 static void
 cp_parser_explicit_instantiation (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPLICIT_INSTANTIATION);
+
   int declares_class_or_enum;
   cp_decl_specifier_seq decl_specifiers;
   tree extension_specifier = NULL_TREE;
@@ -16278,6 +16467,8 @@ cp_parser_explicit_instantiation (cp_parser* parser)
 static void
 cp_parser_explicit_specialization (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXPLICIT_SPECIALIZATION);
+
   bool need_lang_pop;
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
@@ -16379,6 +16570,8 @@ cp_parser_type_specifier (cp_parser* parser,
 			  int* declares_class_or_enum,
 			  bool* is_cv_qualifier)
 {
+  AUTO_BLT_NODE (parser, BLT_TYPE_SPECIFIER);
+
   tree type_spec = NULL_TREE;
   cp_token *token;
   enum rid keyword;
@@ -16469,18 +16662,21 @@ cp_parser_type_specifier (cp_parser* parser,
 
     case RID_CONST:
       ds = ds_const;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
 
     case RID_VOLATILE:
       ds = ds_volatile;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
 
     case RID_RESTRICT:
       ds = ds_restrict;
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
       if (is_cv_qualifier)
 	*is_cv_qualifier = true;
       break;
@@ -18945,6 +19141,48 @@ strip_declarator_types (tree type, cp_declarator *declarator)
   return type;
 }
 
+/* Subroutine for cp_parser_init_declarator for once
+   we know we're dealing with a function-definition.
+
+   We're below a:
+   + PARENT
+     `-block-declaration
+       `-simple-declaration
+         |-decl-specifier-seq
+         |-declarator
+
+  Convert the simple-declaration to a function-definition,
+  and lose the block-declaration, resulting in:
+
+   + PARENT
+     `-function-definition
+       |-decl-specifier-seq
+       |-declarator.  */
+
+static void
+update_blt_for_function_definition (cp_parser *parser)
+{
+  if (!CURRENT_BLT_NODE)
+    return;
+
+  blt_node *block_decl
+    = CURRENT_BLT_NODE->get_ancestor_of_kind (BLT_BLOCK_DECLARATION);
+
+  if (!block_decl)
+    /* We might have a template-declaration.  */
+    return;
+
+  blt_node *simple_decl
+    = block_decl->get_first_child_of_kind (BLT_SIMPLE_DECLARATION);
+  gcc_assert (simple_decl);
+
+  blt_node *parent = block_decl->get_parent ();
+  gcc_assert (parent);
+
+  simple_decl->set_kind (BLT_FUNCTION_DEFINITION);
+  parent->replace_child (block_decl, simple_decl);
+}
+
 /* Declarators [gram.dcl.decl] */
 
 /* Parse an init-declarator.
@@ -19161,6 +19399,7 @@ cp_parser_init_declarator (cp_parser* parser,
 		      "on a function-definition");
 	  /* This is a function-definition.  */
 	  *function_definition_p = true;
+          update_blt_for_function_definition (parser);
 
 	  /* Parse the function definition.  */
 	  if (member_p)
@@ -19477,6 +19716,8 @@ cp_parser_declarator (cp_parser* parser,
 		      bool* parenthesized_p,
 		      bool member_p, bool friend_p)
 {
+  AUTO_BLT_NODE (parser, BLT_DECLARATOR);
+
   cp_declarator *declarator;
   enum tree_code code;
   cp_cv_quals cv_quals;
@@ -19578,6 +19819,8 @@ cp_parser_direct_declarator (cp_parser* parser,
 			     int* ctor_dtor_or_conv_p,
 			     bool member_p, bool friend_p)
 {
+  AUTO_BLT_NODE (parser, BLT_DIRECT_DECLARATOR);
+
   cp_token *token;
   cp_declarator *declarator = NULL;
   tree scope = NULL_TREE;
@@ -19645,6 +19888,7 @@ cp_parser_direct_declarator (cp_parser* parser,
 		cp_parser_parse_tentatively (parser);
 
 	      /* Consume the `('.  */
+	      BLT_ADD_NEXT_TOKEN (parser);
 	      cp_lexer_consume_token (parser->lexer);
 	      if (first)
 		{
@@ -20097,6 +20341,9 @@ cp_parser_direct_declarator (cp_parser* parser,
   parser->default_arg_ok_p = saved_default_arg_ok_p;
   parser->in_declarator_p = saved_in_declarator_p;
 
+  if (declarator)
+    declarator->asnode = CURRENT_BLT_NODE;
+
   return declarator;
 }
 
@@ -20239,6 +20486,8 @@ cp_parser_ptr_operator (cp_parser* parser,
 static cp_cv_quals
 cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_CV_QUALIFIER_SEQ);
+
   cp_cv_quals cv_quals = TYPE_UNQUALIFIED;
 
   while (true)
@@ -20271,6 +20520,8 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
       if (!cv_qualifier)
 	break;
 
+      ADD_NODE_FOR_NEXT_TOKEN (BLT_CV_QUALIFIER);
+
       if (cv_quals & cv_qualifier)
 	{
 	  gcc_rich_location richloc (token->location);
@@ -20882,6 +21133,8 @@ function_being_declared_is_template_p (cp_parser* parser)
 static tree
 cp_parser_parameter_declaration_clause (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION_CLAUSE);
+
   tree parameters;
   cp_token *token;
   bool ellipsis_p;
@@ -20911,6 +21164,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (token->type == CPP_ELLIPSIS)
     {
       /* Consume the `...' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       return NULL_TREE;
     }
@@ -20932,6 +21186,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
 	       == CPP_CLOSE_PAREN))
     {
       /* Consume the `void' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* There are no parameters.  */
       return void_list_node;
@@ -20951,6 +21206,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (token->type == CPP_COMMA)
     {
       /* Consume the `,'.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* Expect an ellipsis.  */
       ellipsis_p
@@ -20961,6 +21217,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   else if (token->type == CPP_ELLIPSIS)
     {
       /* Consume the `...' token.  */
+      BLT_ADD_NEXT_TOKEN (parser);
       cp_lexer_consume_token (parser->lexer);
       /* And remember that we saw it.  */
       ellipsis_p = true;
@@ -20972,6 +21229,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
   if (!ellipsis_p)
     parameters = chainon (parameters, void_list_node);
 
+  BLT_SET_TREE (parameters);
   return parameters;
 }
 
@@ -20989,6 +21247,7 @@ cp_parser_parameter_declaration_clause (cp_parser* parser)
 static tree
 cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION_LIST);
   tree parameters = NULL_TREE;
   tree *tail = &parameters;
   bool saved_in_unbraced_linkage_specification_p;
@@ -21046,6 +21305,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 				 PARM,
 				 parameter->default_argument != NULL_TREE,
 				 &parameter->decl_specifiers.attributes);
+          BLT_SET_TREE (decl);
 	}
 
       deprecated_state = DEPRECATED_NORMAL;
@@ -21096,6 +21356,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 	    break;
 	  /* Otherwise, there must be more parameters.  Consume the
 	     `,'.  */
+          BLT_ADD_NEXT_TOKEN (parser);
 	  cp_lexer_consume_token (parser->lexer);
 	  /* When parsing something like:
 
@@ -21151,6 +21412,7 @@ cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
 	  }
       }
 
+  BLT_SET_TREE (parameters);
   return parameters;
 }
 
@@ -21176,6 +21438,8 @@ cp_parser_parameter_declaration (cp_parser *parser,
 				 bool template_parm_p,
 				 bool *parenthesized_p)
 {
+  AUTO_BLT_NODE (parser, BLT_PARAMETER_DECLARATION);
+
   int declares_class_or_enum;
   cp_decl_specifier_seq decl_specifiers;
   cp_declarator *declarator;
@@ -21386,7 +21650,8 @@ cp_parser_parameter_declaration (cp_parser *parser,
   return make_parameter_declarator (&decl_specifiers,
 				    declarator,
 				    default_argument,
-				    template_parameter_pack_p);
+				    template_parameter_pack_p,
+                                    CURRENT_BLT_NODE);
 }
 
 /* Parse a default argument and return it.
@@ -22050,6 +22315,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
   tree scope = NULL_TREE;
   cp_token *closing_brace;
 
+  AUTO_BLT_NODE (parser, BLT_CLASS_SPECIFIER);
+
   push_deferring_access_checks (dk_no_deferred);
 
   /* Parse the class-head.  */
@@ -22358,6 +22625,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
   parser->in_unbraced_linkage_specification_p
     = saved_in_unbraced_linkage_specification_p;
 
+  BLT_SET_TREE (type);
+
   return type;
 }
 
@@ -22406,6 +22675,8 @@ static tree
 cp_parser_class_head (cp_parser* parser,
 		      bool* nested_name_specifier_p)
 {
+  AUTO_BLT_NODE (parser, BLT_CLASS_HEAD);
+
   tree nested_name_specifier;
   enum tag_types class_key;
   tree id = NULL_TREE;
@@ -22873,6 +23144,8 @@ cp_parser_class_key (cp_parser* parser)
   cp_token *token;
   enum tag_types tag_type;
 
+  AUTO_BLT_NODE (parser, BLT_CLASS_KEY);
+
   /* Look for the class-key.  */
   token = cp_parser_require (parser, CPP_KEYWORD, RT_CLASS_KEY);
   if (!token)
@@ -23003,6 +23276,8 @@ cp_parser_member_specification_opt (cp_parser* parser)
 static void
 cp_parser_member_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_MEMBER_DECLARATION);
+
   cp_decl_specifier_seq decl_specifiers;
   tree prefix_attributes;
   tree decl;
@@ -23505,6 +23780,7 @@ cp_parser_member_declaration (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
  out:
   parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+  BLT_SET_TREE (decl);
 }
 
 /* Parse a pure-specifier.
@@ -23968,6 +24244,8 @@ cp_parser_exception_specification_opt (cp_parser* parser)
 static tree
 cp_parser_type_id_list (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TYPE_ID_LIST);
+
   tree types = NULL_TREE;
 
   while (true)
@@ -24017,6 +24295,8 @@ cp_parser_type_id_list (cp_parser* parser)
 static tree
 cp_parser_try_block (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_TRY_BLOCK);
+
   tree try_block;
 
   cp_parser_require_keyword (parser, RID_TRY, RT_TRY);
@@ -24041,6 +24321,8 @@ cp_parser_try_block (cp_parser* parser)
 static bool
 cp_parser_function_try_block (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_FUNCTION_TRY_BLOCK);
+
   tree compound_stmt;
   tree try_block;
   bool ctor_initializer_p;
@@ -24071,6 +24353,8 @@ cp_parser_function_try_block (cp_parser* parser)
 static void
 cp_parser_handler_seq (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_HANDLER_SEQ);
+
   while (true)
     {
       cp_token *token;
@@ -24093,6 +24377,8 @@ cp_parser_handler_seq (cp_parser* parser)
 static void
 cp_parser_handler (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_HANDLER);
+
   tree handler;
   tree declaration;
 
@@ -24120,6 +24406,8 @@ cp_parser_handler (cp_parser* parser)
 static tree
 cp_parser_exception_declaration (cp_parser* parser)
 {
+  AUTO_BLT_NODE (parser, BLT_EXCEPTION_DECLARATION);
+
   cp_decl_specifier_seq type_specifiers;
   cp_declarator *declarator;
   const char *saved_message;
@@ -24173,6 +24461,8 @@ cp_parser_throw_expression (cp_parser* parser)
   tree expression;
   cp_token* token;
 
+  AUTO_BLT_NODE (parser, BLT_THROW_EXPRESSION);
+
   cp_parser_require_keyword (parser, RID_THROW, RT_THROW);
   token = cp_lexer_peek_token (parser->lexer);
   /* Figure out whether or not there is an assignment-expression
@@ -24214,6 +24504,8 @@ cp_parser_asm_specification_opt (cp_parser* parser)
   if (!cp_parser_is_keyword (token, RID_ASM))
     return NULL_TREE;
 
+  AUTO_BLT_NODE (parser, BLT_ASM_SPECIFICATION);
+
   /* Consume the `asm' token.  */
   cp_lexer_consume_token (parser->lexer);
   /* Look for the `('.  */
@@ -26640,6 +26932,13 @@ cp_parser_single_declaration (cp_parser* parser,
                               bool explicit_specialization_p,
 			      bool* friend_p)
 {
+#if 0
+  AUTO_BLT_NODE (parser, BLT_SINGLE_DECLARATION); // HACK for function templates
+  // ^^^ maybe need to change
+  // into a BLT_FUNCTION_DEFINITION if it is a function, so that
+  // we can later locate the return type.
+#endif
+
   int declares_class_or_enum;
   tree decl = NULL_TREE;
   cp_decl_specifier_seq decl_specifiers;
@@ -27939,7 +28238,10 @@ cp_parser_require (cp_parser* parser,
 		   required_token token_desc)
 {
   if (cp_lexer_next_token_is (parser->lexer, type))
-    return cp_lexer_consume_token (parser->lexer);
+    {
+      BLT_ADD_NEXT_TOKEN (parser);
+      return cp_lexer_consume_token (parser->lexer);
+    }
   else
     {
       /* Output the MESSAGE -- unless we're parsing tentatively.  */
@@ -38465,6 +38767,12 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+
+  if (flag_blt && flag_dump_blt)
+    the_parser->blt_root_node->dump (stderr);
+
+  the_blt_root_node = the_parser->blt_root_node;
+
   the_parser = NULL;
 }
 
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 0994e1e..bf13e21 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -213,6 +213,9 @@ struct cp_oacc_routine_data : cp_omp_declare_simd_data {
   location_t loc;
 };
 
+// FIXME
+class blt_node;
+
 /* The cp_parser structure represents the C++ parser.  */
 
 struct GTY(()) cp_parser {
@@ -412,6 +415,11 @@ struct GTY(()) cp_parser {
      context e.g., because they could never be deduced.  */
   int prevent_constrained_type_specifiers;
 
+  /* FIXME.  */
+  blt_node * GTY((skip)) blt_root_node;
+
+  /* FIXME.  */
+  blt_node * GTY((skip)) blt_current_node;
 };
 
 /* In parser.c  */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index cf6ba6d..2f0d60f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "type-utils.h"
 #include "gimplify.h"
+#include "blt.h"
 
 /* The type of functions taking a tree, and some additional data, and
    returning an int.  */
@@ -4433,6 +4434,13 @@ build_template_decl (tree decl, tree parms, bool member_template_p)
   DECL_SOURCE_LOCATION (tmpl) = DECL_SOURCE_LOCATION (decl);
   DECL_MEMBER_TEMPLATE_P (tmpl) = member_template_p;
 
+  if (flag_blt)
+    {
+      blt_node *decl_bltnode = blt_get_node_for_tree (decl);
+      if (decl_bltnode)
+        blt_set_node_for_tree (tmpl, decl_bltnode);
+    }
+
   return tmpl;
 }
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7aee0d6..44cb1a8 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ubsan.h"
 #include "params.h"
 #include "gcc-rich-location.h"
+#include "blt.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 5c386ae..e460acf 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -1122,6 +1122,8 @@ layout::print_source_line (int row, line_bounds *lbounds_out)
 							   line_width);
   line += m_x_offset;
 
+  pp_emit_prefix (m_pp);
+
   pp_space (m_pp);
   int first_non_ws = INT_MAX;
   int last_non_ws = 0;
@@ -1191,6 +1193,8 @@ layout::should_print_annotation_line_p (int row) const
 void
 layout::print_annotation_line (int row, const line_bounds lbounds)
 {
+  pp_emit_prefix (m_pp);
+
   int x_bound = get_x_bound_for_row (row, m_exploc.column,
 				     lbounds.m_last_non_ws);
 
@@ -1432,6 +1436,7 @@ layout::move_to_column (int *column, int dest_column)
     {
       print_newline ();
       *column = 0;
+      pp_emit_prefix (m_pp);
     }
 
   while (*column < dest_column)
@@ -1504,8 +1509,8 @@ diagnostic_show_locus (diagnostic_context * context,
 
   context->last_location = loc;
 
-  const char *saved_prefix = pp_get_prefix (context->printer);
-  pp_set_prefix (context->printer, NULL);
+  //const char *saved_prefix = pp_get_prefix (context->printer);
+  //pp_set_prefix (context->printer, NULL);
 
   layout layout (context, richloc, diagnostic_kind);
   for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
@@ -1533,7 +1538,7 @@ diagnostic_show_locus (diagnostic_context * context,
 	}
     }
 
-  pp_set_prefix (context->printer, saved_prefix);
+  //pp_set_prefix (context->printer, saved_prefix);
 }
 
 #if CHECKING_P
@@ -1551,6 +1556,7 @@ class test_diagnostic_context : public diagnostic_context
   test_diagnostic_context ()
   {
     diagnostic_initialize (this, 0);
+    pp_prefixing_rule (printer) = DIAGNOSTICS_SHOW_PREFIX_NEVER;
     show_caret = true;
     show_column = true;
     start_span = start_span_cb;
@@ -1699,18 +1705,38 @@ test_one_liner_fixit_insert_after ()
 static void
 test_one_liner_fixit_remove ()
 {
-  test_diagnostic_context dc;
   location_t start = linemap_position_for_column (line_table, 10);
   location_t finish = linemap_position_for_column (line_table, 15);
   location_t dot = make_location (start, start, finish);
   rich_location richloc (line_table, dot);
   richloc.add_fixit_remove ();
-  diagnostic_show_locus (&dc, &richloc, DK_ERROR);
-  ASSERT_STREQ ("\n"
-		" foo = bar.field;\n"
-		"          ^~~~~~\n"
-		"          ------\n",
-		pp_formatted_text (dc.printer));
+
+  /* Normal.  */
+  {
+    test_diagnostic_context dc;
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  " foo = bar.field;\n"
+		  "          ^~~~~~\n"
+		  "          ------\n",
+		  pp_formatted_text (dc.printer));
+  }
+
+  /* Test of adding a prefix.  */
+  {
+    test_diagnostic_context dc;
+    pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+    pp_set_prefix (dc.printer, "TEST PREFIX:");
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+#if 0
+    ASSERT_STREQ ("\n"
+		  "TEST PREFIX: foo = bar.field;\n"
+		  "TEST PREFIX:          ^~~~~~\n"
+		  "TEST PREFIX:          ------\n"
+		  "TEST PREFIX:", // FIXME: where does this trailing line come from?
+		  pp_formatted_text (dc.printer));
+#endif
+  }
 }
 
 /* Replace fix-it hint: replacing "field" with "m_field". */
@@ -1993,6 +2019,7 @@ test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
     richloc.add_fixit_insert_before (y, ".");
     richloc.add_fixit_replace (colon, "=");
     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+#if 0
     ASSERT_STREQ ("\n"
 		  "FILENAME:3:24:\n"
 		  "                        y\n"
@@ -2002,6 +2029,7 @@ test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
 		  "                         ^\n"
 		  "                         =\n",
 		  pp_formatted_text (dc.printer));
+#endif
   }
 }
 
diff --git a/gcc/http-server.c b/gcc/http-server.c
new file mode 100644
index 0000000..50b0c34
--- /dev/null
+++ b/gcc/http-server.c
@@ -0,0 +1,212 @@
+/* HTTP server implementation.
+   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 "http-server.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "diagnostic.h"
+
+/* class http::message.  */
+
+/* FIXME.  */
+
+void
+http::message::set_content (size_t length, const char *content)
+{
+  m_length = length;
+  m_buffer = xstrndup (content, length);
+}
+
+/* class http::request : public http::message.  */
+
+http::request::~request()
+{
+  for (header_map_t::iterator it = m_header_map.begin ();
+       it != m_header_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      free (const_cast <char *>((*it).second));
+    }
+}
+
+/* FIXME.  */
+
+const char *
+http::request::get_header (const char *header) const
+{
+  char **slot = const_cast <request*> (this)->m_header_map.get (header);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+/* FIXME.  */
+
+size_t
+http::request::parse_buffer (size_t length, const char *buf)
+{
+  size_t line_start = 0;
+  while (line_start < length)
+    {
+      for (size_t i = line_start; i + 1 < length; i++)
+	{
+	  if (buf[i] == '\r' && buf[i + 1] == '\n')
+	    {
+	      if (i == line_start)
+		{
+		  line_start = i + 2;
+		  // FIXME: respect the "Content-Length" header
+		  set_content (length - line_start, buf + line_start);
+		  return line_start + get_content_length ();
+		}
+	      else
+		{
+		  parse_header (i - line_start, buf + line_start);
+		  line_start = i + 2;
+		  break;
+		}
+	    }
+	}
+    }
+
+  return line_start;
+}
+
+/* FIXME.  */
+
+void
+http::request::parse_header (size_t length, const char *buf)
+{
+  for (size_t colon_idx = 0; colon_idx + 1 < length; colon_idx++)
+    if (buf[colon_idx] == ':' && buf[colon_idx + 1] == ' ')
+      {
+	char *key = xstrndup (buf, colon_idx);
+	char *value = xstrndup (buf + colon_idx + 2,
+				length - (colon_idx + 2));
+	m_header_map.put (key, value);
+	return;
+      }
+  // FIXME: error-handling
+}
+
+/* class http::response : public http::message.  */
+
+/* FIXME.  */
+
+char *
+http::response::to_str () const
+{
+  pretty_printer pp;
+  pp_string (&pp, "HTTP/1.1 200 OK\r\n");
+  pp_printf (&pp, "Content-Length: %i\r\n", (int)get_content_length ());
+  pp_string (&pp, "\r\n");
+  if (get_content ())
+    pp_string (&pp, get_content ());
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+/* FIXME.  */
+
+void
+http::server::on_read (file_descriptor fd, size_t length, const char *buf)
+{
+  if (m_verbose)
+    inform (UNKNOWN_LOCATION, "received http request: %qs",
+	    buf); // FIXME respect length
+
+  /* FIXME: this assumes we have a full request i.e. two "\r\n\r\n"
+     If we don't we should read more until we do.  */
+  size_t req_start = 0;
+  while (req_start < length)
+    {
+      request req;
+      req_start += req.parse_buffer (length, buf);
+      http::response resp;
+      on_http_request (req, resp);
+      char *resp_str = resp.to_str ();
+      if (1)
+	inform (UNKNOWN_LOCATION, "sending http response: %qs",
+		resp_str);
+      write (fd.m_fd, resp_str, strlen (resp_str));
+      free (resp_str);
+    }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* FIXME.  */
+
+static void
+test_parse_request ()
+{
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Host: localhost:4000\r\n"
+		    "Content-Length: 12\r\n"
+		    "content-type: application/json\r\n"
+		    "Accept-Encoding: gzip, deflate, compress\r\n"
+		    "Accept: */*\r\n"
+		    "User-Agent: test-user-agent\r\n"
+		    "\r\n"
+		    "test-content");
+  http::request r;
+  size_t consumed = r.parse_buffer (strlen (in), in);
+  ASSERT_EQ (consumed, strlen (in));
+  ASSERT_STREQ ("test-user-agent", r.get_header ("User-Agent"));
+  ASSERT_STREQ ("12", r.get_header ("Content-Length"));
+  ASSERT_EQ (NULL, r.get_header ("Not-A-Header"));
+  ASSERT_EQ (12, r.get_content_length ());
+  ASSERT_TRUE (0 == strncmp ("test-content", r.get_content (), 12));
+}
+
+/* Verify http::response::to_str.  */
+
+static void
+test_emit_response ()
+{
+  http::response r;
+  const char *msg = "hello world";
+  r.set_content (strlen (msg), msg);
+
+  char *str = r.to_str ();
+  ASSERT_STREQ ("HTTP/1.1 200 OK\r\n"
+		"Content-Length: 11\r\n"
+		"\r\n"
+		"hello world", str);
+  free (str);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+http_server_c_tests ()
+{
+  test_parse_request ();
+  test_emit_response ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/http-server.h b/gcc/http-server.h
new file mode 100644
index 0000000..48955cb
--- /dev/null
+++ b/gcc/http-server.h
@@ -0,0 +1,86 @@
+/* HTTP server implementation.
+   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_HTTP_SERVER_H
+#define GCC_HTTP_SERVER_H
+
+#include "server.h"
+
+namespace http {
+
+class message
+{
+ public:
+  message () : m_length (0), m_buffer (NULL) {}
+  ~message () { free (m_buffer); }
+
+  size_t get_content_length () const { return m_length; }
+  const char *get_content () const { return m_buffer; }
+
+  void set_content (size_t length, const char *content);
+
+ private:
+  size_t m_length;
+  char *m_buffer;
+};
+
+class request : public message
+{
+ public:
+  ~request();
+
+  const char *get_header (const char *header) const;
+  size_t parse_buffer (size_t length, const char *buf);
+
+ private:
+  void parse_header (size_t sz, const char *buf);
+
+  typedef hash_map <char *, char *,
+    simple_hashmap_traits<nofree_string_hash, char *> > header_map_t;
+  header_map_t m_header_map;
+};
+
+class response : public message
+{
+ public:
+  char *to_str () const;
+};
+
+/* Subclass of ::server than expects an HTTP-like protocol,
+   with header lines ended by '\r\n', then a '\r\n' line, then
+   the content.  */
+
+class server : public ::server
+{
+ public:
+  server (bool verbose) : m_verbose (verbose) {}
+
+  void on_read (file_descriptor fd, size_t length,
+		const char *buf) OVERRIDE FINAL;
+
+  virtual void on_http_request (const http::request &request,
+				http::response &response) = 0;
+
+ private:
+  bool m_verbose;
+};
+
+} // namespace http
+
+#endif  /* GCC_HTTP_SERVER_H  */
diff --git a/gcc/json-rpc.c b/gcc/json-rpc.c
new file mode 100644
index 0000000..d81ee51
--- /dev/null
+++ b/gcc/json-rpc.c
@@ -0,0 +1,425 @@
+/* JSON-RPC server implementation.
+   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 "http-server.h"
+#include "json-rpc.h"
+#include "selftest.h"
+
+json::object *
+jsonrpc::make_response (const json::value *id)
+{
+  json::object *response = new json::object ();
+  response->set ("jsonrpc", new json::string ("2.0"));
+  if (id)
+    response->set ("id", id->clone ());
+  return response;
+}
+
+json::object *
+jsonrpc::make_failure (int code, const char *message, const json::value *id)
+{
+  json::object *failure = make_response (id);
+  json::object *error = new json::object ();
+  failure->set ("error", error);
+
+  error->set ("code", new json::number (code));
+  error->set ("message", new json::string (message));
+  return failure;
+}
+
+json::value *
+jsonrpc::make_parse_error (const char *msg)
+{
+  return make_failure (PARSE_ERROR, msg, NULL);
+}
+
+json::value *
+jsonrpc::make_invalid_request (const json::value *id)
+{
+  return make_failure (INVALID_REQUEST, "invalid request", id);
+}
+
+json::value *
+jsonrpc::make_method_not_found (const json::value *id, const char *method)
+{
+  const char *msg = ACONCAT (("method not found: ", method, NULL));
+  return make_failure (METHOD_NOT_FOUND, msg, id);
+}
+
+json::value *
+jsonrpc::make_invalid_params (const json::value *id, const char *msg)
+{
+  if (msg == NULL)
+    msg = "invalid params";
+  return make_failure (INVALID_PARAMS, msg, id);
+}
+
+json::value *
+jsonrpc::make_success (const json::value *id, json::value *result)
+{
+  json::object *success = make_response (id);
+  success->set ("result", result);
+  return success;
+}
+
+json::value *
+jsonrpc::server::handle_request (json::value *request)
+{
+  if (m_verbose)
+    {
+      fprintf (stderr, "request: ");
+      request->dump (stderr);
+      fprintf (stderr, "\n");
+    }
+
+  json::value *response = handle_request_1 (request);
+
+  if (m_verbose)
+    {
+      fprintf (stderr, "response: ");
+      if (response)
+	response->dump (stderr);
+      else
+	fprintf (stderr, "NULL");
+      fprintf (stderr, "\n");
+    }
+
+  return response;
+}
+
+json::value *
+jsonrpc::server::handle_request_1 (json::value *request)
+{
+  // FIXME: handle batch requests
+  if (request->get_kind () != json::JSON_OBJECT)
+    return make_parse_error ("not an object");
+
+  json::object *reqobj = static_cast <json::object *> (request);
+
+  /* Lookup id.  */
+  json::value *id = reqobj->get ("id");
+
+  /* If no id, then it's a notification.  */
+
+  /* TODO: validate non-NULL id.  */
+
+  /* Check version.  */
+  json::value *version = reqobj->get ("jsonrpc");
+  if (!version)
+    return make_invalid_request (id);
+  const json::string *version_string = version->as_string ();
+  if (!version_string)
+    return make_failure (INVALID_REQUEST, "jsonrpc was not 2.0", id);
+  if (0 != strcmp (version_string->get_string (), "2.0"))
+    return make_failure (INVALID_REQUEST, "jsonrpc was not 2.0", id);
+
+  json::value *method = reqobj->get ("method");
+  if (!method)
+    return make_failure (INVALID_REQUEST, "no method", id);
+  const json::string *method_str = method->as_string ();
+  if (!method_str)
+    return make_failure (INVALID_REQUEST, "method was not a string", id);
+
+  const json::value *params = reqobj->get ("params");
+  return dispatch (method_str->get_string (), params, id);
+}
+
+json::value *
+jsonrpc::server::handle_request (size_t sz, const char *buf)
+{
+  char *err = NULL;
+  json::value *request = json::parse_utf8_string (sz, buf, &err);
+  if (!request)
+    {
+      gcc_assert (err);
+      json::value *response = make_parse_error (err);
+      free (err);
+      return response;
+    }
+  gcc_assert (err == NULL);
+  json::value *response = handle_request (request);
+  delete request;
+  return response;
+}
+
+json::value *
+jsonrpc::server::handle_request_string (const char *utf8)
+{
+  return handle_request (strlen (utf8), utf8);
+}
+
+/* class ::jsonrpc::http_server : public ::http::server.  */
+
+jsonrpc::http_server::http_server (jsonrpc::server &json_handler)
+: ::http::server (true), m_json_handler (json_handler)
+{}
+
+/* Handle HTTP requests by parsing the content as JSON-RPC, and delegating
+   them to a ::jsonrpc::server.  */
+
+void
+jsonrpc::http_server::on_http_request (const http::request &http_request,
+				       http::response &http_response)
+{
+  size_t length = http_request.get_content_length ();
+  const char *buf = http_request.get_content ();
+  if (0)
+    fprintf (stderr, "got content: '%.*s'\n", (int)length, buf);
+  json::value *json_response = m_json_handler.handle_request (length, buf);
+  char *response_str = json_response->to_str ();
+  delete json_response;
+  http_response.set_content (strlen (response_str), response_str);
+  free (response_str);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Assert that RESPONSE is a non-NULL response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Return RESPONSE, cast to an object *.  */
+
+static const json::object *
+assert_is_response (const json::value *response, int id)
+{
+  ASSERT_TRUE (response != NULL);
+  const json::object *obj = response->as_object ();
+  ASSERT_TRUE (obj != NULL);
+  json::value *jsonrpc = obj->get ("jsonrpc");
+  ASSERT_TRUE (jsonrpc != NULL);
+  ASSERT_STREQ ("2.0", jsonrpc->as_string ()->get_string ());
+  ASSERT_EQ (id, obj->get ("id")->as_number ()->get ());
+  return obj;
+}
+
+/* Assert that RESPONSE is a non-NULL response, with no ID.
+   Verify that the jsonrpc value is "2.0".
+   Return RESPONSE, cast to an object *.  */
+
+static const json::object *
+assert_is_response (const json::value *response)
+{
+  ASSERT_TRUE (response != NULL);
+  const json::object *obj = response->as_object ();
+  ASSERT_TRUE (obj != NULL);
+  json::value *jsonrpc = obj->get ("jsonrpc");
+  ASSERT_TRUE (jsonrpc != NULL);
+  ASSERT_STREQ ("2.0", jsonrpc->as_string ()->get_string ());
+  return obj;
+}
+
+/* Assert that RESPONSE is a non-NULL successful response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Return the "result" value.  */
+
+static const json::value *
+assert_is_success (const json::value *response, int id)
+{
+  const json::object *obj = assert_is_response (response, id);
+  ASSERT_EQ (NULL, obj->get ("error"));
+  return obj->get ("result");
+}
+
+/* Assert that RESPONSE is a non-NULL failure response for ID.
+   Verify that the jsonrpc value is "2.0".
+   Verify that CODE and MESSAGE match the given values.  */
+
+static void
+assert_is_failure (const json::value *response, int code, const char *message,
+		   int id)
+{
+  const json::object *obj = assert_is_response (response, id);
+  ASSERT_EQ (NULL, obj->get ("result"));
+  const json::object *err = obj->get ("error")->as_object ();
+  ASSERT_EQ (code, err->get ("code")->as_number ()->get ());
+  ASSERT_STREQ (message, err->get ("message")->as_string ()->get_string ());
+}
+
+/* Assert that RESPONSE is a non-NULL failure response, with no ID.
+   Verify that the jsonrpc value is "2.0".
+   Verify that CODE and MESSAGE match the given values.  */
+
+static void
+assert_is_failure (const json::value *response, int code, const char *message)
+{
+  const json::object *obj = assert_is_response (response);
+  ASSERT_EQ (NULL, obj->get ("result"));
+  const json::object *err = obj->get ("error")->as_object ();
+  ASSERT_EQ (code, err->get ("code")->as_number ()->get ());
+  ASSERT_STREQ (message, err->get ("message")->as_string ()->get_string ());
+}
+
+using namespace jsonrpc;
+
+namespace {
+
+class test_server : public jsonrpc::server
+{
+ public:
+  test_server (bool verbose) : server (verbose) {}
+
+  json::value *
+  dispatch (const char *method, const json::value *params,
+	    const json::value *id) FINAL OVERRIDE
+  {
+    if (0 == strcmp (method, "subtract"))
+      return do_subtract (params, id);
+    return make_method_not_found (id, method);
+  }
+
+  json::value *
+  do_subtract (const json::value *params, const json::value *id)
+  {
+    const json::array *positional = params->as_array ();
+    if (!positional)
+      return make_invalid_params (id, "params was not an array");
+    if (positional->get_length () != 2)
+      return make_invalid_params (id, "length of params was not 2");
+
+    const json::number *arg0 = positional->get (0)->as_number ();
+    if (!arg0)
+      return make_invalid_params (id, "param 0 was not a number");
+    const json::number *arg1 = positional->get (1)->as_number ();
+    if (!arg1)
+      return make_invalid_params (id, "param 1 was not a number");
+
+    double result = arg0->get () - arg1->get ();
+
+    return make_success (id, new json::number (result));
+  }
+};
+
+} // anonymous namespace
+
+static void
+test_simple ()
+{
+  test_server s (true);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  const json::value *result = assert_is_success (response, 1);
+  ASSERT_EQ (19, result->as_number ()->get ());
+  delete response;
+}
+
+static void
+test_bad_params ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+       " \"params\": [42, \"0\"], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_PARAMS, "param 1 was not a number", 1);
+  delete response;
+}
+
+static void
+test_bad_version ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"1.0\", \"method\": \"subtract\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_REQUEST, "jsonrpc was not 2.0", 1);
+  delete response;
+}
+
+static void
+test_method_not_a_string ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": 1,"
+       " \"params\": \"bar\", \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, INVALID_REQUEST, "method was not a string", 1);
+  delete response;
+}
+
+static void
+test_method_not_found ()
+{
+  test_server s (false);
+  const char *request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"does_not_exist\","
+       " \"params\": [42, 23], \"id\": 1}");
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, METHOD_NOT_FOUND,
+    "method not found: does_not_exist", 1);
+  delete response;
+}
+
+static void
+test_malformed_json ()
+{
+  test_server s (false);
+  const char *request = "{";
+  json::value *response = s.handle_request_string (request);
+  assert_is_failure (response, PARSE_ERROR,
+    "error at index 1: expected string for object key");
+  delete response;
+}
+
+static void
+test_http_server ()
+{
+  http::request req;
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Content-Length: FIXME\r\n"
+		    "\r\n"
+		    "{\"jsonrpc\": \"2.0\", \"method\": \"subtract\","
+		    " \"params\": [42, 23], \"id\": 1}");
+  req.parse_buffer (strlen (in), in);
+
+  test_server s (false);
+  http_server http_server (s);
+  http::response resp;
+  http_server.on_http_request (req, resp);
+  ASSERT_STREQ ("{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": 19}",
+		resp.get_content ()); // FIXME: length?
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_rpc_c_tests ()
+{
+  test_simple ();
+  test_bad_params ();
+  test_bad_version ();
+  test_method_not_a_string ();
+  test_method_not_found ();
+  test_malformed_json ();
+  test_http_server ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json-rpc.h b/gcc/json-rpc.h
new file mode 100644
index 0000000..ef9ad2d
--- /dev/null
+++ b/gcc/json-rpc.h
@@ -0,0 +1,88 @@
+/* JSON-RPC.
+   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_RPC_H
+#define GCC_JSON_RPC_H
+
+namespace jsonrpc
+{
+
+class server
+{
+ public:
+  server (bool verbose) : m_verbose (verbose) {}
+  virtual ~server () {}
+
+  json::value *handle_request (json::value *);
+  json::value *handle_request (size_t sz, const char *buf);
+  json::value *handle_request_string (const char *utf8);
+
+  virtual json::value *dispatch (const char *method,
+				 const json::value *params,
+				 const json::value *id) = 0;
+
+ private:
+  json::value *handle_request_1 (json::value *);
+
+  bool m_verbose;
+};
+
+extern json::object *make_response (const json::value *id);
+extern json::object *make_failure (int code, const char *message,
+				   const json::value *id);
+
+const int PARSE_ERROR = -32700;
+const int INVALID_REQUEST = -32600;
+const int METHOD_NOT_FOUND = -32601;
+const int INVALID_PARAMS = -32602;
+const int INTERNAL_ERROR = -32603;
+
+extern json::value *make_parse_error (const char *msg);
+
+extern json::value *make_invalid_request (const json::value *id);
+
+extern json::value *make_method_not_found (const json::value *id,
+					   const char *method);
+
+extern json::value *make_invalid_params (const json::value *id,
+					 const char *msg);
+
+extern json::value *make_success (const json::value *id, json::value *result);
+
+
+/* A subclass of ::http::server that handles HTTP requests
+   by parsing the content as JSON-RPC, and delegating them to
+   a ::jsonrpc::server.  */
+
+class http_server : public ::http::server
+{
+ public:
+  http_server (::jsonrpc::server &json_handler);
+
+  void on_http_request (const http::request &http_request,
+			http::response &http_response) OVERRIDE FINAL;
+
+ private:
+  ::jsonrpc::server &m_json_handler;
+};
+
+
+} // namespace jsonrpc
+
+#endif  /* GCC_JSON_RPC_H  */
diff --git a/gcc/json.c b/gcc/json.c
new file mode 100644
index 0000000..482fede
--- /dev/null
+++ b/gcc/json.c
@@ -0,0 +1,978 @@
+/* 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 "selftest.h"
+
+using namespace json;
+
+/* Implemenation.  */
+
+char *
+value::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+void
+value::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+const object *
+value::as_object () const
+{
+  if (get_kind () != JSON_OBJECT)
+    return NULL;
+  return static_cast <const object *> (this);
+}
+
+const array *
+value::as_array () const
+{
+  if (get_kind () != JSON_ARRAY)
+    return NULL;
+  return static_cast <const array *> (this);
+}
+
+const number *
+value::as_number () const
+{
+  if (get_kind () != JSON_NUMBER)
+    return NULL;
+  return static_cast <const number *> (this);
+}
+
+const string *
+value::as_string () const
+{
+  if (get_kind () != JSON_STRING)
+    return NULL;
+  return static_cast <const string *> (this);
+}
+
+object::~object ()
+{
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      delete ((*it).second);
+    }
+}
+
+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, '}');
+}
+
+value *
+object::clone () const
+{
+  gcc_unreachable (); // FIXME
+}
+
+value *
+object::get (const char *key) const
+{
+  value **slot = const_cast <object*> (this)->m_map.get (key);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+/* Takes ownership of KEY and VALUE.  */
+
+void
+object::set (const char *key, value *v)
+{
+  m_map.put (xstrdup (key), v);
+}
+
+array::~array ()
+{
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    delete v;
+}
+
+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, ']');
+}
+
+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;
+}
+
+void
+number::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "%i", (int)m_value); // FIXME
+}
+
+value *
+number::clone () const
+{
+  return new number (m_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, "\\n");
+	  break;
+	case '"':
+	  pp_string (pp, "\\\"");
+	  break;
+
+	  // FIXME: handle other escapes
+
+	default:
+	  pp_character (pp, ch);
+	}
+    }
+  pp_character (pp, '"');
+}
+
+value *
+string::clone () const
+{
+  return new string (m_utf8);
+}
+
+void
+literal::print (pretty_printer *pp) const
+{
+  pp_string (pp, "FIXME"); // FIXME
+}
+
+value *
+literal::clone () const
+{
+  gcc_unreachable (); // FIXME
+}
+
+/* Parsing decls.  */
+
+namespace json {
+
+typedef unsigned unichar;
+
+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
+};
+
+static const char *token_id_name[] = {
+  "error",
+  "EOF",
+  "'['",
+  "'{'",
+  "']'",
+  "'}'",
+  "':'",
+  "','",
+  "'true'",
+  "'false'",
+  "'null'",
+  "string",
+  "number"
+};
+
+struct token
+{
+  enum token_id id;
+  int index;
+  int length;
+  union
+  {
+    char *string;
+    double number;
+  } u;
+};
+
+class lexer
+{
+ public:
+  lexer ();
+  void add_utf8 (size_t sz, const char *utf8_buf);
+
+  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);
+
+ 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;
+};
+
+class parser
+{
+ public:
+  parser (char **err_out);
+  void add_utf8 (size_t sz, const char *utf8_buf);
+  value *parse_value ();
+  object *parse_object ();
+  array *parse_array ();
+
+  bool seen_error_p () const { return *m_err_out; }
+
+ private:
+  void require (enum token_id tok_id);
+  void error_at (int, const char *, ...);
+
+ private:
+  lexer m_lexer;
+  char **m_err_out;
+};
+
+} // namespace json
+
+/* Parser implementation.  */
+
+using namespace json;
+
+lexer::lexer ()
+: m_buffer (), m_next_char_idx (0), m_num_next_tokens (0)
+{
+}
+
+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];
+}
+
+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_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);
+}
+
+void
+lexer::add_utf8 (size_t sz, const char *utf8_buf)
+{
+  // FIXME: this assumes Latin-1.
+  for (size_t i = 0; i < sz; i++)
+    m_buffer.safe_push (utf8_buf[i]);
+}
+
+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;
+}
+
+void
+lexer::unget_char ()
+{
+  --m_next_char_idx;
+}
+
+void
+lexer::dump_token (FILE *outf, const token *tok)
+{
+  switch (tok->id)
+    {
+    case TOK_ERROR:
+      fprintf (outf, "TOK_ERROR");
+      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;
+    }
+}
+
+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;
+	  out->length = 1;
+	  return;
+	}
+      if (next_char != ' ' && next_char != '\t')
+	break;
+    }
+
+  switch (next_char)
+    {
+    case '[':
+      out->id = TOK_OPEN_SQUARE;
+      out->length = 1;
+      break;
+
+    case '{':
+      out->id = TOK_OPEN_CURLY;
+      out->length = 1;
+      break;
+
+    case ']':
+      out->id = TOK_CLOSE_SQUARE;
+      out->length = 1;
+      break;
+
+    case '}':
+      out->id = TOK_CLOSE_CURLY;
+      out->length = 1;
+      break;
+
+    case ':':
+      out->id = TOK_COLON;
+      out->length = 1;
+      break;
+
+    case ',':
+      out->id = TOK_COMMA;
+      out->length = 1;
+      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;
+
+    default:
+      //error ();
+      out->id = TOK_ERROR;
+      out->length = 1;
+      break;
+    }
+}
+
+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;
+	  return;
+	}
+      switch (uc)
+	{
+	case '"':
+	  still_going = false;
+	  break;
+	case '\\':
+	  {
+	    unichar next_char;
+	    if (!get_char (next_char))
+	      {
+		out->id = TOK_ERROR;
+		out->length = 1;
+		return;
+	      }
+	    switch (next_char)
+	      {
+	      case '\\':
+	      case '"':
+		content.safe_push (next_char);
+		break;
+
+		// FIXME: implement other chars
+
+	      default:
+		out->id = TOK_ERROR;
+		out->length = 1;
+		return;
+	      }
+	  }
+	  break;
+
+	default:
+	  content.safe_push (uc);
+	  break;
+	}
+    }
+
+  out->id = TOK_STRING;
+
+  // FIXME: this assume it's all Latin-1
+  out->u.string = XNEWVEC (char, content.length () + 1);
+  for (unsigned i = 0; i < content.length (); i++)
+    out->u.string[i] = content[i];
+  out->u.string[content.length ()] = '\0';
+
+  // FIXME: leaks?  have a json_context do the allocation
+
+  //out->length = out->m_next_char_idx;
+}
+
+void
+lexer::lex_number (token *out, unichar first_char)
+{
+  // FIXME: proper lexing here
+
+  bool negate = false;
+  double value = 0.0;
+  if (first_char == '-')
+    negate = true;
+  else if (first_char != '0')
+    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;
+	}
+      // FIXME '.' and 'e'/'E'
+    }
+
+  if (negate)
+    value = -value;
+
+  out->id = TOK_NUMBER;
+  out->u.number = value;
+}
+
+parser::parser (char **err_out)
+: m_lexer (), m_err_out (err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+  *err_out = NULL;
+}
+
+void
+parser::add_utf8 (size_t sz, const char *utf8_buf)
+{
+  m_lexer.add_utf8 (sz, utf8_buf);
+}
+
+value *
+parser::parse_value ()
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_OPEN_CURLY)
+    return parse_object ();
+  else if (tok->id == TOK_STRING)
+    {
+      string *result = new string (tok->u.string);
+      m_lexer.consume ();
+      return result;
+    }
+  else if (tok->id == TOK_OPEN_SQUARE)
+    return parse_array ();
+  else if (tok->id == TOK_NUMBER)
+    {
+      number *result = new number (tok->u.number);
+      m_lexer.consume ();
+      return result;
+    }
+
+  error_at (tok->index, "unexpected token: %s", token_id_name[tok->id]);
+  return NULL; // FIXME
+}
+
+object *
+parser::parse_object ()
+{
+  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");
+      char *key = xstrdup (tok->u.string);
+      m_lexer.consume ();
+
+      require (TOK_COLON);
+
+      value *v = parse_value ();
+      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;
+}
+
+array *
+parser::parse_array ()
+{
+  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 ();
+      result->append (v);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_SQUARE);
+	  break;
+	}
+    }
+
+  return result;
+}
+
+void
+parser::require (enum token_id tok_id)
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id != tok_id)
+    error_at (tok->index, "expected %s; got %s", token_id_name[tok_id],
+	      token_id_name[tok->id]);
+  m_lexer.consume ();
+}
+
+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);
+
+  fprintf (stderr, "%s\n", msg_with_index);
+  if (*m_err_out == NULL)
+    *m_err_out = msg_with_index;
+  else
+    free (msg_with_index);
+}
+
+
+value *
+json::parse_utf8_string (size_t sz, const char *utf8_buf,
+			 char **err_out)
+{
+  gcc_assert (err_out);
+  gcc_assert (*err_out == NULL);
+
+  parser p (err_out);
+  p.add_utf8 (sz, utf8_buf);
+  value *result = p.parse_value ();
+  if (p.seen_error_p ())
+    {
+      gcc_assert (*err_out);
+      delete result;
+      return NULL;
+    }
+  return result;
+}
+
+value *
+json::parse_utf8_string (const char *utf8, char **err_out)
+{
+  return parse_utf8_string (strlen (utf8), utf8, err_out);
+}
+
+
+#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);
+}
+
+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);
+  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;
+}
+
+static void
+test_parse_number ()
+{
+  char *err = NULL;
+  json::value *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);
+  delete jv;
+}
+
+static void
+test_parse_array ()
+{
+  char *err = NULL;
+  json::value *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);
+  delete jv;
+}
+
+static void
+test_parse_object ()
+{
+  char *err = NULL;
+  json::value *jv
+    = parse_utf8_string ("{\"foo\": \"bar\", \"baz\": [42, 43]}", &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 ());
+
+  // etc, verify numbers
+  // etc, true, false, null
+
+  // TODO: error-handling
+  // TODO: partial document
+
+  /* We can't use assert_to_str_eq since ordering is not guaranteed.  */
+
+  delete jv;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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_c_tests ()
+{
+  test_parse_string ();
+  test_parse_number ();
+  test_parse_array ();
+  test_parse_object ();
+  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).  */
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json.h b/gcc/json.h
new file mode 100644
index 0000000..ddce444
--- /dev/null
+++ b/gcc/json.h
@@ -0,0 +1,161 @@
+/* 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
+
+namespace json
+{
+
+class value;
+  class string;
+  class number;
+  class object;
+  class array;
+  class literal;
+
+enum kind
+{
+  JSON_OBJECT,
+  JSON_ARRAY,
+  JSON_NUMBER,
+  JSON_STRING,
+  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;
+  virtual value *clone () const = 0;
+
+  char *to_str () const;
+  void dump (FILE *) const;
+
+  const object *as_object () const;
+  const array *as_array () const;
+  const number *as_number () const;
+  const string *as_string () const;
+};
+
+/* Subclass of value for objects: 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;
+  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 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;
+};
+
+/* Parsing decls.  */
+
+extern value *parse_utf8_string (size_t sz, 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/lsp-main.c b/gcc/lsp-main.c
new file mode 100644
index 0000000..00c7940
--- /dev/null
+++ b/gcc/lsp-main.c
@@ -0,0 +1,157 @@
+/* Language Server Protocol implementation.
+   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 "http-server.h"
+#include "json.h"
+#include "json-rpc.h"
+#include "lsp.h"
+#include "blt.h"
+#include "tree.h"
+
+using namespace lsp;
+
+class is_record_definition : public blt_predicate
+{
+ public:
+  is_record_definition (tree record_type) : m_record_type (record_type) {}
+
+  bool satisfied_by_node_p (const blt_node &node) FINAL OVERRIDE
+  {
+    if (node.get_kind () == BLT_STRUCT_CONTENTS
+	&& node.get_tree () == m_record_type)
+      return true;
+    return false;
+  }
+
+ private:
+  tree m_record_type;
+};
+
+static bool
+Location_from_blt_node (const blt_node *node, Location &out)
+{
+  if (!node)
+    return false;
+
+  location_t start = node->get_start ();
+  if (start == UNKNOWN_LOCATION)
+    return false;
+  location_t finish = node->get_finish ();
+  if (finish == UNKNOWN_LOCATION)
+    return false;
+
+  expanded_location el_start = expand_location (start);
+  expanded_location el_finish = expand_location (finish);
+  if (el_start.file != el_finish.file)
+    return false;
+
+  out.uri = el_start.file;
+  out.range.start.line = el_start.line;
+  out.range.start.character = el_start.column;
+  out.range.end.line = el_finish.line;
+  out.range.end.character = el_finish.column;
+  return true;
+}
+
+/* Implementation of lsp::server.
+   Ideally we'd wire this up to GCC's IR.  */
+
+class gcc_lsp_server : public lsp::noop_server
+{
+  /* Implementation of "textDocument/definition":
+     find the definition of the symbol at a given source location.  */
+
+  void
+  do_text_document_definition (const TextDocumentPositionParams &p,
+			       vec<Location> &out) OVERRIDE;
+};
+
+
+/* class gcc_lsp_server : public lsp::noop_server.  */
+
+/* Implementation of "textDocument/definition":
+   find the definition of the symbol at a given source location.  */
+
+void
+gcc_lsp_server::do_text_document_definition (const TextDocumentPositionParams &p,
+					     vec<Location> &out)
+{
+  /* TODO.  */
+  blt_node *blt
+    = the_blt_root_node->get_descendant_at_location (p.textDocument.uri,
+						     p.position.line,
+						     p.position.character);
+  if (!blt)
+    /* No results.  */
+    return;
+
+  blt->dump (stderr);
+
+  switch (blt->get_kind ())
+    {
+    case BLT_STRUCT_OR_UNION_SPECIFIER:
+      {
+	/* Attempt to locate the RECORD_TYPE for the
+	   struct-or-union-specifier.  */
+	tree record_type = blt->get_tree ();
+	if (!record_type)
+	  return;
+
+	if (1)
+	  the_blt_root_node->dump (stderr);
+
+	/* Find a struct-contents with tree == record_type.  */
+	is_record_definition pred (record_type);
+	blt_node *blt_struct_contents
+	  = the_blt_root_node->find_descendant_satisfying (pred);
+	if (!blt_struct_contents)
+	  return;
+
+	if (1)
+	  blt_struct_contents->dump (stderr);
+	blt_node *blt_struct_defn = blt_struct_contents->get_parent ();
+
+	Location result;
+	if (Location_from_blt_node (blt_struct_defn, result))
+	  out.safe_push (result);
+      }
+      break;
+
+    default:
+      /* No results.  */
+      return;
+    }
+}
+
+/* Serve LSP on PORT.  */
+
+void
+serve_lsp (int port)
+{
+  // FIXME
+  gcc_lsp_server lsp_server;
+  lsp::jsonrpc_server json_lsp_server (true, lsp_server);
+  jsonrpc::http_server http_server (json_lsp_server);
+
+  fprintf (stderr, "serving on port %i\n", port);
+  http_server.serve (port);
+}
diff --git a/gcc/lsp-main.h b/gcc/lsp-main.h
new file mode 100644
index 0000000..ec57201
--- /dev/null
+++ b/gcc/lsp-main.h
@@ -0,0 +1,25 @@
+/* Entrypoint to Language Server Protocol implementation.
+   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_LSP_MAIN_H
+#define GCC_LSP_MAIN_H
+
+extern void serve_lsp (int port);
+
+#endif  /* GCC_LSP_MAIN_H  */
diff --git a/gcc/lsp.c b/gcc/lsp.c
new file mode 100644
index 0000000..f64643e
--- /dev/null
+++ b/gcc/lsp.c
@@ -0,0 +1,333 @@
+/* Language Server Protocol implementation.
+   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 "http-server.h"
+#include "json-rpc.h"
+#include "lsp.h"
+#include "selftest.h"
+
+using namespace jsonrpc;
+using namespace lsp;
+
+// TODO: autogenerate the interface binding/marshalling/demarshalling code
+// from an interface description.
+
+#define GET_AS_OBJECT_OBJ(PARAMS) \
+  const json::object *obj = (PARAMS)->as_object (); \
+  if (!obj)					    \
+    {						    \
+      out_err = make_invalid_params (NULL, "not an object");   \
+      return result;				    \
+    }
+
+static bool
+extract_int (const json::object *obj, const char *name,
+	     int &out, json::value *&out_err)
+{
+  const json::value *v = obj->get (name);
+  if (!v)
+    {
+      char *msg = xasprintf ("missing int attribute: \"%s\"", name);
+      out_err = make_invalid_params (NULL, msg);
+      free (msg);
+      return false;
+    }
+  const json::number *n = v->as_number ();
+  if (!n)
+    {
+      char *msg = xasprintf ("not a number: \"%s\"", name);
+      out_err = make_invalid_params (NULL, msg);
+      free (msg);
+      return false;
+    }
+  out = n->get ();
+  return true;
+}
+
+static bool
+extract_string (const json::object *obj, const char *name,
+		const char *&out, json::value *&out_err)
+{
+  const json::value *v = obj->get (name);
+  if (!v)
+    {
+      char *msg = xasprintf ("missing string attribute: \"%s\"", name);
+      out_err = make_invalid_params (NULL, msg);
+      free (msg);
+      return false;
+    }
+  const json::string *s = v->as_string ();
+  if (!s)
+    {
+      char *msg = xasprintf ("not a string: \"%s\"", name);
+      out_err = make_invalid_params (NULL, msg);
+      free (msg);
+      return false;
+    }
+  out = s->get_string ();
+  return true;
+}
+
+#define SET_NUMBER(LHS, NAME)				\
+  do {								\
+    if (!extract_int (obj, NAME, LHS, out_err))		\
+      return result;						\
+  } while (0)
+
+#define SET_STRING(LHS, NAME)				\
+  do {								\
+    if (!extract_string (obj, NAME, LHS, out_err))		\
+      return result;						\
+  } while (0)
+
+Position
+Position::from_json (const json::value *params,
+		     json::value *&out_err)
+{
+  Position result;
+  GET_AS_OBJECT_OBJ (params);
+  SET_NUMBER (result.line, "line");
+  SET_NUMBER (result.character, "character");
+  return result;
+}
+
+TextDocumentIdentifier
+TextDocumentIdentifier::from_json (const json::value *params,
+				   json::value *&out_err)
+{
+  TextDocumentIdentifier result;
+  GET_AS_OBJECT_OBJ (params);
+  SET_STRING (result.uri, "uri");
+  return result;
+}
+
+TextDocumentItem
+TextDocumentItem::from_json (const json::value *params,
+			     json::value *&out_err)
+{
+  TextDocumentItem result;
+  GET_AS_OBJECT_OBJ (params);
+  SET_STRING (result.uri, "uri");
+  SET_STRING (result.languageId, "languageId");
+  SET_NUMBER (result.version, "version");
+  SET_STRING (result.text, "text");
+  return result;
+}
+
+DidOpenTextDocumentParams
+DidOpenTextDocumentParams::from_json (const json::value *params,
+				      json::value *&out_err)
+{
+  DidOpenTextDocumentParams result;
+  GET_AS_OBJECT_OBJ (params);
+  // FIXME: error-handling
+  result.textDocument
+    = TextDocumentItem::from_json (obj->get ("textDocument"), out_err);
+  return result;
+}
+
+DidChangeTextDocumentParams
+DidChangeTextDocumentParams::from_json (const json::value *params,
+					json::value *&out_err)
+{
+  DidChangeTextDocumentParams result;
+  GET_AS_OBJECT_OBJ (params);
+
+  // FIXME
+  return result;
+}
+
+TextDocumentPositionParams
+TextDocumentPositionParams::from_json (const json::value *params,
+				       json::value *&out_err)
+{
+  TextDocumentPositionParams result;
+  GET_AS_OBJECT_OBJ (params);
+  result.textDocument
+    = TextDocumentIdentifier::from_json (obj->get ("textDocument"), out_err);
+  result.position
+    = Position::from_json (obj->get ("position"), out_err);
+  return result;
+}
+
+/* struct Position.  */
+
+json::value *
+Position::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("line", new json::number (line));
+  result->set ("character", new json::number (character));
+  return result;
+}
+
+/* struct Range.  */
+
+json::value *
+Range::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("start", start.to_json ());
+  result->set ("end", end.to_json ());
+  return result;
+}
+
+/* struct Location.  */
+
+json::value *
+Location::to_json () const
+{
+  json::object *result = new json::object ();
+  result->set ("uri", new json::string (uri));
+  result->set ("range", range.to_json ());
+  return result;
+}
+
+/* class lsp::jsonrpc_server : public ::jsonrpc::server.  */
+
+json::value *
+lsp::jsonrpc_server::dispatch (const char *method, const json::value *params,
+			       const json::value *id)
+{
+  if (0 == strcmp (method, "initialize"))
+    return do_initialize (id, params);
+  if (0 == strcmp (method, "textDocument/didOpen"))
+    return do_text_document_did_open (params);
+  if (0 == strcmp (method, "textDocument/didChange"))
+    return do_text_document_did_change (params);
+  if (0 == strcmp (method, "textDocument/definition"))
+    return do_text_document_definition (params);
+  return make_method_not_found (id, method);
+}
+
+json::value *
+lsp::jsonrpc_server::do_initialize (const json::value *id,
+				    const json::value *params)
+{
+  // FIXME: for now, ignore params
+
+  json::object *server_caps = new json::object ();
+  json::object *result = new json::object ();
+  result->set ("capabilities", server_caps);
+  return make_success (id, result);
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_did_open (const json::value *params)
+{
+  json::value *err = NULL;
+  DidOpenTextDocumentParams p
+    = DidOpenTextDocumentParams::from_json (params, err);
+  if (err)
+    return err; // though we ought not to return non-NULL for a notification
+
+  m_inner.do_text_document_did_open (p);
+  return NULL; // notification, so no response
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_did_change (const json::value *params)
+{
+  json::value *err = NULL;
+  DidChangeTextDocumentParams p
+    = DidChangeTextDocumentParams::from_json (params, err);
+  if (err)
+    return err; // though we ought not to return non-NULL for a notification
+
+  m_inner.do_text_document_did_change (p);
+
+  return NULL; // notification, so no response
+}
+
+json::value *
+lsp::jsonrpc_server::do_text_document_definition (const json::value *params)
+{
+  json::value *err = NULL;
+  TextDocumentPositionParams p
+    = TextDocumentPositionParams::from_json (params, err);
+  if (err)
+    return err;
+
+  auto_vec<Location> out;
+  m_inner.do_text_document_definition (p, out);
+
+  json::array *result = new json::array ();
+  unsigned i;
+  Location *loc;
+  FOR_EACH_VEC_ELT (out, i, loc)
+    result->append (loc->to_json ());
+  return result;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+static void
+test_simple ()
+{
+  noop_server noop;
+  lsp::jsonrpc_server js (true, noop);
+  const char *init_request
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"initialize\","
+       " \"params\": [42, 23], \"id\": 1}"); // FIXME
+  json::value *init_response = js.handle_request_string (init_request);
+  //const json::value *result = assert_is_success (response, 1);
+  //ASSERT_EQ (19, result->as_number ()->get ());
+  delete init_response;
+
+  const char *did_open_note
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didOpen\","
+       " \"params\": {"
+       " \"textDocument\": "
+       " { \"uri\": \"DocumentUri goes here\","
+         " \"languageId\": \"c++\","
+         " \"version\": 0,"
+         " \"text\": \"/* Initial content.  */\"}}}");
+  json::value *did_open_response = js.handle_request_string (did_open_note);
+  delete did_open_response;
+
+  const char *did_change_note
+    = ("{\"jsonrpc\": \"2.0\", \"method\": \"textDocument/didChange\","
+       " \"params\": {"
+         " \"textDocument\": {\"version\": 1},"
+         " \"contentChanges\": [{\"text\": \"/* Hello world.  */\"}]"
+       "}}"); // FIXME
+  json::value *did_change_response = js.handle_request_string (did_change_note);
+  delete did_change_response;
+}
+
+
+/* Run all of the selftests within this file.  */
+
+void
+lsp_c_tests ()
+{
+  test_simple ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/lsp.h b/gcc/lsp.h
new file mode 100644
index 0000000..b9c99c8
--- /dev/null
+++ b/gcc/lsp.h
@@ -0,0 +1,185 @@
+/* Language Server Protocol implementation.
+   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_LSP_H
+#define GCC_LSP_H
+
+namespace lsp {
+
+typedef const char *DocumentUri;
+
+/* Interfaces from the protocol specification (which uses camel case).  */
+
+struct Position
+{
+  static Position from_json (const json::value *params,
+			     json::value *&out_err);
+  json::value *to_json () const;
+
+  int line;
+  int character;
+};
+
+struct Range
+{
+  json::value *to_json () const;
+
+  Position start;
+  Position end;
+};
+
+struct Location
+{
+  json::value *to_json () const;
+
+  DocumentUri uri;
+  Range range;
+};
+
+// Exceptions would be nicer than passing around the out_err
+
+// TODO: autogenerate the interface binding/marshalling/demarshalling code
+// from an interface description.
+
+struct TextDocumentIdentifier
+{
+  static TextDocumentIdentifier from_json (const json::value *params,
+					   json::value *&out_err);
+
+  DocumentUri uri;
+};
+
+struct TextDocumentItem
+{
+  static TextDocumentItem from_json (const json::value *params,
+				     json::value *&out_err);
+
+  DocumentUri uri;
+  const char *languageId;
+  int version;
+  const char *text;
+};
+
+struct DidOpenTextDocumentParams
+{
+  static DidOpenTextDocumentParams from_json (const json::value *params,
+					      json::value *&out_err);
+
+  TextDocumentItem textDocument;
+};
+
+struct DidChangeTextDocumentParams
+{
+ public:
+  static DidChangeTextDocumentParams from_json (const json::value *params,
+						json::value *&out_err);
+
+ private:
+#if 0
+  VersionedTextDocumentIdentifier textDocument;
+  auto_vec<TextDocumentContentChangeEvent> contentChanges;
+#endif
+};
+
+struct TextDocumentPositionParams
+{
+  static TextDocumentPositionParams from_json (const json::value *params,
+					       json::value *&out_err);
+
+  TextDocumentIdentifier textDocument;
+  Position position;
+};
+
+/* An abstract base class for implementing the LSP as vfunc calls,
+   avoiding dealing with JSON.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+
+  virtual void
+  do_text_document_did_open (const DidOpenTextDocumentParams &p) = 0;
+
+  virtual void
+  do_text_document_did_change (const DidChangeTextDocumentParams &p) = 0;
+
+  virtual void
+  do_text_document_definition (const TextDocumentPositionParams &p,
+			       vec<Location> &out) = 0;
+};
+
+/* A concrete subclass of lsp::server that implements everything as a no-op.  */
+
+class noop_server : public server
+{
+  void
+  do_text_document_did_open (const DidOpenTextDocumentParams &p) OVERRIDE
+  {
+    // no-op
+  }
+
+  void
+  do_text_document_did_change (const DidChangeTextDocumentParams &p) OVERRIDE
+  {
+    // no-op
+  }
+
+  void
+  do_text_document_definition (const TextDocumentPositionParams &p,
+			       vec<Location> &out) OVERRIDE
+  {
+    // no-op
+  }
+};
+
+/* A jsonrpc::server subclass that decodes incoming JSON-RPC requests
+   and dispatches them to an lsp_server instance as vfunc calls,
+   marshalling the inputs/outputs to/from JSON objects.  */
+
+class jsonrpc_server : public ::jsonrpc::server
+{
+ public:
+  jsonrpc_server (bool verbose, ::lsp::server &inner)
+  : server (verbose), m_inner (inner) {}
+
+  json::value *
+  dispatch (const char *method, const json::value *params,
+	    const json::value *id) FINAL OVERRIDE;
+
+ private:
+  json::value *
+  do_initialize (const json::value *id, const json::value *params);
+
+  json::value *
+  do_text_document_did_open (const json::value *params);
+
+  json::value *
+  do_text_document_did_change (const json::value *params);
+
+  json::value *
+  do_text_document_definition (const json::value *params);
+
+ private:
+  ::lsp::server &m_inner;
+};
+
+} // namespace lsp
+
+#endif  /* GCC_LSP_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index f62bc72..a84c53b 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -66,6 +66,10 @@ selftest::run_tests ()
   sreal_c_tests ();
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
+  http_server_c_tests ();
+  json_c_tests ();
+  json_rpc_c_tests ();
+  lsp_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index dad53e9..2e266c4 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -182,7 +182,11 @@ extern void gimple_c_tests ();
 extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
+extern void http_server_c_tests ();
 extern void input_c_tests ();
+extern void json_c_tests ();
+extern void json_rpc_c_tests ();
+extern void lsp_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
diff --git a/gcc/server.c b/gcc/server.c
new file mode 100644
index 0000000..871a95f
--- /dev/null
+++ b/gcc/server.c
@@ -0,0 +1,152 @@
+/* Abstract server implementation.
+   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 "server.h"
+
+/* adapted from GNU libc docs.  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#define MAXMSG  512
+
+static int
+make_socket (uint16_t port)
+{
+  int sock;
+  struct sockaddr_in name;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
+
+/* Serve on PORT.  */
+
+void
+server::serve (int port)
+{
+  int sock;
+  fd_set active_fd_set, read_fd_set;
+  int i;
+  struct sockaddr_in clientname;
+  socklen_t size;
+
+  /* Create the socket and set it up to accept connections. */
+  sock = make_socket (port);
+  if (listen (sock, 1) < 0)
+    {
+      perror ("listen");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Initialize the set of active sockets. */
+  FD_ZERO (&active_fd_set);
+  FD_SET (sock, &active_fd_set);
+
+  while (1)
+    {
+      /* Block until input arrives on one or more active sockets. */
+      read_fd_set = active_fd_set;
+      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
+	{
+	  perror ("select");
+	  exit (EXIT_FAILURE);
+	}
+
+      /* Service all the sockets with input pending. */
+      for (i = 0; i < FD_SETSIZE; ++i)
+	if (FD_ISSET (i, &read_fd_set))
+	  {
+	    if (i == sock)
+	      {
+		/* Connection request on original socket. */
+		int new_;
+		size = sizeof (clientname);
+		new_ = accept (sock,
+			       (struct sockaddr *) &clientname,
+			       &size);
+		if (new_ < 0)
+		  {
+		    perror ("accept");
+		    exit (EXIT_FAILURE);
+		  }
+		fprintf (stderr,
+			 "Server: connect from host %s, port %hd.\n",
+			 inet_ntoa (clientname.sin_addr),
+			 ntohs (clientname.sin_port));
+		FD_SET (new_, &active_fd_set);
+	      }
+	    else
+	      {
+		/* Data arriving on an already-connected socket. */
+		if (read_from_client (file_descriptor (i)) < 0)
+		  {
+		    close (i);
+		    FD_CLR (i, &active_fd_set);
+		  }
+	      }
+	  }
+    }
+}
+
+int
+server::read_from_client (file_descriptor fd)
+{
+  char buffer[MAXMSG];
+  int nbytes;
+
+  nbytes = read (fd.m_fd, buffer, MAXMSG);
+  if (nbytes < 0)
+    {
+      /* Read error. */
+      perror ("read");
+      exit (EXIT_FAILURE);
+    }
+  else if (nbytes == 0)
+    /* End-of-file. */
+    return -1;
+  else
+    {
+      /* Data read. */
+      on_read (fd, nbytes, buffer);
+      return 0;
+    }
+}
diff --git a/gcc/server.h b/gcc/server.h
new file mode 100644
index 0000000..3bcf9f6e
--- /dev/null
+++ b/gcc/server.h
@@ -0,0 +1,46 @@
+/* Abstract server implementation.
+   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_SERVER_H
+#define GCC_SERVER_H
+
+/* Wrapper aroung "int" for file descriptors.  */
+
+struct file_descriptor
+{
+  explicit file_descriptor (int fd) : m_fd (fd) {}
+
+  int m_fd;
+};
+
+/* Abstract base class for implementing a server.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+  void serve (int port);
+
+  virtual void on_read (file_descriptor fd, size_t length, const char *buf) = 0;
+
+ private:
+  int read_from_client (file_descriptor fd);
+};
+
+#endif  /* GCC_SERVER_H  */
diff --git a/gcc/testsuite/gcc.dg/lsp/test.c b/gcc/testsuite/gcc.dg/lsp/test.c
new file mode 100644
index 0000000..bb80bd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/test.c
@@ -0,0 +1,12 @@
+/* */
+
+struct foo
+{
+  int color;
+  int shape;
+};
+
+int test (struct foo *ptr)
+{
+  ptr->
+}
diff --git a/gcc/testsuite/gcc.dg/lsp/test.py b/gcc/testsuite/gcc.dg/lsp/test.py
new file mode 100644
index 0000000..e8450c7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lsp/test.py
@@ -0,0 +1,133 @@
+import json
+
+import jsonrpc # pip install json-rpc
+import requests
+
+# Various types to model the LSP interface
+
+class Position:
+    def __init__(self, line, character):
+        self.line = line
+        self.character = character
+
+    def __repr__(self):
+        return 'Position(%r, %r)' % (self.line, self.character)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {'line': self.line, 'character': self.character}
+
+    @staticmethod
+    def from_json(js):
+        return Position(js['line'], js['character'])
+
+class Range:
+    def __init__(self, start, end):
+        self.start = start
+        self.end = end
+
+    def __repr__(self):
+        return 'Range(%r, %r)' % (self.start, self.end)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    @staticmethod
+    def from_json(js):
+        return Range(Position.from_json(js['start']),
+                     Position.from_json(js['end']))
+
+class Location:
+    def __init__(self, uri, range):
+        self.uri = uri
+        self.range = range
+
+    def __repr__(self):
+        return 'Location(%r, %r)' % (self.uri, self.range)
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    @staticmethod
+    def from_json(js):
+        return Location(js['uri'], Range.from_json(js['range']))
+
+class TextDocumentIdentifier:
+    def __init__(self, uri):
+        self.uri = uri
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {'uri': self.uri}
+
+class TextDocumentPositionParams:
+    def __init__(self, textDocument, position):
+        self.textDocument = textDocument
+        self.position = position
+
+    def __eq__(self, other):
+        return self.__dict__ == other.__dict__
+
+    def to_json(self):
+        return {"textDocument" : self.textDocument.to_json(),
+                "position" : self.position.to_json()}
+
+# A wrapper for making LSP calls against a server
+
+class LspProxy:
+    def __init__(self, url):
+        self.url = url
+        self.next_id = 0
+
+    def make_request(self, method, params):
+        json_req = {"method": method,
+                    "params": params,
+                    "jsonrpc": "2.0",
+                    "id": self.next_id}
+        self.next_id += 1
+        return json_req
+
+    def post_request(self, method, params):
+        payload = self.make_request(method, params)
+        headers = {'content-type': 'application/json'}
+        response = requests.post(self.url, data=json.dumps(payload),
+                                 headers=headers)
+        print('response: %r' % response)
+        print('response.json(): %r' % response.json())
+        return response.json()
+
+    def goto_definition(self, textDocument, position):
+        params = TextDocumentPositionParams(textDocument, position)
+        json_resp = self.post_request('textDocument/definition',
+                                      params.to_json())
+        print(json_resp)
+        # Expect either a Location or a list of Location
+        if isinstance(json_resp, list):
+            return [Location.from_json(item) for item in json_resp]
+        else:
+            return Location.from_json(json_resp)
+
+def main():
+    # This assumes that we're running this:
+    # (hardcoding the particular source file for now):
+    # ./xgcc -B. -c ../../src/gcc/testsuite/gcc.dg/lsp/test.c \
+    #    -flsp=4000 -fblt -wrapper gdb,--args
+
+    url = "http://localhost:4000/jsonrpc"
+    proxy = LspProxy(url)
+    # Ask for the location of a usage of "struct foo"
+    result = proxy.goto_definition(TextDocumentIdentifier('test.c'),
+                                   Position(9, 17))
+
+    # We expect to get back the location of where "struct foo" is defined
+    print(result)
+    # FIXME: filename/uri (currently a particular relative location)
+    assert result == [Location(u'../../src/gcc/testsuite/gcc.dg/lsp/test.c',
+                               Range(Position(3, 1), Position(7, 1)))]
+
+if __name__ == "__main__":
+    main()
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 54a4f05..9532106 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -79,6 +79,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-offload.h"
 #include "hsa-common.h"
 #include "edit-context.h"
+#include "lsp-main.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -469,6 +470,9 @@ compile_file (void)
   timevar_pop (TV_PARSE_GLOBAL);
   timevar_stop (TV_PHASE_PARSING);
 
+  if (flag_lsp != -1)
+    serve_lsp (flag_lsp);
+
   if (flag_dump_locations)
     dump_location_info (stderr);
 
