public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: [02/33] linemaps
       [not found] ` <c14417b3-4286-d877-f6e2-925bfc754684@acm.org>
@ 2020-11-03 19:39   ` Nathan Sidwell
  2020-11-03 21:13   ` [02/32] linemaps Nathan Sidwell
       [not found]   ` <0bdf00a8-d8ad-9e97-134d-6668c0e8c86b@acm.org>
  2 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 19:39 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Location handling needs to add 2 things

1) a new kind of inclusion -- namely a module.  We add LC_MODULE as a 
map kind,

2) the ability to allocate blocks of line-maps for both ordinary 
locations and macro locations, that are then filled in by the module loader.

-- 
Nathan Sidwell

[-- Attachment #2: 02-cpp-line-maps.diff --]
[-- Type: text/x-patch, Size: 10441 bytes --]

diff --git c/libcpp/include/line-map.h w/libcpp/include/line-map.h
index 44008be5c08..50b2e4ff91a 100644
--- c/libcpp/include/line-map.h
+++ w/libcpp/include/line-map.h
@@ -72,6 +72,7 @@ enum lc_reason
   LC_RENAME,		/* Other reason for name change.  */
   LC_RENAME_VERBATIM,	/* Likewise, but "" != stdin.  */
   LC_ENTER_MACRO,	/* Begin macro expansion.  */
+  LC_MODULE,		/* A (C++) Module.  */
   /* FIXME: add support for stringize and paste.  */
   LC_HWM /* High Water Mark.  */
 };
@@ -439,7 +440,8 @@ struct GTY((tag ("1"))) line_map_ordinary : public line_map {
 
   /* Location from whence this line map was included.  For regular
      #includes, this location will be the last location of a map.  For
-     outermost file, this is 0.  */
+     outermost file, this is 0.  For modules it could be anywhere
+     within a map.  */
   location_t included_from;
 
   /* Size is 20 or 24 bytes, no padding  */
@@ -662,6 +664,15 @@ ORDINARY_MAP_IN_SYSTEM_HEADER_P (const line_map_ordinary *ord_map)
   return ord_map->sysp;
 }
 
+/* TRUE if this line map is for a module (not a source file).  */
+
+inline bool
+MAP_MODULE_P (const line_map *map)
+{
+  return (MAP_ORDINARY_P (map)
+	  && linemap_check_ordinary (map)->reason == LC_MODULE);
+}
+
 /* Get the filename of ordinary map MAP.  */
 
 inline const char *
@@ -1076,6 +1087,9 @@ extern void linemap_check_files_exited (class line_maps *);
 extern location_t linemap_line_start
 (class line_maps *set, linenum_type to_line,  unsigned int max_column_hint);
 
+/* Allocate a raw block of line maps, zero initialized.  */
+extern line_map *line_map_new_raw (line_maps *, bool, unsigned);
+
 /* Add a mapping of logical source line to physical source file and
    line number. This function creates an "ordinary map", which is a
    map that records locations of tokens that are not part of macro
@@ -1093,6 +1107,39 @@ extern const line_map *linemap_add
   (class line_maps *, enum lc_reason, unsigned int sysp,
    const char *to_file, linenum_type to_line);
 
+/* Create a macro map.  A macro map encodes source locations of tokens
+   that are part of a macro replacement-list, at a macro expansion
+   point. See the extensive comments of struct line_map and struct
+   line_map_macro, in line-map.h.
+
+   This map shall be created when the macro is expanded. The map
+   encodes the source location of the expansion point of the macro as
+   well as the "original" source location of each token that is part
+   of the macro replacement-list. If a macro is defined but never
+   expanded, it has no macro map.  SET is the set of maps the macro
+   map should be part of.  MACRO_NODE is the macro which the new macro
+   map should encode source locations for.  EXPANSION is the location
+   of the expansion point of MACRO. For function-like macros
+   invocations, it's best to make it point to the closing parenthesis
+   of the macro, rather than the the location of the first character
+   of the macro.  NUM_TOKENS is the number of tokens that are part of
+   the replacement-list of MACRO.  */
+const line_map_macro *linemap_enter_macro (line_maps *, cpp_hashnode *,
+					   location_t, unsigned int);
+
+/* Create a source location for a module.  The creator must either do
+   this after the TU is tokenized, or deal with saving and restoring
+   map state.  */
+
+extern location_t linemap_module_loc
+  (line_maps *, location_t from, const char *name);
+extern void linemap_module_reparent
+  (line_maps *, location_t loc, location_t new_parent);
+
+/* Restore the linemap state such that the map at LWM-1 continues.  */
+extern void linemap_module_restore
+  (line_maps *, unsigned lwm);
+
 /* Given a logical source location, returns the map which the
    corresponding (source file, line, column) triplet can be deduced
    from. Since the set is built chronologically, the logical lines are
@@ -1102,6 +1149,8 @@ extern const line_map *linemap_add
 extern const line_map *linemap_lookup
   (const line_maps *, location_t);
 
+unsigned linemap_lookup_macro_index (const line_maps *, location_t);
+
 /* Returns TRUE if the line table set tracks token locations across
    macro expansion, FALSE otherwise.  */
 bool linemap_tracks_macro_expansion_locs_p (class line_maps *);
diff --git c/libcpp/line-map.c w/libcpp/line-map.c
index 5a74174579f..4b2b7ef4394 100644
--- c/libcpp/line-map.c
+++ w/libcpp/line-map.c
@@ -378,23 +378,21 @@ linemap_check_files_exited (line_maps *set)
 	     ORDINARY_MAP_FILE_NAME (map));
 }
 
-/* Create a new line map in the line map set SET, and return it.
-   REASON is the reason of creating the map. It determines the type
-   of map created (ordinary or macro map). Note that ordinary maps and
-   macro maps are allocated in different memory location.  */
+/* Create NUM zero-initialized maps of type MACRO_P.  */
 
-static struct line_map *
-new_linemap (line_maps *set,  location_t start_location)
+line_map *
+line_map_new_raw (line_maps *set, bool macro_p, unsigned num)
 {
-  bool macro_p = start_location >= LINE_MAP_MAX_LOCATION;
   unsigned num_maps_allocated = LINEMAPS_ALLOCATED (set, macro_p);
   unsigned num_maps_used = LINEMAPS_USED (set, macro_p);
-
-  if (num_maps_used == num_maps_allocated)
+  
+  if (num > num_maps_allocated - num_maps_used)
     {
       /* We need more space!  */
       if (!num_maps_allocated)
 	num_maps_allocated = 128;
+      if (num_maps_allocated < num_maps_used + num)
+	num_maps_allocated = num_maps_used + num;
       num_maps_allocated *= 2;
 
       size_t size_of_a_map;
@@ -429,6 +427,7 @@ new_linemap (line_maps *set,  location_t start_location)
 	      (num_maps - num_maps_used) * size_of_a_map);
       if (macro_p)
 	set->info_macro.maps = (line_map_macro *)buffer;
+
       else
 	set->info_ordinary.maps = (line_map_ordinary *)buffer;
       LINEMAPS_ALLOCATED (set, macro_p) = num_maps;
@@ -436,13 +435,39 @@ new_linemap (line_maps *set,  location_t start_location)
 
   line_map *result = (macro_p ? (line_map *)&set->info_macro.maps[num_maps_used]
 		      : (line_map *)&set->info_ordinary.maps[num_maps_used]);
-  LINEMAPS_USED (set, macro_p)++;
+  LINEMAPS_USED (set, macro_p) += num;
+
+  return result;
+}
+
+/* Create a new line map in the line map set SET, and return it.
+   REASON is the reason of creating the map. It determines the type
+   of map created (ordinary or macro map). Note that ordinary maps and
+   macro maps are allocated in different memory location.  */
+
+static struct line_map *
+new_linemap (line_maps *set, location_t start_location)
+{
+  line_map *result = line_map_new_raw (set,
+				       start_location >= LINE_MAP_MAX_LOCATION,
+				       1);
 
   result->start_location = start_location;
 
   return result;
 }
 
+/* Return the location of the last source line within an ordinary
+   map.  */
+inline location_t
+LAST_SOURCE_LINE_LOCATION (const line_map_ordinary *map)
+{
+  return (((map[1].start_location - 1
+	    - map->start_location)
+	   & ~((1 << map->m_column_and_range_bits) - 1))
+	  + map->start_location);
+}
+
 /* Add a mapping of logical source line to physical source file and
    line number.
 
@@ -570,6 +595,51 @@ linemap_add (line_maps *set, enum lc_reason reason,
   return map;
 }
 
+/* Create a location for a module NAME imported at FROM.  */
+
+location_t
+linemap_module_loc (line_maps *set, location_t from, const char *name)
+{
+  const line_map_ordinary *map
+    = linemap_check_ordinary (linemap_add (set, LC_MODULE, false, name, 0));
+  const_cast <line_map_ordinary *> (map)->included_from = from;
+
+  location_t loc = linemap_line_start (set, 0, 0);
+
+  return loc;
+}
+
+void
+linemap_module_reparent (line_maps *set, location_t loc, location_t adoptor)
+{
+  const line_map_ordinary *map = linemap_ordinary_map_lookup (set, loc);
+  const_cast<line_map_ordinary *> (map)->included_from = adoptor;
+}
+
+/* A linemap at LWM-1 was interrupted to insert module locations & imports.
+   Append a new map, continuing the interrupted one.  */
+
+void
+linemap_module_restore (line_maps *set, unsigned lwm)
+{
+  if (lwm && lwm != LINEMAPS_USED (set, false))
+    {
+      const line_map_ordinary *pre_map
+	= linemap_check_ordinary (LINEMAPS_MAP_AT (set, false, lwm - 1));
+      unsigned src_line = SOURCE_LINE (pre_map,
+				       LAST_SOURCE_LINE_LOCATION (pre_map));
+      location_t inc_at = pre_map->included_from;
+      if (const line_map_ordinary *post_map
+	  = (linemap_check_ordinary
+	     (linemap_add (set, LC_RENAME_VERBATIM,
+			   ORDINARY_MAP_IN_SYSTEM_HEADER_P (pre_map),
+			   ORDINARY_MAP_FILE_NAME (pre_map), src_line))))
+	/* linemap_add will think we were included from the same as
+	   the preceeding map.  */
+	const_cast <line_map_ordinary *> (post_map)->included_from = inc_at;
+    }
+}
+
 /* Returns TRUE if the line table set tracks token locations across
    macro expansion, FALSE otherwise.  */
 
@@ -1003,14 +1073,25 @@ linemap_macro_map_lookup (const line_maps *set, location_t line)
   if (set == NULL)
     return NULL;
 
+  unsigned ix = linemap_lookup_macro_index (set, line);
+  const struct line_map_macro *result = LINEMAPS_MACRO_MAP_AT (set, ix);
+  linemap_assert (MAP_START_LOCATION (result) <= line);
+
+  return result;
+}
+
+unsigned
+linemap_lookup_macro_index (const line_maps *set, location_t line)
+{
   unsigned mn = LINEMAPS_MACRO_CACHE (set);
   unsigned mx = LINEMAPS_MACRO_USED (set);
   const struct line_map_macro *cached = LINEMAPS_MACRO_MAP_AT (set, mn);
 
   if (line >= MAP_START_LOCATION (cached))
     {
-      if (mn == 0 || line < MAP_START_LOCATION (&cached[-1]))
-	return cached;
+      if (line < (MAP_START_LOCATION (cached)
+		  + MACRO_MAP_NUM_MACRO_TOKENS (cached)))
+	return mn;
       mx = mn - 1;
       mn = 0;
     }
@@ -1025,10 +1106,7 @@ linemap_macro_map_lookup (const line_maps *set, location_t line)
     }
 
   LINEMAPS_MACRO_CACHE (set) = mx;
-  const struct line_map_macro *result = LINEMAPS_MACRO_MAP_AT (set, mx);
-  linemap_assert (MAP_START_LOCATION (result) <= line);
-
-  return result;
+  return mx;
 }
 
 /* Return TRUE if MAP encodes locations coming from a macro
@@ -1747,7 +1825,7 @@ linemap_dump (FILE *stream, class line_maps *set, unsigned ix, bool is_macro)
 {
   const char *const lc_reasons_v[LC_HWM]
       = { "LC_ENTER", "LC_LEAVE", "LC_RENAME", "LC_RENAME_VERBATIM",
-	  "LC_ENTER_MACRO" };
+	  "LC_ENTER_MACRO", "LC_MODULE" };
   const line_map *map;
   unsigned reason;
 

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

* Re: [09/33] core diagnostics
       [not found]                 ` <35879e15-d74a-c664-4d44-15f4b3783d77@acm.org>
@ 2020-11-03 20:03                   ` Nathan Sidwell
  2020-11-21 16:56                     ` Jeff Law
  2020-11-03 21:14                   ` [11/32] instrumentation Nathan Sidwell
       [not found]                   ` <9ae23c4c-67a5-a267-c939-5a96e9488612@acm.org>
  2 siblings, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 20:03 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here are the changes for gcc/configure.ac (config.h.in and configure get 
rebuilt).  This is adding smarts to check for networking features, so 
that a network-aware module mapper can be built.


-- 
Nathan Sidwell

[-- Attachment #2: 10-core-config.diff --]
[-- Type: text/x-patch, Size: 5553 bytes --]

diff --git c/gcc/configure.ac w/gcc/configure.ac
index 73034bb902b..168a3bc3625 100644
--- c/gcc/configure.ac
+++ w/gcc/configure.ac
@@ -1417,8 +1419,8 @@ define(gcc_UNLOCKED_FUNCS, clearerr_unlocked feof_unlocked dnl
   putchar_unlocked putc_unlocked)
 AC_CHECK_FUNCS(times clock kill getrlimit setrlimit atoq \
 	popen sysconf strsignal getrusage nl_langinfo \
-	gettimeofday mbstowcs wcswidth mmap setlocale \
-	gcc_UNLOCKED_FUNCS madvise mallinfo mallinfo2)
+	gettimeofday mbstowcs wcswidth mmap memrchr posix_fallocate setlocale \
+	gcc_UNLOCKED_FUNCS madvise mallinfo execv mallinfo2 fstatat)
 
 if test x$ac_cv_func_mbstowcs = xyes; then
   AC_CACHE_CHECK(whether mbstowcs works, gcc_cv_func_mbstowcs_works,
@@ -1440,6 +1442,10 @@ fi
 
 AC_CHECK_TYPE(ssize_t, int)
 AC_CHECK_TYPE(caddr_t, char *)
+AC_CHECK_TYPE(sighander_t,
+  AC_DEFINE(HAVE_SIGHANDLER_T, 1,
+    [Define if <sys/signal.h> defines sighandler_t]),
+    ,signal.h)
 
 GCC_AC_FUNC_MMAP_BLACKLIST
 
@@ -1585,6 +1591,146 @@ if test $ac_cv_f_setlkw = yes; then
   [Define if F_SETLKW supported by fcntl.])
 fi
 
+# Check if O_CLOEXEC is defined by fcntl
+AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>]], [[
+return open ("/dev/null", O_RDONLY | O_CLOEXEC);]])],
+[ac_cv_o_cloexec=yes],[ac_cv_o_cloexec=no])])
+if test $ac_cv_o_cloexec = yes; then
+  AC_DEFINE(HOST_HAS_O_CLOEXEC, 1,
+  [Define if O_CLOEXEC supported by fcntl.])
+fi
+
+# C++ Modules would like some networking features to provide the mapping
+# server.  You can still use modules without them though.
+# The following network-related checks could probably do with some
+# Windows and other non-linux defenses and checking.
+
+# Local socket connectivity wants AF_UNIX networking
+# Check for AF_UNIX networking
+AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>]],[[
+sockaddr_un un;
+un.sun_family = AF_UNSPEC;
+int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&un, sizeof (un));]])],
+[ac_cv_af_unix=yes],
+[ac_cv_af_unix=no])])
+if test $ac_cv_af_unix = yes; then
+  AC_DEFINE(HAVE_AF_UNIX, 1,
+  [Define if AF_UNIX supported.])
+fi
+
+# Remote socket connectivity wants AF_INET6 networking
+# Check for AF_INET6 networking
+AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>]],[[
+sockaddr_in6 in6;
+in6.sin6_family = AF_UNSPEC;
+struct addrinfo *addrs = 0;
+struct addrinfo hints;
+hints.ai_flags = 0;
+hints.ai_family = AF_INET6;
+hints.ai_socktype = SOCK_STREAM;
+hints.ai_protocol = 0;
+hints.ai_canonname = 0;
+hints.ai_addr = 0;
+hints.ai_next = 0;
+int e = getaddrinfo ("localhost", 0, &hints, &addrs);
+const char *str = gai_strerror (e);
+freeaddrinfo (addrs);
+int fd = socket (AF_INET6, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&in6, sizeof (in6));]])],
+[ac_cv_af_inet6=yes],
+[ac_cv_af_inet6=no])])
+if test $ac_cv_af_inet6 = yes; then
+  AC_DEFINE(HAVE_AF_INET6, 1,
+  [Define if AF_INET6 supported.])
+fi
+
+# Efficient server response wants epoll
+# Check for epoll_create, epoll_ctl, epoll_pwait
+AC_CACHE_CHECK(for epoll, ac_cv_epoll, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/epoll.h>]],[[
+int fd = epoll_create (1);
+epoll_event ev;
+ev.events = EPOLLIN;
+ev.data.fd = 0;
+epoll_ctl (fd, EPOLL_CTL_ADD, 0, &ev);
+epoll_pwait (fd, 0, 0, -1, 0);]])],
+[ac_cv_epoll=yes],
+[ac_cv_epoll=no])])
+if test $ac_cv_epoll = yes; then
+  AC_DEFINE(HAVE_EPOLL, 1,
+  [Define if epoll_create, epoll_ctl, epoll_pwait provided.])
+fi
+
+# If we can't use epoll, try pselect.
+# Check for pselect
+AC_CACHE_CHECK(for pselect, ac_cv_pselect, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/select.h>]],[[
+pselect (0, 0, 0, 0, 0, 0);]])],
+[ac_cv_pselect=yes],
+[ac_cv_pselect=no])])
+if test $ac_cv_pselect = yes; then
+  AC_DEFINE(HAVE_PSELECT, 1,
+  [Define if pselect provided.])
+fi
+
+# And failing that, use good old select.
+# If we can't even use this, the server is serialized.
+# Check for select
+AC_CACHE_CHECK(for select, ac_cv_select, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/select.h>]],[[
+select (0, 0, 0, 0, 0);]])],
+[ac_cv_select=yes],
+[ac_cv_select=no])])
+if test $ac_cv_select = yes; then
+  AC_DEFINE(HAVE_SELECT, 1,
+  [Define if select provided.])
+fi
+
+# Avoid some fnctl calls by using accept4, when available.
+# Check for accept4
+AC_CACHE_CHECK(for accept4, ac_cv_accept4, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/socket.h>]],[[
+int err = accept4 (1, 0, 0, SOCK_NONBLOCK);]])],
+[ac_cv_accept4=yes],
+[ac_cv_accept4=no])])
+if test $ac_cv_accept4 = yes; then
+  AC_DEFINE(HAVE_ACCEPT4, 1,
+  [Define if accept4 provided.])
+fi
+
+# For better server messages, look for a way to stringize network addresses
+# Check for inet_ntop
+AC_CACHE_CHECK(for inet_ntop, ac_cv_inet_ntop, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <arpa/inet.h>
+#include <netinet/in.h>]],[[
+sockaddr_in6 in6;
+char buf[INET6_ADDRSTRLEN];
+const char *str = inet_ntop (AF_INET6, &in6, buf, sizeof (buf));]])],
+[ac_cv_inet_ntop=yes],
+[ac_cv_inet_ntop=no])])
+if test $ac_cv_inet_ntop = yes; then
+  AC_DEFINE(HAVE_INET_NTOP, 1,
+  [Define if inet_ntop provided.])
+fi
+
 # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests.
 CFLAGS="$saved_CFLAGS"
 CXXFLAGS="$saved_CXXFLAGS"

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

* [01/32] langhooks
       [not found] <7fc9e868-3db9-4972-ed67-6ff249f549c3@acm.org>
@ 2020-11-03 21:13 ` Nathan Sidwell
  2020-11-06 19:58   ` Jeff Law
       [not found] ` <c14417b3-4286-d877-f6e2-925bfc754684@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:13 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

I needed a set of hook interfacing the preprocessor to the language. 
they get called from pieces in c-family.

preprocess_main_file:  we need to know when any forced headers have been 
parsed in order to deal with linemaps and macro visibility

preprocess_options: A way for the language to adjust any preprocessor 
options and alter direct callbacks

preprocess_undef: We need visibility of #undefs

preprocess_deferred_macro: macros from header-units are instantiated 
lazily.  This is the hook for the preprocessor to get that done.

preprocess_token: Even in -E processing, we need to observe the token 
stream in order to load up the macro tables of header units.

c-family's c-lex.c, c-opts.c & c-ppoutput.c get to call these hooks in 
various cases

-- 
Nathan Sidwell


[-- Attachment #2: 01-langhooks.diff --]
[-- Type: text/x-patch, Size: 13417 bytes --]

diff --git c/gcc/c-family/c-lex.c w/gcc/c-family/c-lex.c
index e81e16ddc26..44575473719 100644
--- c/gcc/c-family/c-lex.c
+++ w/gcc/c-family/c-lex.c
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-pragma.h"
 #include "debug.h"
 #include "file-prefix-map.h" /* remap_macro_filename()  */
-
+#include "langhooks.h"
 #include "attribs.h"
 
 /* We may keep statistics about how long which files took to compile.  */
@@ -274,9 +274,11 @@ cb_define (cpp_reader *pfile, location_t loc, cpp_hashnode *node)
 
 /* #undef callback for DWARF and DWARF2 debug info.  */
 static void
-cb_undef (cpp_reader * ARG_UNUSED (pfile), location_t loc,
-	  cpp_hashnode *node)
+cb_undef (cpp_reader *pfile, location_t loc, cpp_hashnode *node)
 {
+  if (lang_hooks.preprocess_undef)
+    lang_hooks.preprocess_undef (pfile, loc, node);
+
   const struct line_map *map = linemap_lookup (line_table, loc);
   (*debug_hooks->undef) (SOURCE_LINE (linemap_check_ordinary (map), loc),
 			 (const char *) NODE_NAME (node));
diff --git c/gcc/c-family/c-opts.c w/gcc/c-family/c-opts.c
index 120f4489f6c..c8f08d9e014 100644
--- c/gcc/c-family/c-opts.c
+++ w/gcc/c-family/c-opts.c
@@ -1105,9 +1116,11 @@ c_common_post_options (const char **pfilename)
       input_location = UNKNOWN_LOCATION;
     }
 
-  struct cpp_callbacks *cb = cpp_get_callbacks (parse_in);
+  struct cpp_callbacks *cb = cpp_get_callbacks (parse_in);
   cb->file_change = cb_file_change;
   cb->dir_change = cb_dir_change;
+  if (lang_hooks.preprocess_options)
+    lang_hooks.preprocess_options (parse_in);
   cpp_post_options (parse_in);
   init_global_opts_from_cpp (&global_options, cpp_get_options (parse_in));
 
@@ -1169,6 +1182,11 @@ c_common_init (void)
   /* Has to wait until now so that cpplib has its hash table.  */
   init_pragma ();
 
+  struct cpp_callbacks *cb = cpp_get_callbacks (parse_in);
+  cb->user_deferred_macro = lang_hooks.preprocess_deferred_macro;
+  if (!cb->undef)
+    cb->undef = lang_hooks.preprocess_undef;
+
   if (flag_preprocess_only)
     {
       c_finish_options ();
@@ -1550,7 +1568,13 @@ push_command_line_include (void)
       cpp_opts->warn_unused_macros = cpp_warn_unused_macros;
       /* Restore the line map back to the main file.  */
       if (!cpp_opts->preprocessed)
-	cpp_change_file (parse_in, LC_RENAME, this_input_filename);
+	{
+	  cpp_change_file (parse_in, LC_RENAME, this_input_filename);
+	  if (lang_hooks.preprocess_main_file)
+	    /* We're starting the main file.  Inform the FE of that.  */
+	    lang_hooks.preprocess_main_file
+	      (parse_in, line_table, LINEMAPS_LAST_ORDINARY_MAP (line_table));
+	}
 
       /* Set this here so the client can change the option if it wishes,
 	 and after stacking the main file so we don't trace the main file.  */
@@ -1560,14 +1584,19 @@ push_command_line_include (void)
 
 /* File change callback.  Has to handle -include files.  */
 static void
-cb_file_change (cpp_reader * ARG_UNUSED (pfile),
-		const line_map_ordinary *new_map)
+cb_file_change (cpp_reader *reader, const line_map_ordinary *new_map)
 {
   if (flag_preprocess_only)
     pp_file_change (new_map);
   else
     fe_file_change (new_map);
 
+  if (new_map && cpp_opts->preprocessed
+      && lang_hooks.preprocess_main_file && MAIN_FILE_P (new_map)
+      && ORDINARY_MAP_STARTING_LINE_NUMBER (new_map))
+    /* We're starting the main file.  Inform the FE of that.  */
+    lang_hooks.preprocess_main_file (reader, line_table, new_map);
+
   if (new_map 
       && (new_map->reason == LC_ENTER || new_map->reason == LC_RENAME))
     {
diff --git c/gcc/c-family/c-ppoutput.c w/gcc/c-family/c-ppoutput.c
index 44c6f30e06b..e3e0e59fcc7 100644
--- c/gcc/c-family/c-ppoutput.c
+++ w/gcc/c-family/c-ppoutput.c
@@ -21,6 +21,7 @@
 #include "coretypes.h"
 #include "c-common.h"		/* For flags.  */
 #include "../libcpp/internal.h"
+#include "langhooks.h"
 #include "c-pragma.h"		/* For parse_in.  */
 #include "file-prefix-map.h"    /* remap_macro_filename()  */
 
@@ -301,125 +302,48 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
 
 /* Writes out the preprocessed file, handling spacing and paste
    avoidance issues.  */
+
 static void
 scan_translation_unit (cpp_reader *pfile)
 {
-  bool avoid_paste = false;
-  bool do_line_adjustments
-    = cpp_get_options (parse_in)->lang != CLK_ASM
-      && !flag_no_line_commands;
-  bool in_pragma = false;
-  bool line_marker_emitted = false;
+  token_streamer streamer (pfile);
+  uintptr_t filter = 0;
+
+  if (lang_hooks.preprocess_token)
+    filter = lang_hooks.preprocess_token (pfile, NULL, filter);
 
   print.source = NULL;
   for (;;)
     {
-      location_t loc;
-      const cpp_token *token = cpp_get_token_with_location (pfile, &loc);
+      location_t spelling_loc;
+      const cpp_token *token
+	= cpp_get_token_with_location (pfile, &spelling_loc);
 
-      if (token->type == CPP_PADDING)
+      streamer.stream (pfile, token, spelling_loc);
+      if (filter)
 	{
-	  avoid_paste = true;
-	  if (print.source == NULL
-	      || (!(print.source->flags & PREV_WHITE)
-		  && token->val.source == NULL))
-	    print.source = token->val.source;
-	  continue;
+	  unsigned flags = lang_hooks.preprocess_token (pfile, token, filter);
+	  if (flags & lang_hooks::PT_begin_pragma)
+	    streamer.begin_pragma ();
 	}
-
       if (token->type == CPP_EOF)
 	break;
+    }
 
-      /* Subtle logic to output a space if and only if necessary.  */
-      if (avoid_paste)
-	{
-	  int src_line = LOCATION_LINE (loc);
-
-	  if (print.source == NULL)
-	    print.source = token;
-
-	  if (src_line != print.src_line
-	      && do_line_adjustments
-	      && !in_pragma)
-	    {
-	      line_marker_emitted = do_line_change (pfile, token, loc, false);
-	      putc (' ', print.outf);
-	      print.printed = true;
-	    }
-	  else if (print.source->flags & PREV_WHITE
-		   || (print.prev
-		       && cpp_avoid_paste (pfile, print.prev, token))
-		   || (print.prev == NULL && token->type == CPP_HASH))
-	    {
-	      putc (' ', print.outf);
-	      print.printed = true;
-	    }
-	}
-      else if (token->flags & PREV_WHITE)
-	{
-	  int src_line = LOCATION_LINE (loc);
-
-	  if (src_line != print.src_line
-	      && do_line_adjustments
-	      && !in_pragma)
-	    line_marker_emitted = do_line_change (pfile, token, loc, false);
-	  putc (' ', print.outf);
-	  print.printed = true;
-	}
+  if (filter)
+    lang_hooks.preprocess_token (pfile, NULL, filter);
+}
 
-      avoid_paste = false;
-      print.source = NULL;
-      print.prev = token;
-      if (token->type == CPP_PRAGMA)
-	{
-	  const char *space;
-	  const char *name;
-
-	  line_marker_emitted = maybe_print_line (token->src_loc);
-	  fputs ("#pragma ", print.outf);
-	  c_pp_lookup_pragma (token->val.pragma, &space, &name);
-	  if (space)
-	    fprintf (print.outf, "%s %s", space, name);
-	  else
-	    fprintf (print.outf, "%s", name);
-	  print.printed = true;
-	  in_pragma = true;
-	}
-      else if (token->type == CPP_PRAGMA_EOL)
-	{
-	  maybe_print_line (token->src_loc);
-	  in_pragma = false;
-	}
-      else
-	{
-	  if (cpp_get_options (parse_in)->debug)
-	    linemap_dump_location (line_table, token->src_loc, print.outf);
-
-	  if (do_line_adjustments
-	      && !in_pragma
-	      && !line_marker_emitted
-	      && print.prev_was_system_token != !!in_system_header_at (loc)
-	      && !is_location_from_builtin_token (loc))
-	    /* The system-ness of this token is different from the one
-	       of the previous token.  Let's emit a line change to
-	       mark the new system-ness before we emit the token.  */
-	    {
-	      do_line_change (pfile, token, loc, false);
-	      print.prev_was_system_token = !!in_system_header_at (loc);
-	    }
-	  cpp_output_token (token, print.outf);
-	  line_marker_emitted = false;
-	  print.printed = true;
-	}
+class do_streamer : public token_streamer
+{
+ public:
+  uintptr_t filter;
 
-      /* CPP_COMMENT tokens and raw-string literal tokens can
-	 have embedded new-line characters.  Rather than enumerating
-	 all the possible token types just check if token uses
-	 val.str union member.  */
-      if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR)
-	account_for_newlines (token->val.str.text, token->val.str.len);
+  do_streamer (cpp_reader *pfile, uintptr_t filter)
+    :token_streamer (pfile), filter (filter)
+    {
     }
-}
+};
 
 static void
 directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
@@ -427,7 +351,7 @@ directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
   va_list args;
   va_start (args, data_);
 
-  token_streamer *streamer = reinterpret_cast <token_streamer *> (data_);
+  do_streamer *streamer = reinterpret_cast <do_streamer *> (data_);
   switch (task)
     {
     default:
@@ -452,6 +376,13 @@ directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
 	const cpp_token *token = va_arg (args, const cpp_token *);
 	location_t spelling_loc = va_arg (args, location_t);
 	streamer->stream (pfile, token, spelling_loc);
+	if (streamer->filter)
+	  {
+	    unsigned flags = lang_hooks.preprocess_token
+	      (pfile, token, streamer->filter);
+	    if (flags & lang_hooks::PT_begin_pragma)
+	      streamer->begin_pragma ();
+	  }
       }
       break;
     }
@@ -464,8 +395,13 @@ directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
 static void
 scan_translation_unit_directives_only (cpp_reader *pfile)
 {
-  token_streamer streamer (pfile);
+  uintptr_t filter = 0;
+  if (lang_hooks.preprocess_token)
+    filter = lang_hooks.preprocess_token (pfile, NULL, filter);
+  do_streamer streamer (pfile, filter);
   cpp_directive_only_process (pfile, &streamer, directives_only_cb);
+  if (streamer.filter)
+    lang_hooks.preprocess_token (pfile, NULL, streamer.filter);
 }
 
 /* Adjust print.src_line for newlines embedded in output.  */
@@ -564,15 +500,16 @@ print_line_1 (location_t src_loc, const char *special_flags, FILE *stream)
       unsigned char *to_file_quoted =
          (unsigned char *) alloca (to_file_len * 4 + 1);
 
-      print.src_line = LOCATION_LINE (src_loc);
-      print.src_file = file_path;
-
       /* cpp_quote_string does not nul-terminate, so we have to do it
 	 ourselves.  */
       unsigned char *p = cpp_quote_string (to_file_quoted,
 					   (const unsigned char *) file_path,
 					   to_file_len);
       *p = '\0';
+
+      print.src_line = LOCATION_LINE (src_loc);
+      print.src_file = file_path;
+
       fprintf (stream, "# %u \"%s\"%s",
 	       print.src_line, to_file_quoted, special_flags);
 
@@ -678,9 +615,10 @@ cb_define (cpp_reader *pfile, location_t line, cpp_hashnode *node)
 }
 
 static void
-cb_undef (cpp_reader *pfile ATTRIBUTE_UNUSED, location_t line,
-	  cpp_hashnode *node)
+cb_undef (cpp_reader *pfile, location_t line, cpp_hashnode *node)
 {
+  if (lang_hooks.preprocess_undef)
+    lang_hooks.preprocess_undef (pfile, line, node);
   maybe_print_line (line);
   fprintf (print.outf, "#undef %s\n", NODE_NAME (node));
   print.src_line++;
diff --git c/gcc/langhooks-def.h w/gcc/langhooks-def.h
index a909915d018..1285d548050 100644
--- c/gcc/langhooks-def.h
+++ w/gcc/langhooks-def.h
@@ -103,6 +103,11 @@ extern void lhd_finalize_early_debug (void);
 #define LANG_HOOKS_INIT_OPTIONS_STRUCT	hook_void_gcc_optionsp
 #define LANG_HOOKS_INIT_OPTIONS		lhd_init_options
 #define LANG_HOOKS_INITIALIZE_DIAGNOSTICS lhd_initialize_diagnostics
+#define LANG_HOOKS_PREPROCESS_MAIN_FILE NULL
+#define LANG_HOOKS_PREPROCESS_OPTIONS NULL
+#define LANG_HOOKS_PREPROCESS_UNDEF NULL
+#define LANG_HOOKS_PREPROCESS_DEFERRED_MACRO NULL
+#define LANG_HOOKS_PREPROCESS_TOKEN NULL
 #define LANG_HOOKS_REGISTER_DUMPS	lhd_register_dumps
 #define LANG_HOOKS_COMPLAIN_WRONG_LANG_P lhd_complain_wrong_lang_p
 #define LANG_HOOKS_HANDLE_OPTION	lhd_handle_option
@@ -317,6 +322,11 @@ extern void lhd_end_section (void);
   LANG_HOOKS_INIT_OPTIONS_STRUCT, \
   LANG_HOOKS_INIT_OPTIONS, \
   LANG_HOOKS_INITIALIZE_DIAGNOSTICS, \
+  LANG_HOOKS_PREPROCESS_MAIN_FILE, \
+  LANG_HOOKS_PREPROCESS_OPTIONS, \
+  LANG_HOOKS_PREPROCESS_UNDEF, \
+  LANG_HOOKS_PREPROCESS_DEFERRED_MACRO, \
+  LANG_HOOKS_PREPROCESS_TOKEN, \
   LANG_HOOKS_REGISTER_DUMPS, \
   LANG_HOOKS_COMPLAIN_WRONG_LANG_P, \
   LANG_HOOKS_HANDLE_OPTION, \
diff --git c/gcc/langhooks.h w/gcc/langhooks.h
index a35cf21b673..c70c2317c32 100644
--- c/gcc/langhooks.h
+++ w/gcc/langhooks.h
@@ -356,6 +356,29 @@ struct lang_hooks
      global diagnostic context structure.  */
   void (*initialize_diagnostics) (diagnostic_context *);
 
+  /* Beginning the main source file.  */
+  void (*preprocess_main_file) (cpp_reader *, line_maps *,
+				const line_map_ordinary *);
+
+  /* Adjust libcpp options and callbacks.  */
+  void (*preprocess_options) (cpp_reader *);
+
+  /* Undefining a macro.  */
+  void (*preprocess_undef) (cpp_reader *, location_t, cpp_hashnode *);
+
+  /* Define a deferred macro.  */
+  struct cpp_macro *(*preprocess_deferred_macro) (cpp_reader *, location_t,
+						  cpp_hashnode *);
+
+  /* Observer for preprocessing stream.  */
+  uintptr_t (*preprocess_token) (cpp_reader *, const cpp_token *, uintptr_t);
+  /* Various flags it can return about the token.  */
+  enum PT_flags
+    {
+     PT_begin_pragma = 1 << 0
+    };
+  
+
   /* Register language-specific dumps.  */
   void (*register_dumps) (gcc::dump_manager *);
 


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

* [02/32] linemaps
       [not found] ` <c14417b3-4286-d877-f6e2-925bfc754684@acm.org>
  2020-11-03 19:39   ` [02/33] linemaps Nathan Sidwell
@ 2020-11-03 21:13   ` Nathan Sidwell
  2020-11-06 20:10     ` Jeff Law
       [not found]   ` <0bdf00a8-d8ad-9e97-134d-6668c0e8c86b@acm.org>
  2 siblings, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:13 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Location handling needs to add 2 things

1) a new kind of inclusion -- namely a module.  We add LC_MODULE as a 
map kind,

2) the ability to allocate blocks of line-maps for both ordinary 
locations and macro locations, that are then filled in by the module loader.

-- 
Nathan Sidwell


[-- Attachment #2: 02-cpp-line-maps.diff --]
[-- Type: text/x-patch, Size: 10442 bytes --]

diff --git c/libcpp/include/line-map.h w/libcpp/include/line-map.h
index 44008be5c08..50b2e4ff91a 100644
--- c/libcpp/include/line-map.h
+++ w/libcpp/include/line-map.h
@@ -72,6 +72,7 @@ enum lc_reason
   LC_RENAME,		/* Other reason for name change.  */
   LC_RENAME_VERBATIM,	/* Likewise, but "" != stdin.  */
   LC_ENTER_MACRO,	/* Begin macro expansion.  */
+  LC_MODULE,		/* A (C++) Module.  */
   /* FIXME: add support for stringize and paste.  */
   LC_HWM /* High Water Mark.  */
 };
@@ -439,7 +440,8 @@ struct GTY((tag ("1"))) line_map_ordinary : public line_map {
 
   /* Location from whence this line map was included.  For regular
      #includes, this location will be the last location of a map.  For
-     outermost file, this is 0.  */
+     outermost file, this is 0.  For modules it could be anywhere
+     within a map.  */
   location_t included_from;
 
   /* Size is 20 or 24 bytes, no padding  */
@@ -662,6 +664,15 @@ ORDINARY_MAP_IN_SYSTEM_HEADER_P (const line_map_ordinary *ord_map)
   return ord_map->sysp;
 }
 
+/* TRUE if this line map is for a module (not a source file).  */
+
+inline bool
+MAP_MODULE_P (const line_map *map)
+{
+  return (MAP_ORDINARY_P (map)
+	  && linemap_check_ordinary (map)->reason == LC_MODULE);
+}
+
 /* Get the filename of ordinary map MAP.  */
 
 inline const char *
@@ -1076,6 +1087,9 @@ extern void linemap_check_files_exited (class line_maps *);
 extern location_t linemap_line_start
 (class line_maps *set, linenum_type to_line,  unsigned int max_column_hint);
 
+/* Allocate a raw block of line maps, zero initialized.  */
+extern line_map *line_map_new_raw (line_maps *, bool, unsigned);
+
 /* Add a mapping of logical source line to physical source file and
    line number. This function creates an "ordinary map", which is a
    map that records locations of tokens that are not part of macro
@@ -1093,6 +1107,39 @@ extern const line_map *linemap_add
   (class line_maps *, enum lc_reason, unsigned int sysp,
    const char *to_file, linenum_type to_line);
 
+/* Create a macro map.  A macro map encodes source locations of tokens
+   that are part of a macro replacement-list, at a macro expansion
+   point. See the extensive comments of struct line_map and struct
+   line_map_macro, in line-map.h.
+
+   This map shall be created when the macro is expanded. The map
+   encodes the source location of the expansion point of the macro as
+   well as the "original" source location of each token that is part
+   of the macro replacement-list. If a macro is defined but never
+   expanded, it has no macro map.  SET is the set of maps the macro
+   map should be part of.  MACRO_NODE is the macro which the new macro
+   map should encode source locations for.  EXPANSION is the location
+   of the expansion point of MACRO. For function-like macros
+   invocations, it's best to make it point to the closing parenthesis
+   of the macro, rather than the the location of the first character
+   of the macro.  NUM_TOKENS is the number of tokens that are part of
+   the replacement-list of MACRO.  */
+const line_map_macro *linemap_enter_macro (line_maps *, cpp_hashnode *,
+					   location_t, unsigned int);
+
+/* Create a source location for a module.  The creator must either do
+   this after the TU is tokenized, or deal with saving and restoring
+   map state.  */
+
+extern location_t linemap_module_loc
+  (line_maps *, location_t from, const char *name);
+extern void linemap_module_reparent
+  (line_maps *, location_t loc, location_t new_parent);
+
+/* Restore the linemap state such that the map at LWM-1 continues.  */
+extern void linemap_module_restore
+  (line_maps *, unsigned lwm);
+
 /* Given a logical source location, returns the map which the
    corresponding (source file, line, column) triplet can be deduced
    from. Since the set is built chronologically, the logical lines are
@@ -1102,6 +1149,8 @@ extern const line_map *linemap_add
 extern const line_map *linemap_lookup
   (const line_maps *, location_t);
 
+unsigned linemap_lookup_macro_index (const line_maps *, location_t);
+
 /* Returns TRUE if the line table set tracks token locations across
    macro expansion, FALSE otherwise.  */
 bool linemap_tracks_macro_expansion_locs_p (class line_maps *);
diff --git c/libcpp/line-map.c w/libcpp/line-map.c
index 5a74174579f..4b2b7ef4394 100644
--- c/libcpp/line-map.c
+++ w/libcpp/line-map.c
@@ -378,23 +378,21 @@ linemap_check_files_exited (line_maps *set)
 	     ORDINARY_MAP_FILE_NAME (map));
 }
 
-/* Create a new line map in the line map set SET, and return it.
-   REASON is the reason of creating the map. It determines the type
-   of map created (ordinary or macro map). Note that ordinary maps and
-   macro maps are allocated in different memory location.  */
+/* Create NUM zero-initialized maps of type MACRO_P.  */
 
-static struct line_map *
-new_linemap (line_maps *set,  location_t start_location)
+line_map *
+line_map_new_raw (line_maps *set, bool macro_p, unsigned num)
 {
-  bool macro_p = start_location >= LINE_MAP_MAX_LOCATION;
   unsigned num_maps_allocated = LINEMAPS_ALLOCATED (set, macro_p);
   unsigned num_maps_used = LINEMAPS_USED (set, macro_p);
-
-  if (num_maps_used == num_maps_allocated)
+  
+  if (num > num_maps_allocated - num_maps_used)
     {
       /* We need more space!  */
       if (!num_maps_allocated)
 	num_maps_allocated = 128;
+      if (num_maps_allocated < num_maps_used + num)
+	num_maps_allocated = num_maps_used + num;
       num_maps_allocated *= 2;
 
       size_t size_of_a_map;
@@ -429,6 +427,7 @@ new_linemap (line_maps *set,  location_t start_location)
 	      (num_maps - num_maps_used) * size_of_a_map);
       if (macro_p)
 	set->info_macro.maps = (line_map_macro *)buffer;
+
       else
 	set->info_ordinary.maps = (line_map_ordinary *)buffer;
       LINEMAPS_ALLOCATED (set, macro_p) = num_maps;
@@ -436,13 +435,39 @@ new_linemap (line_maps *set,  location_t start_location)
 
   line_map *result = (macro_p ? (line_map *)&set->info_macro.maps[num_maps_used]
 		      : (line_map *)&set->info_ordinary.maps[num_maps_used]);
-  LINEMAPS_USED (set, macro_p)++;
+  LINEMAPS_USED (set, macro_p) += num;
+
+  return result;
+}
+
+/* Create a new line map in the line map set SET, and return it.
+   REASON is the reason of creating the map. It determines the type
+   of map created (ordinary or macro map). Note that ordinary maps and
+   macro maps are allocated in different memory location.  */
+
+static struct line_map *
+new_linemap (line_maps *set, location_t start_location)
+{
+  line_map *result = line_map_new_raw (set,
+				       start_location >= LINE_MAP_MAX_LOCATION,
+				       1);
 
   result->start_location = start_location;
 
   return result;
 }
 
+/* Return the location of the last source line within an ordinary
+   map.  */
+inline location_t
+LAST_SOURCE_LINE_LOCATION (const line_map_ordinary *map)
+{
+  return (((map[1].start_location - 1
+	    - map->start_location)
+	   & ~((1 << map->m_column_and_range_bits) - 1))
+	  + map->start_location);
+}
+
 /* Add a mapping of logical source line to physical source file and
    line number.
 
@@ -570,6 +595,51 @@ linemap_add (line_maps *set, enum lc_reason reason,
   return map;
 }
 
+/* Create a location for a module NAME imported at FROM.  */
+
+location_t
+linemap_module_loc (line_maps *set, location_t from, const char *name)
+{
+  const line_map_ordinary *map
+    = linemap_check_ordinary (linemap_add (set, LC_MODULE, false, name, 0));
+  const_cast <line_map_ordinary *> (map)->included_from = from;
+
+  location_t loc = linemap_line_start (set, 0, 0);
+
+  return loc;
+}
+
+void
+linemap_module_reparent (line_maps *set, location_t loc, location_t adoptor)
+{
+  const line_map_ordinary *map = linemap_ordinary_map_lookup (set, loc);
+  const_cast<line_map_ordinary *> (map)->included_from = adoptor;
+}
+
+/* A linemap at LWM-1 was interrupted to insert module locations & imports.
+   Append a new map, continuing the interrupted one.  */
+
+void
+linemap_module_restore (line_maps *set, unsigned lwm)
+{
+  if (lwm && lwm != LINEMAPS_USED (set, false))
+    {
+      const line_map_ordinary *pre_map
+	= linemap_check_ordinary (LINEMAPS_MAP_AT (set, false, lwm - 1));
+      unsigned src_line = SOURCE_LINE (pre_map,
+				       LAST_SOURCE_LINE_LOCATION (pre_map));
+      location_t inc_at = pre_map->included_from;
+      if (const line_map_ordinary *post_map
+	  = (linemap_check_ordinary
+	     (linemap_add (set, LC_RENAME_VERBATIM,
+			   ORDINARY_MAP_IN_SYSTEM_HEADER_P (pre_map),
+			   ORDINARY_MAP_FILE_NAME (pre_map), src_line))))
+	/* linemap_add will think we were included from the same as
+	   the preceeding map.  */
+	const_cast <line_map_ordinary *> (post_map)->included_from = inc_at;
+    }
+}
+
 /* Returns TRUE if the line table set tracks token locations across
    macro expansion, FALSE otherwise.  */
 
@@ -1003,14 +1073,25 @@ linemap_macro_map_lookup (const line_maps *set, location_t line)
   if (set == NULL)
     return NULL;
 
+  unsigned ix = linemap_lookup_macro_index (set, line);
+  const struct line_map_macro *result = LINEMAPS_MACRO_MAP_AT (set, ix);
+  linemap_assert (MAP_START_LOCATION (result) <= line);
+
+  return result;
+}
+
+unsigned
+linemap_lookup_macro_index (const line_maps *set, location_t line)
+{
   unsigned mn = LINEMAPS_MACRO_CACHE (set);
   unsigned mx = LINEMAPS_MACRO_USED (set);
   const struct line_map_macro *cached = LINEMAPS_MACRO_MAP_AT (set, mn);
 
   if (line >= MAP_START_LOCATION (cached))
     {
-      if (mn == 0 || line < MAP_START_LOCATION (&cached[-1]))
-	return cached;
+      if (line < (MAP_START_LOCATION (cached)
+		  + MACRO_MAP_NUM_MACRO_TOKENS (cached)))
+	return mn;
       mx = mn - 1;
       mn = 0;
     }
@@ -1025,10 +1106,7 @@ linemap_macro_map_lookup (const line_maps *set, location_t line)
     }
 
   LINEMAPS_MACRO_CACHE (set) = mx;
-  const struct line_map_macro *result = LINEMAPS_MACRO_MAP_AT (set, mx);
-  linemap_assert (MAP_START_LOCATION (result) <= line);
-
-  return result;
+  return mx;
 }
 
 /* Return TRUE if MAP encodes locations coming from a macro
@@ -1747,7 +1825,7 @@ linemap_dump (FILE *stream, class line_maps *set, unsigned ix, bool is_macro)
 {
   const char *const lc_reasons_v[LC_HWM]
       = { "LC_ENTER", "LC_LEAVE", "LC_RENAME", "LC_RENAME_VERBATIM",
-	  "LC_ENTER_MACRO" };
+	  "LC_ENTER_MACRO", "LC_MODULE" };
   const line_map *map;
   unsigned reason;
 


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

* [03/32] cpp-callbacks
       [not found]   ` <0bdf00a8-d8ad-9e97-134d-6668c0e8c86b@acm.org>
@ 2020-11-03 21:13     ` Nathan Sidwell
  2020-11-06 20:10       ` Jeff Law
       [not found]     ` <c55d12d9-e966-5b36-1538-98c9c4b418c8@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:13 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here are the callbacks in the preprocessor itself.

a) one to handle deferred macros

b) one to handle include translation.  For every '#include <header>', 
there is the possibility of replacing that with 'import <header>'.  This 
hook determines if that happens.


-- 
Nathan Sidwell


[-- Attachment #2: 03-cpp-callbacks.diff --]
[-- Type: text/x-patch, Size: 1020 bytes --]

diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -680,6 +695,9 @@ struct cpp_callbacks
   /* Callback that can change a user lazy into normal macro.  */
   void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned);
 
+  /* Callback to handle deferred cpp_macros.  */
+  cpp_macro *(*user_deferred_macro) (cpp_reader *, location_t, cpp_hashnode *);
+
   /* Callback to parse SOURCE_DATE_EPOCH from environment.  */
   time_t (*get_source_date_epoch) (cpp_reader *);
 
@@ -698,6 +716,11 @@ struct cpp_callbacks
   /* Callback for filename remapping in __FILE__ and __BASE_FILE__ macro
      expansions.  */
   const char *(*remap_filename) (const char*);
+
+  /* Maybe translate a #include into something else.  Return a
+     cpp_buffer containing the translation if translating.  */
+  char *(*translate_include) (cpp_reader *, line_maps *, location_t,
+			      const char *path);
 };
 
 #ifdef VMS


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

* [04/32] cpp lexer
       [not found]     ` <c55d12d9-e966-5b36-1538-98c9c4b418c8@acm.org>
@ 2020-11-03 21:13       ` Nathan Sidwell
  2020-11-03 23:08         ` Joseph Myers
  2020-11-06 20:23         ` Jeff Law
       [not found]       ` <85996243-86cf-88b4-5b3b-451eaf3a0df6@acm.org>
  1 sibling, 2 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:13 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

c++ modules creates 2 new kinds of preprocessor lines
[export] module ...
[export] import ...

To all intents and purposes these are cppdirectives spelt without a 
leading '#'.  module and import are context-sensitive keywords.  Thus 
preprocessor tokenizing needs a bit of token peeking.  This is that peeking.

We have a new node flag 'NODE_MODULE', which marks whether an identifier 
is significant to this peeking.  When we see such an identifier at the 
beginning of a logical line, we need to peek further and figure out 
whether these are those keywords.

When successfully peeked, we replace the identifiers with internal-only 
tokens that the c++ parser recognizes.

-- 
Nathan Sidwell


[-- Attachment #2: 04-cpp-lexer.diff --]
[-- Type: text/x-patch, Size: 19243 bytes --]

diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -487,6 +494,9 @@ struct cpp_options
   /* Nonzero for the '::' token.  */
   unsigned char scope;
 
+  /* Nonzero means tokenize C++20 module directives.  */
+  unsigned char module_directives;
+
   /* Holds the name of the target (execution) character set.  */
   const char *narrow_charset;
 
@@ -831,6 +857,7 @@ struct GTY(()) cpp_macro {
 #define NODE_USED	(1 << 5)	/* Dumped with -dU.  */
 #define NODE_CONDITIONAL (1 << 6)	/* Conditional macro */
 #define NODE_WARN_OPERATOR (1 << 7)	/* Warn about C++ named operator.  */
+#define NODE_MODULE (1 << 8)		/* C++-20 module-related name.  */
 
 /* Different flavors of hash node.  */
 enum node_type
@@ -888,9 +915,9 @@ struct GTY(()) cpp_hashnode {
   unsigned int directive_index : 7;	/* If is_directive,
 					   then index into directive table.
 					   Otherwise, a NODE_OPERATOR.  */
-  unsigned char rid_code;		/* Rid code - for front ends.  */
+  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
+  unsigned int flags : 9;		/* CPP flags.  */
   ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
-  unsigned int flags : 8;		/* CPP flags.  */
 
   /* 6 bits spare (plus another 32 on 64-bit hosts).  */
 
diff --git c/libcpp/lex.c w/libcpp/lex.c
index fb222924c8c..b3498f195bf 100644
--- c/libcpp/lex.c
+++ w/libcpp/lex.c
@@ -2606,6 +2622,131 @@ _cpp_temp_token (cpp_reader *pfile)
   return result;
 }
 
+/* RESULT is a CPP_NAME with NODE_MODULE set.  See if we should enter
+   deferred_pragma mode to tokenize the rest of the line.  */
+
+static void
+cpp_maybe_module_directive (cpp_reader *pfile, cpp_token *result)
+{
+  unsigned backup = 0; /* Tokens we peeked.  */
+  cpp_hashnode *node = result->val.node.node;
+  cpp_token *peek = result;
+  cpp_token *keyword = peek;
+  cpp_hashnode *(&n_modules)[spec_nodes::M_HWM][2] = pfile->spec_nodes.n_modules;
+  int header_count = 0;
+
+  /* Enter directives mode for the peeking.  */
+  pfile->state.in_deferred_pragma = true;
+  pfile->state.pragma_allow_expansion = true;
+  pfile->state.save_comments = 0;
+  pfile->directive_line = result->src_loc;
+
+  if (node == n_modules[spec_nodes::M_EXPORT][0])
+    {
+      peek = _cpp_lex_direct (pfile);
+      keyword = peek;
+      backup++;
+      if (keyword->type != CPP_NAME)
+	goto not_module;
+      node = keyword->val.node.node;
+      if (!(node->flags & NODE_MODULE))
+	goto not_module;
+    }
+
+  if (__builtin_expect (node == n_modules[spec_nodes::M__IMPORT][0], false))
+    /* __import  */
+    header_count = backup + 2 + 16;
+  else if (__builtin_expect (node == n_modules[spec_nodes::M_IMPORT][0], false))
+    /* import  */
+    header_count = backup + 2 + (CPP_OPTION (pfile, preprocessed) ? 16 : 0);
+  else if (__builtin_expect (node == n_modules[spec_nodes::M_MODULE][0], false))
+    ; /* module  */
+  else
+    goto not_module;
+
+  /* We've seen [export] {module|import|__import}.  Check the next token.  */
+  if (header_count)
+    /* After '{,__}import' a header name may appear.  */
+    pfile->state.angled_headers = true;
+  peek = _cpp_lex_direct (pfile);
+  backup++;
+
+  /* ... import followed by identifier, ':', '<' or
+     header-name preprocessing tokens, or module
+     followed by cpp-identifier, ':' or ';' preprocessing
+     tokens.  C++ keywords are not yet relevant.  */
+  if (peek->type == CPP_NAME
+      || peek->type == CPP_COLON
+      ||  (header_count
+	   ? (peek->type == CPP_LESS
+	      || (peek->type == CPP_STRING && peek->val.str.text[0] != 'R')
+	      || peek->type == CPP_HEADER_NAME)
+	   : peek->type == CPP_SEMICOLON))
+    {
+      pfile->state.pragma_allow_expansion = !CPP_OPTION (pfile, preprocessed);
+      if (!pfile->state.pragma_allow_expansion)
+	pfile->state.prevent_expansion++;
+
+      if (!header_count && linemap_included_from
+	  (LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table)))
+	cpp_error_with_line (pfile, CPP_DL_ERROR, keyword->src_loc, 0,
+			     "module control-line cannot be in included file");
+
+      /* The first one or two tokens cannot be macro names.  */
+      for (int ix = backup; ix--;)
+	{
+	  cpp_token *tok = ix ? keyword : result;
+	  cpp_hashnode *node = tok->val.node.node;
+
+	  /* Don't attempt to expand the token.  */
+	  tok->flags |= NO_EXPAND;
+	  if (_cpp_defined_macro_p (node)
+	      && _cpp_maybe_notify_macro_use (pfile, node, tok->src_loc)
+	      && !cpp_fun_like_macro_p (node))
+	    cpp_error_with_line (pfile, CPP_DL_ERROR, tok->src_loc, 0, 
+				 "module control-line \"%s\" cannot be"
+				 " an object-like macro",
+				 NODE_NAME (node));
+	}
+
+      /* Map to underbar variants.  */
+      keyword->val.node.node = n_modules[header_count
+					 ? spec_nodes::M_IMPORT
+					 : spec_nodes::M_MODULE][1];
+      if (backup != 1)
+	result->val.node.node = n_modules[spec_nodes::M_EXPORT][1];
+
+      /* Maybe tell the tokenizer we expect a header-name down the
+	 road.  */
+      pfile->state.directive_file_token = header_count;
+    }
+  else
+    {
+    not_module:
+      /* Drop out of directive mode.  */
+      pfile->state.save_comments
+	= !CPP_OPTION (pfile, discard_comments);
+      pfile->state.in_deferred_pragma = false;
+      pfile->state.angled_headers = false;
+    }
+
+  /* In either case we want to backup the peeked tokens.  */
+  if (backup)
+    {
+      /* If we saw EOL, we should drop it, because this isn't a module
+	 control-line after all.  */
+      bool eol = peek->type == CPP_PRAGMA_EOL;
+      if (!eol || backup > 1)
+	{
+	  /* Put put the peeked tokens back  */
+	  _cpp_backup_tokens_direct (pfile, backup);
+	  /* But if the last one was an EOL, forget it.  */
+	  if (eol)
+	    pfile->lookaheads--;
+	}
+    }
+}
+
 /* Lex a token into RESULT (external interface).  Takes care of issues
    like directive handling, token lookahead, multiple include
    optimization and skipping.  */
@@ -2654,6 +2795,22 @@ _cpp_lex_token (cpp_reader *pfile)
 	    }
 	  else if (pfile->state.in_deferred_pragma)
 	    result = &pfile->directive_result;
+	  else if (result->type == CPP_NAME
+		   && __builtin_expect
+		   (result->val.node.node->flags & NODE_MODULE, 0)
+		   && !pfile->state.skipping
+		   /* Unlike regular directives, we do not deal with
+		      tokenizing module directives as macro arguments.
+		      That's not permitted.  */
+		   && !pfile->state.parsing_args)
+	    {
+	      /* P1857.  Before macro expansion, At start of logical
+		 line ... */
+	      /* We don't have to consider lookaheads at this point.  */
+	      gcc_checking_assert (!pfile->lookaheads);
+
+	      cpp_maybe_module_directive (pfile, result);
+	    }
 
 	  if (pfile->cb.line_change && !pfile->state.skipping)
 	    pfile->cb.line_change (pfile, result, pfile->state.parsing_args);
@@ -3446,7 +3609,11 @@ cpp_output_token (const cpp_token *token, FILE *fp)
       break;
 
     case SPELL_LITERAL:
+      if (token->type == CPP_HEADER_NAME)
+	fputc ('"', fp);
       fwrite (token->val.str.text, 1, token->val.str.len, fp);
+      if (token->type == CPP_HEADER_NAME)
+	fputc ('"', fp);
       break;
 
     case SPELL_NONE:
@@ -3932,6 +4099,188 @@ do_peek_prev (const unsigned char *peek, const unsigned char *bound)
     return peek;
 }
 
+/* If PEEK[-1] is identifier MATCH, scan past it and trailing white
+   space.  Otherwise return NULL.  */
+
+static const unsigned char *
+do_peek_ident (const char *match, const unsigned char *peek,
+	       const unsigned char *limit)
+{
+  for (; *++match; peek++)
+    if (*peek != *match)
+      {
+	peek = do_peek_next (peek, limit);
+	if (*peek != *match)
+	  return NULL;
+      }
+
+  /* Must now not be looking at an identifier char.  */
+  peek = do_peek_next (peek, limit);
+  if (ISIDNUM (*peek))
+    return NULL;
+
+  /* Skip control-line whitespace.  */
+ ws:
+  while (*peek == ' ' || *peek == '\t')
+    peek++;
+  if (__builtin_expect (*peek == '\\', false))
+    {
+      peek = do_peek_backslash (peek, limit);
+      if (*peek != '\\')
+	goto ws;
+    }
+
+  return peek;
+}
+
+/* Are we looking at a module control line starting as PEEK - 1?  */
+
+static bool
+do_peek_module (cpp_reader *pfile, unsigned char c,
+		const unsigned char *peek, const unsigned char *limit)
+{
+  bool import = false;
+
+  if (__builtin_expect (c == 'e', false))
+    {
+      if (!((peek[0] == 'x' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("export", peek, limit))))
+	return false;
+
+      /* export, peek for import or module.  No need to peek __import
+	 here.  */
+      if (peek[0] == 'i')
+	{
+	  if (!((peek[1] == 'm' || peek[1] == '\\')
+		&& (peek = do_peek_ident ("import", peek + 1, limit))))
+	    return false;
+	  import = true;
+	}
+      else if (peek[0] == 'm')
+	{
+	  if (!((peek[1] == 'o' || peek[1] == '\\')
+		&& (peek = do_peek_ident ("module", peek + 1, limit))))
+	    return false;
+	}
+      else
+	return false;
+    }
+  else if (__builtin_expect (c == 'i', false))
+    {
+      if (!((peek[0] == 'm' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("import", peek, limit))))
+	return false;
+      import = true;
+    }
+  else if (__builtin_expect (c == '_', false))
+    {
+      /* Needed for translated includes.   */
+      if (!((peek[0] == '_' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("__import", peek, limit))))
+	return false;
+      import = true;
+    }
+  else if (__builtin_expect (c == 'm', false))
+    {
+      if (!((peek[0] == 'o' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("module", peek, limit))))
+	return false;
+    }
+  else
+    return false;
+
+  /* Peek the next character to see if it's good enough.  We'll be at
+     the first non-whitespace char, including skipping an escaped
+     newline.  */
+  /* ... import followed by identifier, ':', '<' or header-name
+     preprocessing tokens, or module followed by identifier, ':' or
+     ';' preprocessing tokens.  */
+  unsigned char p = *peek++;
+      
+  /* A character literal is ... single quotes, ... optionally preceded
+     by u8, u, U, or L */
+  /* A string-literal is a ... double quotes, optionally prefixed by
+     R, u8, u8R, u, uR, U, UR, L, or LR */
+  if (p == 'u')
+    {
+      peek = do_peek_next (peek, limit);
+      if (*peek == '8')
+	{
+	  peek++;
+	  goto peek_u8;
+	}
+      goto peek_u;
+    }
+  else if (p == 'U' || p == 'L')
+    {
+    peek_u8:
+      peek = do_peek_next (peek, limit);
+    peek_u:
+      if (*peek == '\"' || *peek == '\'')
+	return false;
+
+      if (*peek == 'R')
+	goto peek_R;
+      /* Identifier. Ok.  */
+    }
+  else if (p == 'R')
+    {
+    peek_R:
+      if (CPP_OPTION (pfile, rliterals))
+	{
+	  peek = do_peek_next (peek, limit);
+	  if (*peek == '\"')
+	    return false;
+	}
+      /* Identifier. Ok.  */
+    }
+  else if ('Z' - 'A' == 25
+	   ? ((p >= 'A' && p <= 'Z') || (p >= 'a' && p <= 'z') || p == '_')
+	   : ISIDST (p))
+    {
+      /* Identifier.  Ok. */
+    }
+  else if (p == '<')
+    {
+      /* Maybe angle header, ok for import.  Reject
+	 '<=', '<<' digraph:'<:'.  */
+      if (!import)
+	return false;
+      peek = do_peek_next (peek, limit);
+      if (*peek == '=' || *peek == '<'
+	  || (*peek == ':' && CPP_OPTION (pfile, digraphs)))
+	return false;
+    }
+  else if (p == ';')
+    {
+      /* SEMICOLON, ok for module.  */
+      if (import)
+	return false;
+    }
+  else if (p == '"')
+    {
+      /* STRING, ok for import.  */
+      if (!import)
+	return false;
+    }
+  else if (p == ':')
+    {
+      /* Maybe COLON, ok.  Reject '::', digraph:':>'.  */
+      peek = do_peek_next (peek, limit);
+      if (*peek == ':' || (*peek == '>' && CPP_OPTION (pfile, digraphs)))
+	return false;
+    }
+  else
+    /* FIXME: Detect a unicode character, excluding those not
+       permitted as the initial character. [lex.name]/1.  I presume
+       we need to check the \[uU] spellings, and directly using
+       Unicode in say UTF8 form?  Or perhaps we do the phase-1
+       conversion of UTF8 to universal-character-names?  */
+    return false;
+
+  return true;
+}
+
 /* Directives-only scanning.  Somewhat more relaxed than correct
    parsing -- some ill-formed programs will not be rejected.  */
 
@@ -3940,6 +4289,8 @@ cpp_directive_only_process (cpp_reader *pfile,
 			    void *data,
 			    void (*cb) (cpp_reader *, CPP_DO_task, void *, ...))
 {
+  bool module_p = CPP_OPTION (pfile, module_directives);
+
   do
     {
     restart:
@@ -4332,6 +4683,51 @@ cpp_directive_only_process (cpp_reader *pfile,
 	      }
 	      goto dflt;
 
+	    case '_':
+	    case 'e':
+	    case 'i':
+	    case 'm':
+	      if (bol && module_p && !pfile->state.skipping
+		  && do_peek_module (pfile, c, pos, limit))
+		{
+		  /* We've seen the start of a module control line.
+		     Start up the tokenizer.  */
+		  pos--; /* Backup over the first character.  */
+
+		  /* Backup over whitespace to start of line.  */
+		  while (pos > line_start
+			 && (pos[-1] == ' ' || pos[-1] == '\t'))
+		    pos--;
+
+		  if (pos > base)
+		    cb (pfile, CPP_DO_print, data, line_count, base, pos - base);
+
+		  /* Prep things for directive handling. */
+		  buffer->next_line = pos;
+		  buffer->need_line = true;
+
+		  /* Now get tokens until the PRAGMA_EOL.  */
+		  do
+		    {
+		      location_t spelling;
+		      const cpp_token *tok
+			= cpp_get_token_with_location (pfile, &spelling);
+
+		      gcc_assert (pfile->state.in_deferred_pragma
+				  || tok->type == CPP_PRAGMA_EOL);
+		      cb (pfile, CPP_DO_token, data, tok, spelling);
+		    }
+		  while (pfile->state.in_deferred_pragma);
+
+		  if (pfile->buffer->next_line < pfile->buffer->rlimit)
+		    cb (pfile, CPP_DO_location, data,
+			pfile->line_table->highest_line);
+
+		  pfile->mi_valid = false;
+		  goto restart;
+		}
+	      goto dflt;
+
 	    default:
 	    dflt:
 	      bol = false;
diff --git c/libcpp/macro.c w/libcpp/macro.c
index e304f67c2e0..f5f280dfdc7 100644
--- c/libcpp/macro.c
+++ w/libcpp/macro.c
@@ -2930,6 +2932,85 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location)
     }
 
   pfile->about_to_expand_macro_p = saved_about_to_expand_macro;
+
+  if (pfile->state.directive_file_token
+      && !pfile->state.parsing_args
+      && !(result->type == CPP_PADDING || result->type == CPP_COMMENT)
+      && !(15 & --pfile->state.directive_file_token))
+    {
+      /* Do header-name frobbery.  Concatenate < ... > as approprate.
+	 Do header search if needed, and finally drop the outer <> or
+	 "".  */
+      pfile->state.angled_headers = false;
+
+      /* Do angle-header reconstitution.  Then do include searching.
+	 We'll always end up with a ""-quoted header-name in that
+	 case.  If searching finds nothing, we emit a diagnostic and
+	 an empty string.  */
+      size_t len = 0;
+      char *fname = NULL;
+
+      cpp_token *tmp = _cpp_temp_token (pfile);
+      *tmp = *result;
+
+      tmp->type = CPP_HEADER_NAME;
+      bool need_search = !pfile->state.directive_file_token;
+      pfile->state.directive_file_token = 0;
+
+      bool angle = result->type != CPP_STRING;
+      if (result->type == CPP_HEADER_NAME
+	  || (result->type == CPP_STRING && result->val.str.text[0] != 'R'))
+	{
+	  len = result->val.str.len - 2;
+	  fname = XNEWVEC (char, len + 1);
+	  memcpy (fname, result->val.str.text + 1, len);
+	  fname[len] = 0;
+	}
+      else if (result->type == CPP_LESS)
+	fname = _cpp_bracket_include (pfile);
+
+      if (fname)
+	{
+	  /* We have a header-name.  Look it up.  This will emit an
+	     unfound diagnostic.  Canonicalize the found name.  */
+	  const char *found = fname;
+
+	  if (need_search)
+	    {
+	      found = cpp_find_header_unit (pfile, fname, angle, tmp->src_loc);
+	      if (!found)
+		found = "";
+	      len = strlen (found);
+	    }
+	  /* Force a leading './' if it's not absolute.  */
+	  bool dotme = (found[0] == '.' ? !IS_DIR_SEPARATOR (found[1])
+			: found[0] && !IS_ABSOLUTE_PATH (found));
+
+	  if (BUFF_ROOM (pfile->u_buff) < len + 1 + dotme * 2)
+	    _cpp_extend_buff (pfile, &pfile->u_buff, len + 1 + dotme * 2);
+	  unsigned char *buf = BUFF_FRONT (pfile->u_buff);
+	  size_t pos = 0;
+	      
+	  if (dotme)
+	    {
+	      buf[pos++] = '.';
+	      /* Apparently '/' is unconditional.  */
+	      buf[pos++] = '/';
+	    }
+	  memcpy (&buf[pos], found, len);
+	  pos += len;
+	  buf[pos] = 0;
+
+	  tmp->val.str.len = pos;
+	  tmp->val.str.text = buf;
+
+	  tmp->type = CPP_HEADER_NAME;
+	  XDELETEVEC (fname);
+	  
+	  result = tmp;
+	}
+    }
+
   return result;
 }
 
diff --git c/gcc/c-family/c-lex.c w/gcc/c-family/c-lex.c
index e81e16ddc26..44575473719 100644
--- c/gcc/c-family/c-lex.c
+++ w/gcc/c-family/c-lex.c
@@ -654,8 +656,11 @@ c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags,
       *value = build_int_cst (integer_type_node, tok->val.pragma);
       break;
 
-      /* These tokens should not be visible outside cpplib.  */
     case CPP_HEADER_NAME:
+      *value = build_string (tok->val.str.len, (const char *)tok->val.str.text);
+      break;
+
+      /* These tokens should not be visible outside cpplib.  */
     case CPP_MACRO_ARG:
       gcc_unreachable ();
 
diff --git c/libcpp/init.c w/libcpp/init.c
index 6c52f50de39..96ade569457 100644
--- c/libcpp/init.c
+++ w/libcpp/init.c
@@ -840,4 +855,27 @@ post_options (cpp_reader *pfile)
       CPP_OPTION (pfile, trigraphs) = 0;
       CPP_OPTION (pfile, warn_trigraphs) = 0;
     }
+
+  if (CPP_OPTION (pfile, module_directives))
+    {
+      /* These unspellable tokens have a leading space.  */
+      const char *const inits[spec_nodes::M_HWM]
+	= {"export ", "module ", "import ", "__import"};
+
+      for (int ix = 0; ix != spec_nodes::M_HWM; ix++)
+	{
+	  cpp_hashnode *node = cpp_lookup (pfile, UC (inits[ix]),
+					   strlen (inits[ix]));
+
+	  /* Token we pass to the compiler.  */
+	  pfile->spec_nodes.n_modules[ix][1] = node;
+
+	  if (ix != spec_nodes::M__IMPORT)
+	    /* Token we recognize when lexing, drop the trailing ' '.  */
+	    node = cpp_lookup (pfile, NODE_NAME (node), NODE_LEN (node) - 1);
+
+	  node->flags |= NODE_MODULE;
+	  pfile->spec_nodes.n_modules[ix][0] = node;
+	}
+    }
 }
diff --git c/libcpp/internal.h w/libcpp/internal.h
index 4759961a33a..17b65601b66 100644
--- c/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -280,6 +280,9 @@ struct lexer_state
   /* Nonzero when tokenizing a deferred pragma.  */
   unsigned char in_deferred_pragma;
 
+  /* Count to token that is a header-name.  */
+  unsigned char directive_file_token;
+
   /* Nonzero if the deferred pragma being handled allows macro expansion.  */
   unsigned char pragma_allow_expansion;
 };
@@ -292,6 +295,12 @@ struct spec_nodes
   cpp_hashnode *n_false;		/* C++ keyword false */
   cpp_hashnode *n__VA_ARGS__;		/* C99 vararg macros */
   cpp_hashnode *n__VA_OPT__;		/* C++ vararg macros */
+
+  enum {M_EXPORT, M_MODULE, M_IMPORT, M__IMPORT, M_HWM};
+  
+  /* C++20 modules, only set when module_directives is in effect.
+     incoming variants [0], outgoing ones [1] */
+  cpp_hashnode *n_modules[M_HWM][2];
 };
 
 typedef struct _cpp_line_note _cpp_line_note;


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

* [05/32] cpp files
       [not found]       ` <85996243-86cf-88b4-5b3b-451eaf3a0df6@acm.org>
@ 2020-11-03 21:14         ` Nathan Sidwell
  2020-11-17  1:27           ` Jeff Law
       [not found]         ` <ca22e13a-6869-bc48-d7c4-a04128f3fcb8@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

As I mentioned in patch 03, include translation is a thing.  This amends 
the file handling to determine that, and for resolving explicit imports 
of header-units

The logic for locating a header unit is the same as locating a 
header-file.  Except there's a final step of mapping the header-file 
name to a compiled module interface.  That latter step is handled elsewhere.

Also, as one needs an explicit compile step to build a header-unit, we 
need a way of telling the preprocessor that the main file is itself a 
header, and it should locate the appropriate place on the INCLUDE path 
so INCLUDE_NEXT works.

nathan

-- 
Nathan Sidwell


[-- Attachment #2: 05-cpp-files.diff --]
[-- Type: text/x-patch, Size: 8769 bytes --]

diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -971,6 +1002,9 @@ extern cpp_callbacks *cpp_get_callbacks (cpp_reader *) ATTRIBUTE_PURE;
 extern void cpp_set_callbacks (cpp_reader *, cpp_callbacks *);
 extern class mkdeps *cpp_get_deps (cpp_reader *) ATTRIBUTE_PURE;
 
+extern const char *cpp_find_header_unit (cpp_reader *, const char *file,
+					 bool angle_p,  location_t);
+
 /* This function reads the file, but does not start preprocessing.  It
    returns the name of the original file; this is the same as the
    input file, except for preprocessed input.  This will generate at
diff --git c/libcpp/files.c w/libcpp/files.c
index 5af41364d0a..c23cd18ef2a 100644
--- c/libcpp/files.c
+++ w/libcpp/files.c
@@ -111,6 +111,9 @@ struct _cpp_file
 
   /* If this file is implicitly preincluded.  */
   bool implicit_preinclude : 1;
+
+  /* Is a C++ Module header unit.  */
+  int header_unit : 2;
 };
 
 /* A singly-linked list for all searches for a given file name, with
@@ -891,9 +894,9 @@ has_unique_contents (cpp_reader *pfile, _cpp_file *file, bool import,
 }
 
 /* Place the file referenced by FILE into a new buffer on the buffer
-   stack if possible.  IMPORT is true if this stacking attempt is
-   because of a #import directive.  Returns true if a buffer is
-   stacked.  Use LOC for any diagnostics.  */
+   stack if possible.  Returns true if a buffer is stacked.  Use LOC
+   for any diagnostics.  */
+
 bool
 _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
 		 location_t loc)
@@ -901,39 +904,75 @@ _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
   if (is_known_idempotent_file (pfile, file, type == IT_IMPORT))
     return false;
 
-  if (!read_file (pfile, file, loc))
-    return false;
-
-  if (!has_unique_contents (pfile, file, type == IT_IMPORT, loc))
-    return false;
-
   int sysp = 0;
-  if (pfile->buffer && file->dir)
-    sysp = MAX (pfile->buffer->sysp, file->dir->sysp);
-
-  /* Add the file to the dependencies on its first inclusion.  */
-  if (CPP_OPTION (pfile, deps.style) > (sysp != 0)
-      && !file->stack_count
-      && file->path[0]
-      && !(file->main_file && CPP_OPTION (pfile, deps.ignore_main_file)))
-    deps_add_dep (pfile->deps, file->path);
-
-  /* Clear buffer_valid since _cpp_clean_line messes it up.  */
-  file->buffer_valid = false;
-  file->stack_count++;
-
-  /* Stack the buffer.  */
-  cpp_buffer *buffer
-    = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
-		       CPP_OPTION (pfile, preprocessed)
-		       && !CPP_OPTION (pfile, directives_only));
-  buffer->file = file;
-  buffer->sysp = sysp;
-  buffer->to_free = file->buffer_start;
+  char *buf = nullptr;
 
-  /* Initialize controlling macro state.  */
-  pfile->mi_valid = true;
-  pfile->mi_cmacro = 0;
+  /* Check C++ module include translation.  */
+  if (!file->header_unit && type < IT_HEADER_HWM
+      /* Do not include translate include-next.  */
+      && type != IT_INCLUDE_NEXT
+      && pfile->cb.translate_include)
+    buf = (pfile->cb.translate_include
+	   (pfile, pfile->line_table, loc, file->path));
+
+  if (buf)
+    {
+      /* We don't increment the line number at the end of a buffer,
+	 because we don't usually need that location (we're popping an
+	 include file).  However in this case we do want to do the
+	 increment.  So push a writable buffer of two newlines to acheive
+	 that.  */
+      static uchar newlines[] = "\n\n";
+      cpp_push_buffer (pfile, newlines, 2, true);
+
+      cpp_buffer *buffer
+	= cpp_push_buffer (pfile, reinterpret_cast<unsigned char *> (buf),
+			   strlen (buf), true);
+      buffer->to_free = buffer->buf;
+
+      file->header_unit = +1;
+      _cpp_mark_file_once_only (pfile, file);
+    }
+  else
+    {
+      /* Not a header unit, and we know it.  */
+      file->header_unit = -1;
+
+      if (!read_file (pfile, file, loc))
+	return false;
+
+      if (!has_unique_contents (pfile, file, type == IT_IMPORT, loc))
+	return false;
+
+      if (pfile->buffer && file->dir)
+	sysp = MAX (pfile->buffer->sysp, file->dir->sysp);
+
+      /* Add the file to the dependencies on its first inclusion.  */
+      if (CPP_OPTION (pfile, deps.style) > (sysp != 0)
+	  && !file->stack_count
+	  && file->path[0]
+	  && !(file->main_file && CPP_OPTION (pfile, deps.ignore_main_file)))
+	deps_add_dep (pfile->deps, file->path);
+
+      /* Clear buffer_valid since _cpp_clean_line messes it up.  */
+      file->buffer_valid = false;
+      file->stack_count++;
+
+      /* Stack the buffer.  */
+      cpp_buffer *buffer
+	= cpp_push_buffer (pfile, file->buffer, file->st.st_size,
+			   CPP_OPTION (pfile, preprocessed)
+			   && !CPP_OPTION (pfile, directives_only));
+      buffer->file = file;
+      buffer->sysp = sysp;
+      buffer->main_file = (type >= IT_HEADER_HWM
+			   && !CPP_OPTION (pfile, main_search));
+      buffer->to_free = file->buffer_start;
+
+      /* Initialize controlling macro state.  */
+      pfile->mi_valid = true;
+      pfile->mi_cmacro = 0;
+    }
 
   /* In the case of a normal #include, we're now at the start of the
      line *following* the #include.  A separate location_t for this
@@ -941,19 +980,30 @@ _cpp_stack_file (cpp_reader *pfile, _cpp_file *file, include_type type,
 
      This does not apply if we found a PCH file, we're not a regular
      include, or we ran out of locations.  */
-  if (file->pchname == NULL
-      && type < IT_DIRECTIVE_HWM
-      && pfile->line_table->highest_location != LINE_MAP_MAX_LOCATION - 1)
+  bool decrement = (file->pchname == NULL
+		    && type < IT_DIRECTIVE_HWM
+		    && (pfile->line_table->highest_location
+			!= LINE_MAP_MAX_LOCATION - 1));
+  if (decrement)
     pfile->line_table->highest_location--;
 
-  /* Add line map and do callbacks.  */
-  _cpp_do_file_change (pfile, LC_ENTER, file->path,
+  if (file->header_unit <= 0)
+    /* Add line map and do callbacks.  */
+    _cpp_do_file_change (pfile, LC_ENTER, file->path,
 		       /* With preamble injection, start on line zero,
 			  so the preamble doesn't appear to have been
 			  included from line 1.  Likewise when
 			  starting preprocessed, we expect an initial
 			  locating line.  */
-		       type == IT_PRE_MAIN ? 0 : 1, sysp);
+			 type == IT_PRE_MAIN ? 0 : 1, sysp);
+  else if (decrement)
+    {
+      /* Adjust the line back one so we appear on the #include line itself.  */
+      const line_map_ordinary *map
+	= LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table);
+      linenum_type line = SOURCE_LINE (map, pfile->line_table->highest_line);
+      linemap_line_start (pfile->line_table, line - 1, 0);
+    }
 
   return true;
 }
@@ -1058,6 +1108,65 @@ _cpp_stack_include (cpp_reader *pfile, const char *fname, int angle_brackets,
   return _cpp_stack_file (pfile, file, type, loc);
 }
 
+/* NAME is a header file name, find the path we'll use to open it.  */
+
+const char *
+cpp_find_header_unit (cpp_reader *pfile, const char *name, bool angle,
+		      location_t loc)
+{
+  cpp_dir *dir = search_path_head (pfile, name, angle, IT_INCLUDE);
+  if (!dir)
+    return NULL;
+
+  _cpp_file *file = _cpp_find_file (pfile, name, dir, angle,
+				    _cpp_FFK_NORMAL, loc);
+  if (!file)
+    return NULL;
+
+  if (file->fd > 0)
+    {
+      /* Don't leave it open.  */
+      close (file->fd);
+      file->fd = 0;
+    }
+
+  file->header_unit = +1;
+  _cpp_mark_file_once_only (pfile, file);
+  return file->path;
+}
+
+/* Retrofit the just-entered main file asif it was an include.  This
+   will permit correct include_next use, and mark it as a system
+   header if that's where it resides.  We use filesystem-appropriate
+   prefix matching of the include path to locate the main file.  */
+void
+cpp_retrofit_as_include (cpp_reader *pfile)
+{
+  /* We should be the outermost.  */
+  gcc_assert (!pfile->buffer->prev);
+
+  if (const char *name = pfile->main_file->name)
+    {
+      /* Locate name on the include dir path, using a prefix match.  */
+      size_t name_len = strlen (name);
+      for (cpp_dir *dir = pfile->quote_include; dir; dir = dir->next)
+	if (dir->len < name_len
+	    && IS_DIR_SEPARATOR (name[dir->len])
+	    && !filename_ncmp (name, dir->name, dir->len))
+	  {
+	    pfile->main_file->dir = dir;
+	    pfile->buffer->main_file = false;
+	    if (dir->sysp)
+	      cpp_make_system_header (pfile, 1, 0);
+	    break;
+	  }
+    }
+
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
+}
+
 /* Could not open FILE.  The complication is dependency output.  */
 static void
 open_file_failed (cpp_reader *pfile, _cpp_file *file, int angle_brackets,


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

* [06/32] cpp macros
       [not found]         ` <ca22e13a-6869-bc48-d7c4-a04128f3fcb8@acm.org>
@ 2020-11-03 21:14           ` Nathan Sidwell
  2020-11-23 21:13             ` Jeff Law
       [not found]           ` <4d14b230-3263-9a13-3159-c4853f282761@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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


Header units can provide macros to an importer.  For efficiency that is 
done lazily.  When we import a header unit we mark the identifers it 
defines as significant.  It's only when we need the macro definition 
(including in #ifdef &| defined (X) processing) that we resolve the 
macro.  We already had lazy (builtin) macros.  This extends it to 
deferred macros.  Each deferred macro has an index the resolver can use. 
   on an LP64 host that index does not inlarge the node structure.

As a macro can be undefined by this mechanism, the users of this 
deferred interface must be prepared to handle 'not a macro after all'



-- 
Nathan Sidwell


[-- Attachment #2: 06-cpp-macro.diff --]
[-- Type: text/x-patch, Size: 14959 bytes --]

diff --git c/libcpp/directives.c w/libcpp/directives.c
index d7b59aae901..899ebbf023a 100644
--- c/libcpp/directives.c
+++ w/libcpp/directives.c
@@ -667,7 +667,8 @@ do_undef (cpp_reader *pfile)
 				   pfile->directive_line, 0,
 				   "undefining \"%s\"", NODE_NAME (node));
 
-	  if (CPP_OPTION (pfile, warn_unused_macros))
+	  if (node->value.macro
+	      && CPP_OPTION (pfile, warn_unused_macros))
 	    _cpp_warn_if_unused_macro (pfile, node, NULL);
 
 	  _cpp_free_definition (node);
@@ -1981,8 +1983,10 @@ do_ifdef (cpp_reader *pfile)
       if (node)
 	{
 	  skip = !_cpp_defined_macro_p (node);
+	  if (!_cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line))
+	    /* It wasn't a macro after all.  */
+	    skip = true;
 	  _cpp_mark_macro_used (node);
-	  _cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line);
 	  if (pfile->cb.used)
 	    pfile->cb.used (pfile, pfile->directive_line, node);
 	  check_eol (pfile, false);
@@ -2006,8 +2010,10 @@ do_ifndef (cpp_reader *pfile)
       if (node)
 	{
 	  skip = _cpp_defined_macro_p (node);
+	  if (!_cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line))
+	    /* It wasn't a macro after all.  */
+	    skip = false;
 	  _cpp_mark_macro_used (node);
-	  _cpp_maybe_notify_macro_use (pfile, node, pfile->directive_line);
 	  if (pfile->cb.used)
 	    pfile->cb.used (pfile, pfile->directive_line, node);
 	  check_eol (pfile, false);
diff --git c/libcpp/expr.c w/libcpp/expr.c
index e01a47a8c34..894e8515c90 100644
--- c/libcpp/expr.c
+++ w/libcpp/expr.c
@@ -1061,6 +1061,7 @@ parse_defined (cpp_reader *pfile)
 	}
     }
 
+  bool is_defined = false;
   if (node)
     {
       if ((pfile->context != initial_context
@@ -1068,9 +1069,11 @@ parse_defined (cpp_reader *pfile)
 	  && CPP_OPTION (pfile, warn_expansion_to_defined))
         cpp_pedwarning (pfile, CPP_W_EXPANSION_TO_DEFINED,
 		        "this use of \"defined\" may not be portable");
-
+      is_defined = _cpp_defined_macro_p (node);
+      if (!_cpp_maybe_notify_macro_use (pfile, node, token->src_loc))
+	/* It wasn't a macro after all.  */
+	is_defined = false;
       _cpp_mark_macro_used (node);
-      _cpp_maybe_notify_macro_use (pfile, node, token->src_loc);
 
       /* A possible controlling macro of the form #if !defined ().
 	 _cpp_parse_expr checks there was no other junk on the line.  */
@@ -1086,7 +1089,7 @@ parse_defined (cpp_reader *pfile)
   result.unsignedp = false;
   result.high = 0;
   result.overflow = false;
-  result.low = node && _cpp_defined_macro_p (node);
+  result.low = is_defined;
   return result;
 }
 
diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -308,6 +308,13 @@ enum cpp_normalize_level {
@@ -801,7 +824,10 @@ struct GTY(()) cpp_macro {
      tokens.  */
   unsigned int extra_tokens : 1;
 
-  /* 1 bits spare (32-bit). 33 on 64-bit target.  */
+  /* Imported (from a legacy header module).  */
+  unsigned int imported : 1;
+
+  /* 0 bits spare (32-bit). 32 on 64-bit target.  */
 
   union cpp_exp_u
   {
@@ -874,7 +901,7 @@ enum cpp_builtin_type
 union GTY(()) _cpp_hashnode_value {
   /* Assert (maybe NULL) */
   cpp_macro * GTY((tag ("NT_VOID"))) answers;
-  /* Macro (never NULL) */
+  /* Macro (maybe NULL) */
   cpp_macro * GTY((tag ("NT_USER_MACRO"))) macro;
   /* Code for a builtin macro.  */
   enum cpp_builtin_type GTY ((tag ("NT_BUILTIN_MACRO"))) builtin;
@@ -888,5 +915,9 @@ struct GTY(()) cpp_hashnode {
 
  /* 6 bits spare (plus another 32 on 64-bit hosts).  */
+
+  /* On a 64-bit system there would be 32-bits of padding to the value
+     field.  So placing the deferred index here is not costly.   */
+  unsigned deferred;			/* Deferred index, (unless zero).  */
 
   union _cpp_hashnode_value GTY ((desc ("%1.type"))) value;
 };
@@ -1027,6 +1066,18 @@ inline bool cpp_macro_p (const cpp_hashnode *node)
 {
   return node->type & NT_MACRO_MASK;
 }
+inline cpp_macro *cpp_set_deferred_macro (cpp_hashnode *node,
+					  cpp_macro *forced = NULL)
+{
+  cpp_macro *old = node->value.macro;
+
+  node->value.macro = forced;
+  node->type = NT_USER_MACRO;
+  node->flags &= ~NODE_USED;
+
+  return old;
+}
+cpp_macro *cpp_get_deferred_macro (cpp_reader *, cpp_hashnode *, location_t);
 
 /* Returns true if NODE is a function-like user macro.  */
 inline bool cpp_fun_like_macro_p (cpp_hashnode *node)
@@ -1034,11 +1085,13 @@ inline bool cpp_fun_like_macro_p (cpp_hashnode *node)
   return cpp_user_macro_p (node) && node->value.macro->fun_like;
 }
 
-extern const unsigned char *cpp_macro_definition (cpp_reader *,
-						  cpp_hashnode *);
+extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *);
+extern const unsigned char *cpp_macro_definition (cpp_reader *, cpp_hashnode *,
+						  const cpp_macro *);
 inline location_t cpp_macro_definition_location (cpp_hashnode *node)
 {
-  return node->value.macro->line;
+  const cpp_macro *macro = node->value.macro;
+  return macro ? macro->line : 0;
 }
 extern void _cpp_backup_tokens (cpp_reader *, unsigned int);
 extern const cpp_token *cpp_peek_token (cpp_reader *, int);
@@ -1219,6 +1272,8 @@ extern int cpp_ideq (const cpp_token *, const char *);
 extern void cpp_output_line (cpp_reader *, FILE *);
 extern unsigned char *cpp_output_line_to_string (cpp_reader *,
 						 const unsigned char *);
+extern const unsigned char *cpp_alloc_token_string
+  (cpp_reader *, const unsigned char *, unsigned);
 extern void cpp_output_token (const cpp_token *, FILE *);
 extern const char *cpp_type2name (enum cpp_ttype, unsigned char flags);
 /* Returns the value of an escape sequence, truncated to the correct
@@ -1274,6 +1329,8 @@ extern void cpp_scan_nooutput (cpp_reader *);
 extern int  cpp_sys_macro_p (cpp_reader *);
 extern unsigned char *cpp_quote_string (unsigned char *, const unsigned char *,
 					unsigned int);
+extern bool cpp_compare_macros (const cpp_macro *macro1,
+				const cpp_macro *macro2);
 
 /* In files.c */
 extern bool cpp_included (cpp_reader *, const char *);
diff --git c/libcpp/internal.h w/libcpp/internal.h
index 4759961a33a..a92abf281c0 100644
--- c/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -649,13 +665,14 @@ inline bool _cpp_defined_macro_p (cpp_hashnode *node)
 }
 
 /* In macro.c */
-extern void _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
-				   location_t loc);
-inline void _cpp_maybe_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
+extern bool _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
+				   location_t);
+inline bool _cpp_maybe_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
 					 location_t loc)
 {
   if (!(node->flags & NODE_USED))
-    _cpp_notify_macro_use (pfile, node, loc);
+    return _cpp_notify_macro_use (pfile, node, loc);
+  return true;
 }
 extern cpp_macro *_cpp_new_macro (cpp_reader *, cpp_macro_kind, void *);
 extern void _cpp_free_definition (cpp_hashnode *);
@@ -869,29 +886,7 @@ ufputs (const unsigned char *s, FILE *f)
   return fputs ((const char *)s, f);
 }
 
-  /* In line-map.c.  */
-
-/* Create a macro map.  A macro map encodes source locations of tokens
-   that are part of a macro replacement-list, at a macro expansion
-   point. See the extensive comments of struct line_map and struct
-   line_map_macro, in line-map.h.
-
-   This map shall be created when the macro is expanded. The map
-   encodes the source location of the expansion point of the macro as
-   well as the "original" source location of each token that is part
-   of the macro replacement-list. If a macro is defined but never
-   expanded, it has no macro map.  SET is the set of maps the macro
-   map should be part of.  MACRO_NODE is the macro which the new macro
-   map should encode source locations for.  EXPANSION is the location
-   of the expansion point of MACRO. For function-like macros
-   invocations, it's best to make it point to the closing parenthesis
-   of the macro, rather than the the location of the first character
-   of the macro.  NUM_TOKENS is the number of tokens that are part of
-   the replacement-list of MACRO.  */
-const line_map_macro *linemap_enter_macro (class line_maps *,
-					   struct cpp_hashnode*,
-					   location_t,
-					   unsigned int);
+/* In line-map.c.  */
 
 /* Create and return a virtual location for a token that is part of a
    macro expansion-list at a macro expansion point.  See the comment
diff --git c/libcpp/lex.c w/libcpp/lex.c
index fb222924c8c..b3498f195bf 100644
--- c/libcpp/lex.c
+++ w/libcpp/lex.c
@@ -1577,13 +1577,20 @@ static void
 create_literal (cpp_reader *pfile, cpp_token *token, const uchar *base,
 		unsigned int len, enum cpp_ttype type)
 {
-  uchar *dest = _cpp_unaligned_alloc (pfile, len + 1);
-
-  memcpy (dest, base, len);
-  dest[len] = '\0';
   token->type = type;
   token->val.str.len = len;
-  token->val.str.text = dest;
+  token->val.str.text = cpp_alloc_token_string (pfile, base, len);
+}
+
+const uchar *
+cpp_alloc_token_string (cpp_reader *pfile,
+			const unsigned char *ptr, unsigned len)
+{
+  uchar *dest = _cpp_unaligned_alloc (pfile, len + 1);
+
+  dest[len] = 0;
+  memcpy (dest, ptr, len);
+  return dest;
 }
 
 /* A pair of raw buffer pointers.  The currently open one is [1], the
diff --git c/libcpp/macro.c w/libcpp/macro.c
index e304f67c2e0..f5f280dfdc7 100644
--- c/libcpp/macro.c
+++ w/libcpp/macro.c
@@ -268,6 +268,8 @@ class vaopt_state {
 
 /* Macro expansion.  */
 
+static cpp_macro *get_deferred_or_lazy_macro (cpp_reader *, cpp_hashnode *,
+					      location_t);
 static int enter_macro_context (cpp_reader *, cpp_hashnode *,
 				const cpp_token *, location_t);
 static int builtin_macro (cpp_reader *, cpp_hashnode *,
@@ -338,10 +340,6 @@ static cpp_macro *create_iso_definition (cpp_reader *);
 /* #define directive parsing and handling.  */
 
 static cpp_macro *lex_expansion_token (cpp_reader *, cpp_macro *);
-static bool warn_of_redefinition (cpp_reader *, cpp_hashnode *,
-				  const cpp_macro *);
-static bool compare_macros (const cpp_macro *, const cpp_macro *);
-
 static bool parse_params (cpp_reader *, unsigned *, bool *);
 static void check_trad_stringification (cpp_reader *, const cpp_macro *,
 					const cpp_string *);
@@ -353,8 +351,6 @@ static const cpp_token* cpp_get_token_1 (cpp_reader *, location_t *);
 
 static cpp_hashnode* macro_of_context (cpp_context *context);
 
-static bool in_macro_expansion_p (cpp_reader *pfile);
-
 /* Statistical counter tracking the number of macros that got
    expanded.  */
 unsigned num_expanded_macros_counter = 0;
@@ -2845,6 +2841,12 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location)
       if (node->type == NT_VOID || (result->flags & NO_EXPAND))
 	break;
 
+      if (!(node->flags & NODE_USED)
+	  && node->type == NT_USER_MACRO
+	  && !node->value.macro
+	  && !cpp_get_deferred_macro (pfile, node, result->src_loc))
+	break;
+
       if (!(node->flags & NODE_DISABLED))
 	{
 	  int ret = 0;
@@ -3104,22 +3185,15 @@ warn_of_redefinition (cpp_reader *pfile, cpp_hashnode *node,
   if (node->flags & NODE_CONDITIONAL)
     return false;
 
-  cpp_macro *macro1 = node->value.macro;
-  if (macro1->lazy)
-    {
-      /* We don't want to mark MACRO as used, but do need to finalize
-	 its laziness.  */
-      pfile->cb.user_lazy_macro (pfile, macro1, macro1->lazy - 1);
-      macro1->lazy = 0;
-    }
-
-  return compare_macros (macro1, macro2);
+  if (cpp_macro *macro1 = get_deferred_or_lazy_macro (pfile, node, macro2->line))
+    return cpp_compare_macros (macro1, macro2);
+  return false;
 }
 
 /* Return TRUE if MACRO1 and MACRO2 differ.  */
 
-static bool
-compare_macros (const cpp_macro *macro1, const cpp_macro *macro2)
+bool
+cpp_compare_macros (const cpp_macro *macro1, const cpp_macro *macro2)
 {
   /* Redefinition of a macro is allowed if and only if the old and new
      definitions are the same.  (6.10.3 paragraph 2).  */
@@ -3601,6 +3675,7 @@ _cpp_new_macro (cpp_reader *pfile, cpp_macro_kind kind, void *placement)
   macro->used = !CPP_OPTION (pfile, warn_unused_macros);
   macro->count = 0;
   macro->fun_like = 0;
+  macro->imported = false;
   macro->extra_tokens = 0;
   /* To suppress some diagnostics.  */
   macro->syshdr = pfile->buffer && pfile->buffer->sysp != 0;
@@ -3678,11 +3753,46 @@ cpp_define_lazily (cpp_reader *pfile, cpp_hashnode *node, unsigned num)
   macro->lazy = num + 1;
 }
 
+/* NODE is a deferred macro, resolve it, returning the definition
+   (which may be NULL).  */
+cpp_macro *
+cpp_get_deferred_macro (cpp_reader *pfile, cpp_hashnode *node,
+			location_t loc)
+{
+  node->value.macro = pfile->cb.user_deferred_macro (pfile, loc, node);
+
+  if (!node->value.macro)
+    node->type = NT_VOID;
+
+  return node->value.macro;
+}
+
+static cpp_macro *
+get_deferred_or_lazy_macro (cpp_reader *pfile, cpp_hashnode *node,
+			    location_t loc)
+{
+  cpp_macro *macro = node->value.macro;
+  if (!macro)
+    {
+      macro = cpp_get_deferred_macro (pfile, node, loc);
+      if (!macro)
+	return NULL;
+    }
+
+  if (macro->lazy)
+    {
+      pfile->cb.user_lazy_macro (pfile, macro, macro->lazy - 1);
+      macro->lazy = 0;
+    }
+
+  return macro;
+}
+
 /* Notify the use of NODE in a macro-aware context (i.e. expanding it,
    or testing its existance).  Also applies any lazy definition.
    Return FALSE if the macro isn't really there.  */
 
-extern void
+extern bool
 _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
 		       location_t loc)
 {
@@ -3690,14 +3800,8 @@ _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
   switch (node->type)
     {
     case NT_USER_MACRO:
-      {
-	cpp_macro *macro = node->value.macro;
-	if (macro->lazy)
-	  {
-	    pfile->cb.user_lazy_macro (pfile, macro, macro->lazy - 1);
-	    macro->lazy = 0;
-	  }
-      }
+      if (!get_deferred_or_lazy_macro (pfile, node, loc))
+	return false;
       /* FALLTHROUGH.  */
 
     case NT_BUILTIN_MACRO:
@@ -3713,6 +3817,8 @@ _cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
     default:
       abort ();
     }
+
+  return true;
 }
 
 /* Warn if a token in STRING matches one of a function-like MACRO's
@@ -3765,12 +3871,19 @@ check_trad_stringification (cpp_reader *pfile, const cpp_macro *macro,
 const unsigned char *
 cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node)
 {
-  unsigned int i, len;
-  unsigned char *buffer;
-
   gcc_checking_assert (cpp_user_macro_p (node));
 
-  const cpp_macro *macro = node->value.macro;
+  if (const cpp_macro *macro = get_deferred_or_lazy_macro (pfile, node, 0))
+    return cpp_macro_definition (pfile, node, macro);
+  return NULL;
+}
+
+const unsigned char *
+cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node,
+		      const cpp_macro *macro)
+{
+  unsigned int i, len;
+  unsigned char *buffer;
 
   /* Calculate length.  */
   len = NODE_LEN (node) * 10 + 2;		/* ' ' and NUL.  */


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

* [07/32] cpp main
       [not found]           ` <4d14b230-3263-9a13-3159-c4853f282761@acm.org>
@ 2020-11-03 21:14             ` Nathan Sidwell
  2020-11-10 23:18               ` Jeff Law
       [not found]             ` <688bd28f-5998-0def-8c40-03b817832d63@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here's the changes to starting the main file. I have added the ability 
to search the user or system include paths for the main file.  That's 
real helpful to users attempting to build header-units.  I'll discuss 
the CLI with the options patch.

Also recording the location at which the main file starts is helpful for 
whole-file diagnostics.


-- 
Nathan Sidwell


[-- Attachment #2: 07-cpp-main.diff --]
[-- Type: text/x-patch, Size: 3769 bytes --]

diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -308,6 +308,13 @@ enum cpp_normalize_level {
   normalized_none
 };
 
+enum cpp_main_search 
+{
+  CMS_none,
+  CMS_user,
+  CMS_system
+};
+
 /* This structure is nested inside struct cpp_reader, and
    carries all the options visible to the command line.  */
 struct cpp_options
@@ -560,6 +573,8 @@ struct cpp_options
 
   /* The maximum depth of the nested #include.  */
   unsigned int max_include_depth;
+
+  cpp_main_search main_search : 8;
 };
 
 /* Diagnostic levels.  To get a diagnostic without associating a
@@ -978,6 +1012,10 @@ extern class mkdeps *cpp_get_deps (cpp_reader *) ATTRIBUTE_PURE;
    too.  If there was an error opening the file, it returns NULL.  */
 extern const char *cpp_read_main_file (cpp_reader *, const char *,
 				       bool injecting = false);
+extern location_t cpp_main_loc (const cpp_reader *);
+
+/* Adjust for the main file to be an include.  */
+extern void cpp_retrofit_as_include (cpp_reader *);
 
 /* Set up built-ins with special behavior.  Use cpp_init_builtins()
    instead unless your know what you are doing.  */
diff --git c/libcpp/init.c w/libcpp/init.c
index 6c52f50de39..96ade569457 100644
--- c/libcpp/init.c
+++ w/libcpp/init.c
@@ -672,8 +672,14 @@ cpp_read_main_file (cpp_reader *pfile, const char *fname, bool injecting)
     deps_add_default_target (deps, fname);
 
   pfile->main_file
-    = _cpp_find_file (pfile, fname, &pfile->no_search_path, /*angle=*/0,
-		      _cpp_FFK_NORMAL, 0);
+    = _cpp_find_file (pfile, fname,
+		      CPP_OPTION (pfile, preprocessed) ? &pfile->no_search_path
+		      : CPP_OPTION (pfile, main_search) == CMS_user
+		      ? pfile->quote_include
+		      : CPP_OPTION (pfile, main_search) == CMS_system
+		      ? pfile->bracket_include : &pfile->no_search_path,
+		      /*angle=*/0, _cpp_FFK_NORMAL, 0);
+
   if (_cpp_find_failed (pfile->main_file))
     return NULL;
 
@@ -695,7 +701,16 @@ cpp_read_main_file (cpp_reader *pfile, const char *fname, bool injecting)
 			     LINEMAP_LINE (last), LINEMAP_SYSP (last));
       }
 
-  return ORDINARY_MAP_FILE_NAME (LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table));
+  auto *map = LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table);
+  pfile->main_loc = MAP_START_LOCATION (map);
+
+  return ORDINARY_MAP_FILE_NAME (map);
+}
+
+location_t
+cpp_main_loc (const cpp_reader *pfile)
+{
+  return pfile->main_loc;
 }
 
 /* For preprocessed files, if the very first characters are
diff --git c/libcpp/internal.h w/libcpp/internal.h
index 4759961a33a..17b65601b66 100644
--- c/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -357,6 +366,9 @@ struct cpp_buffer
      token from the enclosing buffer is returned.  */
   bool return_at_eof : 1;
 
+  /* Is from main file.  */
+  bool main_file : 1;
+
   /* One for a system header, two for a C system header file that therefore
      needs to be extern "C" protected in C++, and zero otherwise.  */
   unsigned char sysp;
@@ -583,6 +595,10 @@ struct cpp_reader
   /* If non-zero, the lexer will use this location for the next token
      instead of getting a location from the linemap.  */
   location_t forced_token_location;
+
+  /* Location identifying the main source file -- intended to be line
+     zero of said file.  */
+  location_t main_loc;
 };
 
 /* Character classes.  Based on the more primitive macros in safe-ctype.h.
@@ -635,7 +651,7 @@ static inline int cpp_in_primary_file (cpp_reader *);
 static inline int
 cpp_in_primary_file (cpp_reader *pfile)
 {
-  return pfile->line_table->depth == 1;
+  return pfile->buffer->main_file;
 }
 
 /* True if NODE is a macro for the purposes of ifdef, defined etc.  */


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

* [08/32] cpp mkdeps
       [not found]             ` <688bd28f-5998-0def-8c40-03b817832d63@acm.org>
@ 2020-11-03 21:14               ` Nathan Sidwell
  2020-11-10 23:20                 ` Jeff Law
       [not found]               ` <89819c10-e86d-9b01-5673-5223a525a135@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

The final bit of preprocessor change is that to mkdeps.  We have a new 
kind of thing to be dependent upon -- a compiled module interface. 
That's also an additional target.

This adds the logic to mkdeps to keep those dependencies separate from 
include file dependencies.  I made use of this when demonstrating a 
module-aware Make POC.


-- 
Nathan Sidwell


[-- Attachment #2: 08-cpp-mkdeps.diff --]
[-- Type: text/x-patch, Size: 4777 bytes --]

diff --git c/libcpp/include/mkdeps.h w/libcpp/include/mkdeps.h
index 6d05351cb4a..1d6dd9562a5 100644
--- c/libcpp/include/mkdeps.h
+++ w/libcpp/include/mkdeps.h
@@ -55,9 +57,12 @@ extern void deps_add_default_target (class mkdeps *, const char *);
    dependency entered should be the primary source file.  */
 extern void deps_add_dep (class mkdeps *, const char *);
 
-/* Write out a deps buffer to a specified file.  The third argument
+extern void deps_add_module (struct mkdeps *, const char *,
+			     const char * = NULL, bool = false);
+
+/* Write out a deps buffer to a specified file.  The last argument
    is the number of columns to word-wrap at (0 means don't wrap).  */
-extern void deps_write (const class mkdeps *, FILE *, bool, unsigned int);
+extern void deps_write (const cpp_reader *, FILE *, unsigned int);
 
 /* Write out a deps buffer to a file, in a form that can be read back
    with deps_restore.  Returns nonzero on error, in which case the
diff --git c/libcpp/mkdeps.c w/libcpp/mkdeps.c
index ea5f060c380..0e70b0b633b 100644
--- c/libcpp/mkdeps.c
+++ w/libcpp/mkdeps.c
@@ -81,7 +81,7 @@ public:
   };
 
   mkdeps ()
-    : quote_lwm (0)
+    : module_name (NULL), cmi_name (NULL), is_header_unit (false), quote_lwm (0)
   {
   }
   ~mkdeps ()
@@ -94,14 +94,22 @@ public:
       free (const_cast <char *> (deps[i]));
     for (i = vpath.size (); i--;)
       XDELETEVEC (vpath[i].str);
+    for (i = modules.size (); i--;)
+      XDELETEVEC (modules[i]);
+    XDELETEVEC (module_name);
+    free (const_cast <char *> (cmi_name));
   }
 
 public:
   vec<const char *> targets;
   vec<const char *> deps;
   vec<velt> vpath;
+  vec<const char *> modules;
 
 public:
+  const char *module_name;
+  const char *cmi_name;
+  bool is_header_unit;
   unsigned short quote_lwm;
 };
 
@@ -323,6 +331,27 @@ deps_add_vpath (class mkdeps *d, const char *vpath)
     }
 }
 
+/* Add a new module dependency.  M is the module name, with P being
+   any partition name thereof (might be NULL).  If CMI is NULL, this
+   is an import dependency.  Otherwise, this is an output dependency
+   specifying CMI as the output file, of type IS_HEADER_UNIT.  */
+
+void
+deps_add_module (struct mkdeps *d, const char *m,
+		 const char *cmi, bool is_header_unit)
+{
+  m = xstrdup (m);
+
+  if (cmi)
+    {
+      d->module_name = m;
+      d->is_header_unit = is_header_unit;
+      d->cmi_name = xstrdup (cmi);
+    }
+  else
+    d->modules.push (m);
+}
+
 /* Write NAME, with a leading space to FP, a Makefile.  Advance COL as
    appropriate, wrap at COLMAX, returning new column number.  Iff
    QUOTE apply quoting.  Append TRAIL.  */
@@ -379,6 +408,8 @@ make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
   if (d->deps.size ())
     {
       column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
+      if (CPP_OPTION (pfile, deps.modules) && d->cmi_name)
+	column = make_write_name (d->cmi_name, fp, column, colmax);
       fputs (":", fp);
       column++;
       make_write_vec (d->deps, fp, column, colmax);
@@ -387,6 +418,59 @@ make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
 	for (unsigned i = 1; i < d->deps.size (); i++)
 	  fprintf (fp, "%s:\n", munge (d->deps[i]));
     }
+
+  if (!CPP_OPTION (pfile, deps.modules))
+    return;
+
+  if (d->modules.size ())
+    {
+      column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
+      if (d->cmi_name)
+	column = make_write_name (d->cmi_name, fp, column, colmax);
+      fputs (":", fp);
+      column++;
+      column = make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
+      fputs ("\n", fp);
+    }
+
+  if (d->module_name)
+    {
+      if (d->cmi_name)
+	{
+	  /* module-name : cmi-name */
+	  column = make_write_name (d->module_name, fp, 0, colmax,
+				    true, ".c++m");
+	  fputs (":", fp);
+	  column++;
+	  column = make_write_name (d->cmi_name, fp, column, colmax);
+	  fputs ("\n", fp);
+
+	  column = fprintf (fp, ".PHONY:");
+	  column = make_write_name (d->module_name, fp, column, colmax,
+				    true, ".c++m");
+	  fputs ("\n", fp);
+	}
+
+      if (d->cmi_name && !d->is_header_unit)
+	{
+	  /* An order-only dependency.
+	      cmi-name :| first-target
+	     We can probably drop this this in favour of Make-4.3's grouped
+	      targets '&:'  */
+	  column = make_write_name (d->cmi_name, fp, 0, colmax);
+	  fputs (":|", fp);
+	  column++;
+	  column = make_write_name (d->targets[0], fp, column, colmax);
+	  fputs ("\n", fp);
+	}
+    }
+  
+  if (d->modules.size ())
+    {
+      column = fprintf (fp, "CXX_IMPORTS +=");
+      make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
+      fputs ("\n", fp);
+    }
 }
 
 /* Write out dependencies according to the selected format (which is


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

* [09/32] core diagnostics
       [not found]               ` <89819c10-e86d-9b01-5673-5223a525a135@acm.org>
@ 2020-11-03 21:14                 ` Nathan Sidwell
  2020-11-21 16:57                   ` Jeff Law
       [not found]                 ` <35879e15-d74a-c664-4d44-15f4b3783d77@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

The 'included from ...' chain that one gets at the start of a diagnostic 
needs extending to include importing.  There are a few combinations to 
handle, but nothing particularly exciting.

-- 
Nathan Sidwell


[-- Attachment #2: 09-core-diag.diff --]
[-- Type: text/x-patch, Size: 1705 bytes --]

diff --git c/gcc/diagnostic.c w/gcc/diagnostic.c
index 1b6c9845892..52bf5e0da1f 100644
--- c/gcc/diagnostic.c
+++ w/gcc/diagnostic.c
@@ -689,12 +705,13 @@ diagnostic_report_current_module (diagnostic_context *context, location_t where)
       set_last_module (context, map);
       if (! MAIN_FILE_P (map))
 	{
-	  bool first = true;
+	  bool first = true, need_inc = true, was_module = MAP_MODULE_P (map);
 	  expanded_location s = {};
 	  do
 	    {
 	      where = linemap_included_from (map);
 	      map = linemap_included_from_linemap (line_table, map);
+	      bool is_module = MAP_MODULE_P (map);
 	      s.file = LINEMAP_FILE (map);
 	      s.line = SOURCE_LINE (map, where);
 	      int col = -1;
@@ -706,14 +723,24 @@ diagnostic_report_current_module (diagnostic_context *context, location_t where)
 	      const char *line_col = maybe_line_and_column (s.line, col);
 	      static const char *const msgs[] =
 		{
-		 N_("In file included from"),
+		 NULL,
 		 N_("                 from"),
+		 N_("In file included from"),	/* 2 */
+		 N_("        included from"),
+		 N_("In module"),		/* 4 */
+		 N_("of module"),
+		 N_("In module imported at"),	/* 6 */
+		 N_("imported at"),
 		};
-	      unsigned index = !first;
+
+	      unsigned index = (was_module ? 6 : is_module ? 4
+				: need_inc ? 2 : 0) + !first;
+
 	      pp_verbatim (context->printer, "%s%s %r%s%s%R",
-			   first ? "" : ",\n", _(msgs[index]),
+			   first ? "" : was_module ? ", " : ",\n",
+			   _(msgs[index]),
 			   "locus", s.file, line_col);
-	      first = false;
+	      first = false, need_inc = was_module, was_module = is_module;
 	    }
 	  while (! MAIN_FILE_P (map));
 	  pp_verbatim (context->printer, ":");


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

* [11/32] instrumentation
       [not found]                 ` <35879e15-d74a-c664-4d44-15f4b3783d77@acm.org>
  2020-11-03 20:03                   ` [09/33] " Nathan Sidwell
@ 2020-11-03 21:14                   ` Nathan Sidwell
  2020-11-06 20:28                     ` Jeff Law
       [not found]                   ` <9ae23c4c-67a5-a267-c939-5a96e9488612@acm.org>
  2 siblings, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:14 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

I add one new parameter -- the number of concurrently open module files, 
and 3 instrumentation timers.


-- 
Nathan Sidwell


[-- Attachment #2: 11-core-parmtime.diff --]
[-- Type: text/x-patch, Size: 1444 bytes --]

diff --git c/gcc/params.opt w/gcc/params.opt
index 7bac39a9d58..0366584f94e 100644
--- c/gcc/params.opt
+++ w/gcc/params.opt
@@ -349,6 +349,10 @@ Maximal stack frame growth due to inlining (in percent).
 Common Joined UInteger Var(param_large_unit_insns) Optimization Init(10000) Param
 The size of translation unit to be considered large.
 
+-param=lazy-modules=
+C++ Joined UInteger Var(param_lazy_modules) Init(32768) Param
+Maximum number of concurrently open C++ module files when lazy loading.
+
 -param=lim-expensive=
 Common Joined UInteger Var(param_lim_expensive) Init(20) Param Optimization
 The minimum cost of an expensive expression in the loop invariant motion.
diff --git c/gcc/timevar.def w/gcc/timevar.def
index 08c21c04009..f2bd58f0f58 100644
--- c/gcc/timevar.def
+++ w/gcc/timevar.def
@@ -145,6 +145,9 @@ DEFTIMEVAR (TV_CONSTEXPR	     , "constant expression evaluation")
 DEFTIMEVAR (TV_CONSTRAINT_NORM	     , "constraint normalization")
 DEFTIMEVAR (TV_CONSTRAINT_SAT	     , "constraint satisfaction")
 DEFTIMEVAR (TV_CONSTRAINT_SUB	     , "constraint subsumption")
+DEFTIMEVAR (TV_MODULE_IMPORT	     , "module import")
+DEFTIMEVAR (TV_MODULE_EXPORT	     , "module export")
+DEFTIMEVAR (TV_MODULE_MAPPER         , "module mapper")
 DEFTIMEVAR (TV_FLATTEN_INLINING      , "flatten inlining")
 DEFTIMEVAR (TV_EARLY_INLINING        , "early inlining heuristics")
 DEFTIMEVAR (TV_INLINE_PARAMETERS     , "inline parameters")


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

* [12/32] user documentation
       [not found]                   ` <9ae23c4c-67a5-a267-c939-5a96e9488612@acm.org>
@ 2020-11-03 21:15                     ` Nathan Sidwell
  2020-11-25 19:17                       ` Jeff Law
       [not found]                     ` <c1c50ae5-44d5-4b2d-dba8-76b459b7d994@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

This is the user documentation.

-- 
Nathan Sidwell


[-- Attachment #2: 12-core-doc.diff --]
[-- Type: text/x-patch, Size: 18173 bytes --]

diff --git c/gcc/doc/invoke.texi w/gcc/doc/invoke.texi
index 492b7dcdf10..3fdee8bbd3b 100644
--- c/gcc/doc/invoke.texi
+++ w/gcc/doc/invoke.texi
@@ -97,7 +97,7 @@ The usual way to run GCC is to run the executable called @command{gcc}, or
 When you compile C++ programs, you should invoke GCC as @command{g++} 
 instead.  @xref{Invoking G++,,Compiling C++ Programs}, 
 for information about the differences in behavior between @command{gcc} 
-and @code{g++} when compiling C++ programs.
+and @command{g++} when compiling C++ programs.
 
 @cindex grouping options
 @cindex options, grouping
@@ -172,6 +172,7 @@ listing and explanation of the binary and decimal byte size prefixes.
 * Spec Files::          How to pass switches to sub-processes.
 * Environment Variables:: Env vars that affect GCC.
 * Precompiled Headers:: Compiling a header once, and using it many times.
+* C++ Modules::		Experimental C++20 module system.
 @end menu
 
 @c man begin OPTIONS
@@ -595,7 +596,7 @@ Objective-C and Objective-C++ Dialects}.
 -fpreprocessed  -ftabstop=@var{width}  -ftrack-macro-expansion  @gol
 -fwide-exec-charset=@var{charset}  -fworking-directory @gol
 -H  -imacros @var{file}  -include @var{file} @gol
--M  -MD  -MF  -MG  -MM  -MMD  -MP  -MQ  -MT @gol
+-M  -MD  -MF  -MG  -MM  -MMD  -MP  -Mno-modules -MQ  -MT @gol
 -no-integrated-cpp  -P  -pthread  -remap @gol
 -traditional  -traditional-cpp  -trigraphs @gol
 -U@var{macro}  -undef  @gol
@@ -1567,7 +1568,7 @@ name suffix).  This option applies to all following input files until
 the next @option{-x} option.  Possible values for @var{language} are:
 @smallexample
 c  c-header  cpp-output
-c++  c++-header  c++-cpp-output
+c++  c++-header  c++-system-header c++-user-header c++-cpp-output
 objective-c  objective-c-header  objective-c-cpp-output
 objective-c++ objective-c++-header objective-c++-cpp-output
 assembler  assembler-with-cpp
@@ -3037,6 +3038,63 @@ To save space, do not emit out-of-line copies of inline functions
 controlled by @code{#pragma implementation}.  This causes linker
 errors if these functions are not inlined everywhere they are called.
 
+@item -fmodules-ts
+@itemx -fno-modules-ts
+@opindex fmodules-ts
+@opindex fno-modules-ts
+Enable support for C++ 20 modules.  The @option{-fno-modules-ts} is
+usually not needed, as that is the default.  Even though this is a
+C++20 feature, it is not currently implicitly enabled by selecting
+that standard version.
+
+@item -fno-module-lazy
+@opindex fno-module-lazy
+@opindex fmodule-lazy
+Disable lazy module importing and module mapper creation.
+
+@item -fmodule-header
+@itemx -fmodule-header=user
+@itemx -fmodule-header=system
+@opindex fmodule-header
+Compile as a header unit.
+
+@item -fmodule-implicit-inline
+@opindex fmodule-implicit-inline
+Memmber functions defined in their class definitions are not
+implicitly inline for modular code.  This is different to traditional
+C++ behaviour, for good reasons.  However, it may result in a
+difficulty during code porting.  This option will make such function
+definitions implicitly inline.  It does however generate an ABI
+incompatibility, so you must use it everywhere or nowhere.  (Such
+definitions outside of a named module remain implicitly inline,
+regardless.)
+
+@item -fmodule-only
+@opindex fmodule-only
+Only emit the module CMI, inhibiting any object file.
+
+@item -flang-info-include-translate
+@itemx -flang-info-include-translate=@var{header}
+@opindex flang-info-include-translate
+Note include translation events.
+
+@item -Winvalid-imported-macros
+@opindex Winvalid-imported-macros
+@opindex Wno-invalid-imported-macros
+Verify all imported macro definitions are valid at end of
+compilation.  This is not enabled by default, as it requires
+additional processing to determine.
+
+@item -fmodule-mapper=@r{[}@var{hostname}@r{]}:@var{port}@r{[}?@var{ident}@r{]}
+@itemx -fmodule-mapper=|@var{program}@r{[}?@var{ident}@r{]} @var{args...}
+@itemx -fmodule-mapper==@var{socket}@r{[}?@var{ident}@r{]}
+@itemx -fmodule-mapper=@var{file}@r{[}?@var{ident}@r{]}
+@vindex CXX_MODULE_MAPPER @r{environment variable}
+@opindex fmodule-mapper
+An oracle to query for module name to filename mappings.  If
+unspecified the @env{CXX_MODULE_MAPPER} environment variable is used,
+and if that is unset, a default is provided.
+
 @item -fms-extensions
 @opindex fms-extensions
 Disable Wpedantic warnings about constructs used in MFC, such as implicit
@@ -14215,7 +14273,7 @@ Note that it is quite common that execution counts of some part of
 programs depends, for example, on length of temporary file names or
 memory space randomization (that may affect hash-table collision rate).
 Such non-reproducible part of programs may be annotated by
-@code{no_instrument_function} function attribute. @code{gcov-dump} with
+@code{no_instrument_function} function attribute. @command{gcov-dump} with
 @option{-l} can be used to dump gathered data and verify that they are
 indeed reproducible.
 
@@ -16585,6 +16643,11 @@ By default, the dump will contain messages about successful
 optimizations (equivalent to @option{-optimized}) together with
 low-level details about the analysis.
 
+@item -fdump-lang
+@opindex fdump-lang
+Dump language-specific information.  The file name is made by appending
+@file{.lang} to the source file name.
+
 @item -fdump-lang-all
 @itemx -fdump-lang-@var{switch}
 @itemx -fdump-lang-@var{switch}-@var{options}
@@ -16605,6 +16668,14 @@ Enable all language-specific dumps.
 Dump class hierarchy information.  Virtual table information is emitted
 unless '@option{slim}' is specified.  This option is applicable to C++ only.
 
+@item module
+Dump module information.  Options @option{lineno} (locations),
+@option{graph} (reachability), @option{blocks} (clusters),
+@option{uid} (serialization), @option{alias} (mergeable),
+@option{asmname} (Elrond), @option{eh} (mapper) & @option{vops}
+(macros) may provide additional information.  This option is
+applicable to C++ only.
+
 @item raw
 Dump the raw internal tree data.  This option is applicable to C++ only.
 
@@ -32246,3 +32317,225 @@ precompiled header, the actual behavior is a mixture of the
 behavior for the options.  For instance, if you use @option{-g} to
 generate the precompiled header but not when using it, you may or may
 not get debugging information for routines in the precompiled header.
+
+@node C++ Modules
+@section C++ Modules
+@cindex speed of compilation
+
+Modules are a C++ 20 language feature.  As the name suggests, it
+provides a modular compilation system, intending to provide both
+faster builds and better library isolation.  The ``Merging Modules''
+paper @uref{http//wg21.link/p1103}, provides the easiest to read set
+of changes to the standard, although it does not capture later
+changes.  That specification is now in the C++ Working Draft,
+@uref{git@@github.com:cplusplus/draft.git}, as of 2020-03-14, it is
+considered complete (there may be defect reports to come).
+@emph{Modules support is a work in progress.  The implementation is
+not complete.  Command-line options are not stable.}
+
+Modular compilation is @emph{not} enabled with just the
+@option{-std=c++20} option.  You must explicitly enable it with the
+@option{-fmodules-ts} option.  It is independent of the language
+version selected.
+
+No new source file suffixes are required or supported.  If you wish to
+use a non-standard suffix (@xref{Overall Options}), you will also need
+to provide a @option{-x c++} option too.@footnote{Some users like to
+distinguish module interface files with a new suffix, such as naming
+the source @code{module.cppm}, which involves
+teaching all tools about the new suffix.  A different scheme, such as
+naming @code{module-m.cpp} would be less invasive.}
+
+Compiling a module interface unit produces an additional output (to
+the assembly or object file), called a Compiled Module Interface
+(CMI).  This encodes the exported declarations of the module.
+Importing a module reads in the CMI.  The import graph is a Directed
+Acyclic Graph (DAG).  You must build imports before the importer.
+
+Header files may themselves be compiled to header units, which are a
+transitional ability aiming at faster compilation.  The
+@option{-fmodule-header} option is used to enable this, and implies
+the @option{-fmodules-ts} option.  These CMIs are named by the fully
+resolved underlying header file, and thus may be a complete pathname
+containing subdirectories.  If the header file is found at an absolute
+pathname, the CMI location is still relative to a CMI root directory.
+
+As header files often have no suffix, you commonly have to specify a
+@option{-x} option to tell the compiler the source is a header file.
+You may use @option{-x c++-header}, @option{-x c++-user-header} or
+@option{-x c++-system-header}.  When used in conjunction with
+@option{-fmodules-ts}, these all imply an appropriate
+@option{-fmodule-header} option.  The latter two variants will use the
+user or system include path to search for the file specified.  This
+allows you to, for instance, compile standard library header files as
+header units, without needing to know exactly where they are
+installed.  Specifying the language as one of these variants also
+inhibits output of the object file, as header files have no associated
+object file.
+
+When creating an output CMI any missing directory components are
+created in a manner that is safe for concurrent builds creating
+multiple, different, CMIs within a common subdirectory tree.
+
+CMI contents are written to a temporary file, which is then atomically
+renamed.  Observers will either see old contents (if there is an
+existing file), or complete new contents.  They will not observe the
+CMI during its creation.  This is unlike object file writing, which
+may be observed by an external process.
+
+CMIs are read in lazily.  Generally blocks are read when name lookup
+or template instantiation occurs.  To inhibit this, the
+@option{-fno-module-lazy} option may be used.
+
+The @option{-fmodule-only} option disables generation of the
+associated object file for compiling a module interface.  Only the CMI
+is generated.  This option is implied when using the
+@option{-fmodule-header} option.
+
+The @option{--param lazy-modules=@var{n}} parameter controls the limit
+on the number of concurrently open module files during lazy loading.
+Should more modules be imported, an LRU algorithm is used to determine
+which files to close -- until that file is needed again.  This limit
+may be exceeded with deep module dependency hierarchies.  With large
+code bases there may be more imports than the process limit of file
+descriptors.  By default, the limit is a few less than the per-process
+file descriptor hard limit, if that is determinable.@footnote{Where
+applicable the soft limit is incremented as needed towards the hard limit.}
+
+The @option{-flang-info-include-translate} options note whether
+include translation occurs.  With no argument, all include translation
+is noted.  Otherwise, queries about include translation of a specific
+header file is noted.  The latter form may be repeated.
+
+The @option{-Winvalid-imported-macros} option causes all imported macros
+to be resolved at the end of compilation.  Without this, imported
+macros are only resolved when expanded or (re)defined.  This option
+will detect conflicting import definitions for all macros.
+
+The @option{-fmodule-mapper} family of options are described below.
+
+@menu
+* C++ Module Mapper::       Module Mapper
+* C++ Module Preprocessing::  Module Preprocessing
+@end menu
+
+@node C++ Module Mapper
+@subsection Module Mapper
+@cindex C++ Module Mapper
+
+A module mapper provides a server or file that the compiler queries to
+determine the mapping between module names and CMI files.  It is also
+used to build CMIs on demand.  A mapper may be specified with the
+@option{-fmodule-mapper=@var{val}} option or @env{CXX_MODULE_MAPPER}
+environment variable.  The value may have one of the following forms:
+
+@table @gcctabopt
+
+@item @r{[}@var{hostname}@r{]}:@var{port}@r{[}?@var{ident}@r{]}
+An optional hostname and a numeric port number to connect to.  If the
+hostname is omitted, the loopback address is used.  If the hostname
+corresponds to multiple IPV6 addresses, these are tried in turn, until
+one is successful.  If your host lacks ipv6, this form is
+non-functional.  If you must use ipv4 @emph{get with the 21st century},
+or failing that use @option{-fmodule-mapper='|ncat @var{ipv4host}
+@var{port}'}.
+
+@item =@var{socket}@r{[}?@var{ident}@r{]}
+A local domain socket.  If your host lacks local domain sockets, this
+form is non-functional.
+
+@item |@var{program}@r{[}?@var{ident}@r{]} @r{[}@var{args...}@r{]}
+A program to spawn, and communicate with on its stdin/stdout streams.
+Your @var{PATH} environment variable is searched for the program.
+Arguments are separated by space characters, (it is not possible for
+one of the arguments delivered to the program to contain a space).
+
+@item <>@r{[}?@var{ident}@r{]}
+@item <>@var{fdinout}@r{[}?@var{ident}@r{]}
+@item <@var{fdin}>@var{fdout}@r{[}?@var{ident}@r{]}
+File descriptors to communicate over.  The first form, @option{<>},
+communicates over stdin and stdout.  The second form specifies a
+bidirectional file descriptor and the last form allows specifying
+two independent descriptors.  Note that other compiler options might
+cause the compiler to read stdin or write stdout.
+
+@item @var{file}@r{[}?@var{ident}@r{]}
+A mapping file consisting of space-separated module-name, filename
+pairs, one per line.  Only the mappings for the direct imports and any
+module export name need be provided.  If other mappings are provided,
+they override those stored in any imported CMI files.  A repository
+root may be specified in the mapping file by using @samp{$root} as the
+module name in the first active line.
+
+@end table
+
+As shown, an optional @var{ident} may suffix the first word of the
+option, indicated by a @samp{?} prefix.  The value is used in the
+initial handshake with the module server, or to specify a prefix on
+mapping file lines.  In the server case, the main source file name is
+used if no @var{ident} is specified.  In the file case, all non-blank
+lines are significant, unless a value is specified, in which case only
+lines beginning with @var{ident} are significant.  The @var{ident}
+must be separated by whitespace from the module name.  Be aware that
+@samp{<}, @samp{>}, @samp{?}  and @samp{|} characters are often
+significant to the shell, and therefore may need quoting.
+
+The mapper is connected to or loaded lazily, when the first module
+mapping is required.  The networking protocols are only supported on
+hosts that provide networking.  If no mapper is specified a default is
+provided.
+
+A project-specific mapper is expected to be provided by the build
+system that invokes the compiler.  It is not expected that a
+general-purpose server is provided for all compilations.  As such, the
+server will know the build configuration, the compiler it invoked, and
+the environment (such as working directory) in which that is
+operating.  As it may parallelize builds, several compilations may
+connect to the same socket.
+
+The default mapper generates CMI files in a @samp{gcm.cache}
+directory.  CMI files have a @samp{.gcm} suffix.  The module unit name
+is used directly to provide the basename.  Header units construct a
+relative path using the underlying header file name.  If the path is
+already relative, a @samp{,} directory is prepended.  Internal
+@samp{..} components are translated to @samp{,,}.  No attempt is made
+to canonicalize these filenames beyond that done by the preprocessor's
+include search algorithm, as in general it is ambiguous when symbolic
+links are present.
+
+The mapper protocol was published as ``A Module Mapper''
+@uref{https://wg21.link/p1184}.  The implementation is provided by
+@command{libcody}, @uref{https://www.github.com/urnathan/libcody},
+which specifies the canonical protocol definition.  A proof of concept
+server implementation embedded in @command{make} was described in
+''Make Me A Module'', @uref{https://wg21.link/p1602}.
+
+@node C++ Module Preprocessing
+@subsection Module Preprocessing
+@cindex C++ Module Preprocessing
+
+Modules affect preprocessing because of header units and include
+translation.  Some uses of the preprocessor as a separate step will
+either not produce a correct output, or require CMIs to be available.
+
+Header units import macros.  These macros can affect later conditional
+inclusion, which therefore can cascade to differing import sets.  When
+preprocessing, it is necessary to load the CMI.  If a header unit is
+unavailable, the preprocessor will issue a warning and continue (when
+no just preprocessing, an error is emitted).  Detecting such imports
+requires preprocessor tokenization of the input stream to phase 4
+(macro expansion).
+
+Include translation converts @code{#include}, @code{include_next} and
+@code{import} directives to internal @code{import} declarations.
+Whether a particular directive is translated is controlled by the
+module mapper.  Header unit names are canonicalized during
+preprocessing.
+
+Dependency information can be emitted for macro import, extending the
+functionality of @option{-MD} and @option{-MMD} options.  Detection of
+import declarations also requires phase 4 preprocessing, and thus
+requires full preprocessing (or compilation).
+
+The @option{-M}, @option{-MM} and @option{-fdirectives-only} options halt
+preprocessing before phase 4.
diff --git c/gcc/doc/cppopts.texi w/gcc/doc/cppopts.texi
index 7f1849d841f..0df8f4cb446 100644
--- c/gcc/doc/cppopts.texi
+++ w/gcc/doc/cppopts.texi
@@ -139,6 +139,11 @@ this useless.
 
 This feature is used in automatic updating of makefiles.
 
+@item -Mno-modules
+@opindex Mno-modules
+Disable dependency generation for compiled module interfaces.  Some
+dependency parsers are not fully make-compatible.
+
 @item -MP
 @opindex MP
 This option instructs CPP to add a phony target for each dependency


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

* [13/32] new options
       [not found]                     ` <c1c50ae5-44d5-4b2d-dba8-76b459b7d994@acm.org>
@ 2020-11-03 21:15                       ` Nathan Sidwell
  2020-11-25 19:27                         ` Jeff Law
       [not found]                       ` <c0a0b999-f96e-177c-b393-9eaf8d1aa520@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here are the new options, along with the C++ lang-spec changes.

Modules is enabled by -fmodules-ts, it is not implicitly enabled by 
-std=c++20.  Usually that's the only option you need to add for a 
module-aware build.

to build a header unit you can either add -fmodule-header to a c++ 
build, or you can set the language to be c++-header and add -fmodules-ts:

g++ -x c++-header -fmodules-ts my-header-file

to search the user or system include paths select c++-user-header of 
c++-system-header as the language.

enabling -fmodules-ts will disable PCH, they do not play well together. 
There is a potential issue down the road when we implicitly enable 
modules.  At that point building a header-unit could become 
indistinguishable from building a PCH.  Perhaps we should consider 
phasing in an explicit PCH option?

-- 
Nathan Sidwell


[-- Attachment #2: 13-family-options.diff --]
[-- Type: text/x-patch, Size: 8189 bytes --]

diff --git c/gcc/c-family/c-opts.c w/gcc/c-family/c-opts.c
index 120f4489f6c..c8f08d9e014 100644
--- c/gcc/c-family/c-opts.c
+++ w/gcc/c-family/c-opts.c
@@ -234,6 +234,7 @@ c_common_init_options (unsigned int decoded_options_count,
   cpp_opts = cpp_get_options (parse_in);
   cpp_opts->dollars_in_ident = DOLLARS_IN_IDENTIFIERS;
   cpp_opts->objc = c_dialect_objc ();
+  cpp_opts->deps.modules = true;
 
   /* Reset to avoid warnings on internal definitions.  We set it just
      before passing on command-line options to cpplib.  */
@@ -367,6 +368,18 @@ c_common_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       cpp_opts->deps.phony_targets = true;
       break;
 
+    case OPT_Mmodules:
+      /* Do not set deps_seen, so the user can unconditionally turn
+	 this on or off.  */
+      cpp_opts->deps.modules = true;
+      break;
+
+    case OPT_Mno_modules:
+      /* Do not set deps_seen, so the user can unconditionally turn
+	 this on or off.  */
+      cpp_opts->deps.modules = false;
+      break;
+
     case OPT_MQ:
     case OPT_MT:
       deps_seen = true;
diff --git c/gcc/c-family/c-pch.c w/gcc/c-family/c-pch.c
index a2292f46a7d..f55a1fde3ad 100644
--- c/gcc/c-family/c-pch.c
+++ w/gcc/c-family/c-pch.c
@@ -206,6 +206,10 @@ c_common_valid_pch (cpp_reader *pfile, const char *name, int fd)
   /* Perform a quick test of whether this is a valid
      precompiled header for the current language.  */
 
+  /* C++ modules and PCH don't play together.  */
+  if (flag_modules)
+    return 2;
+
   sizeread = read (fd, ident, IDENT_LENGTH + 16);
   if (sizeread == -1)
     fatal_error (input_location, "cannot read %s: %m", name);
diff --git c/gcc/c-family/c.opt w/gcc/c-family/c.opt
index 10e53ea67c9..1edc7f2b2bd 100644
--- c/gcc/c-family/c.opt
+++ w/gcc/c-family/c.opt
@@ -236,6 +236,14 @@ MMD
 C ObjC C++ ObjC++ NoDriverArg Separate MissingArgError(missing filename after %qs)
 Like -MD but ignore system header files.
 
+Mmodules
+C++
+Generate C++ Module dependency information.
+
+Mno-modules
+C++
+; undocumented
+
 MP
 C ObjC C++ ObjC++
 Generate phony targets for all headers.
@@ -1666,6 +1674,53 @@ flax-vector-conversions
 C ObjC C++ ObjC++ Var(flag_lax_vector_conversions)
 Allow implicit conversions between vectors with differing numbers of subparts and/or differing element types.
 
+fmodules-ts
+C++ ObjC++ Var(flag_modules) Integer Init(0)
+Enable C++ modules-ts (experimental).
+
+fno-modules
+C++ ObjC++ Undocumented RejectNegative Var(flag_modules,0) Integer
+;; undocumented
+
+fmodule-header
+C++ ObjC RejectNegative Var(flag_header_unit,0) Integer
+Enable C++ header module (experimental).
+
+fmodule-header=
+C++ ObjC++ Joined RejectNegative Undocumented
+
+fmodule-implicit-inline
+C++ ObjC++ Var(flag_module_implicit_inline,0) Integer
+Member functions defined within their class are inline in module purview.
+
+fmodule-only
+C++ ObjC RejectNegative Var(flag_module_only) Integer
+Only emit Binary Module Interface.
+
+fmodule-mapper=
+C++ ObjC++ Joined RejectNegative MissingArgError(missing mapper)
+Mapper for module to CMI files.
+
+fmodule-lazy
+C++ ObjC++ Var(flag_module_lazy) Init(1)
+Enable lazy module importing.
+
+fmodule-version-ignore
+C++ ObjC Var(flag_module_version_ignore) Integer
+; undocumented, Very dangerous, but occasionally useful
+
+Winvalid-imported-macros
+C++ ObjC++ Var(warn_imported_macros)
+Warn about macros that have conflicting header units definitions.
+
+flang-info-include-translate
+C++ Var(note_include_translate)
+Note #include directives translated to import declarations.
+
+flang-info-include-translate=
+C++ Joined RejectNegative MissingArgError(missing header name)
+Note a #include translation of a specific header.
+
 fmax-include-depth=
 C ObjC C++ ObjC++ Joined RejectNegative UInteger
 fmax-include-depth=<number> Set the maximum depth of the nested #include.
diff --git c/gcc/cp/lang-specs.h w/gcc/cp/lang-specs.h
index 0ad4a33b93e..1388aaed198 100644
--- c/gcc/cp/lang-specs.h
+++ w/gcc/cp/lang-specs.h
@@ -40,17 +40,57 @@ along with GCC; see the file COPYING3.  If not see
   {".tcc", "@c++-header", 0, 0, 0},
   {".hh",  "@c++-header", 0, 0, 0},
   {"@c++-header",
-      "%{E|M|MM:cc1plus -E %(cpp_options) %2 %(cpp_debug_options)}"
+      "%{E|M|MM:cc1plus -E %{fmodules-ts:-fdirectives-only -fmodule-header}"
+      "  %(cpp_options) %2 %(cpp_debug_options)}"
+      "%{!E:%{!M:%{!MM:"
+      "  %{save-temps*|no-integrated-cpp:cc1plus -E"
+      "    %{fmodules-ts:-fdirectives-only -fmodule-header}"
+      "	   %(cpp_options) %2 -o %{save-temps*:%b.ii} %{!save-temps*:%g.ii} \n}"
+      "  cc1plus %{save-temps*|no-integrated-cpp:-fpreprocessed"
+      "            %{fmodules-ts:-fdirectives-only}"
+      " 	   %{save-temps*:%b.ii} %{!save-temps*:%g.ii}}"
+      "  %{!save-temps*:%{!no-integrated-cpp:%(cpp_unique_options)}}"
+      "  %{fmodules-ts:-fmodule-header %{fpreprocessed:-fdirectives-only}}"
+      "  %(cc1_options) %2"
+      "  %{!S:-o %g.s%V}"
+      "  %{!fsyntax-only:%{!fmodule-*:%{!fmodules-*:%{!fdump-ada-spec*:"
+      "	     %{!o*:--output-pch=%i.gch}%W{o*:--output-pch=%*}}}}}}}}",
+     CPLUSPLUS_CPP_SPEC, 0, 0},
+  {"@c++-system-header",
+      "%{E|M|MM:cc1plus -E"
+      "  %{fmodules-ts:-fdirectives-only -fmodule-header=system}"
+      "  %(cpp_options) %2 %(cpp_debug_options)}"
       "%{!E:%{!M:%{!MM:"
       "  %{save-temps*|no-integrated-cpp:cc1plus -E"
+      "    %{fmodules-ts:-fdirectives-only -fmodule-header=system}"
       "	   %(cpp_options) %2 -o %{save-temps*:%b.ii} %{!save-temps*:%g.ii} \n}"
       "  cc1plus %{save-temps*|no-integrated-cpp:-fpreprocessed"
+      "            %{fmodules-ts:-fdirectives-only}"
       " 	   %{save-temps*:%b.ii} %{!save-temps*:%g.ii}}"
       "  %{!save-temps*:%{!no-integrated-cpp:%(cpp_unique_options)}}"
+      "  %{fmodules-ts:-fmodule-header=system %{fpreprocessed:-fdirectives-only}}"
       "  %(cc1_options) %2"
-      "  %{!fsyntax-only:%{!S:-o %g.s}"
-      "    %{!fdump-ada-spec*:%{!o*:--output-pch=%i.gch}"
-      "      %W{o*:--output-pch=%*}}%V}}}}",
+      "  %{!S:-o %g.s%V}"
+      "  %{!fsyntax-only:%{!fmodule-*:%{!fmodules-*:%{!fdump-ada-spec*:"
+      "	     %{!o*:--output-pch=%i.gch}%W{o*:--output-pch=%*}}}}}}}}",
+     CPLUSPLUS_CPP_SPEC, 0, 0},
+  {"@c++-user-header",
+      "%{E|M|MM:cc1plus -E"
+      "  %{fmodules-ts:-fdirectives-only -fmodule-header=user}"
+      "  %(cpp_options) %2 %(cpp_debug_options)}"
+      "%{!E:%{!M:%{!MM:"
+      "  %{save-temps*|no-integrated-cpp:cc1plus -E"
+      "    %{fmodules-ts:-fdirectives-only -fmodule-header=user}"
+      "	   %(cpp_options) %2 -o %{save-temps*:%b.ii} %{!save-temps*:%g.ii} \n}"
+      "  cc1plus %{save-temps*|no-integrated-cpp:-fpreprocessed"
+      "            %{fmodules-ts:-fdirectives-only}"
+      " 	   %{save-temps*:%b.ii} %{!save-temps*:%g.ii}}"
+      "  %{!save-temps*:%{!no-integrated-cpp:%(cpp_unique_options)}}"
+      "  %{fmodules-ts:-fmodule-header=user %{fpreprocessed:-fdirectives-only}}"
+      "  %(cc1_options) %2"
+      "  %{!S:-o %g.s%V}"
+      "  %{!fsyntax-only:%{!fmodule-*:%{!fmodules-*:%{!fdump-ada-spec*:"
+      "	     %{!o*:--output-pch=%i.gch}%W{o*:--output-pch=%*}}}}}}}}",
      CPLUSPLUS_CPP_SPEC, 0, 0},
   {"@c++",
       "%{E|M|MM:cc1plus -E %(cpp_options) %2 %(cpp_debug_options)}"
@@ -60,11 +100,14 @@ along with GCC; see the file COPYING3.  If not see
       "  cc1plus %{save-temps*|no-integrated-cpp:-fpreprocessed"
       " 	   %{save-temps*:%b.ii} %{!save-temps*:%g.ii}}"
       "  %{!save-temps*:%{!no-integrated-cpp:%(cpp_unique_options)}}"
-      "	 %(cc1_options) %2"
-      "  %{!fsyntax-only:%(invoke_as)}}}}",
+      "  %(cc1_options) %2"
+      "  %{fmodule-only:%{!S:-o %g.s%V}}"
+      "  %{!fsyntax-only:%{!fmodule-only:%(invoke_as)}}}}}",
       CPLUSPLUS_CPP_SPEC, 0, 0},
   {".ii", "@c++-cpp-output", 0, 0, 0},
   {"@c++-cpp-output",
       "%{!E:%{!M:%{!MM:"
       "  cc1plus -fpreprocessed %i %(cc1_options) %2"
-      "  %{!fsyntax-only:%(invoke_as)}}}}", 0, 0, 0},
+      "  %{fmodule-only:%{!S:-o %g.s%V}}"
+      "  %{!fsyntax-only:%{!fmodule-only:%{!fmodule-header*:"
+      "     %(invoke_as)}}}}}}", 0, 0, 0},


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

* [14/32] new keywords
       [not found]                       ` <c0a0b999-f96e-177c-b393-9eaf8d1aa520@acm.org>
@ 2020-11-03 21:15                         ` Nathan Sidwell
  2020-11-06 20:29                           ` Jeff Law
       [not found]                         ` <6666545b-0583-4812-4745-d51994465818@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

We have 3 new keywords.  As I mentioned in the preprocessor lexing, the 
keywords are context-sensitive, and we create internal ones.  These 
internal ones have an 'invisible' space at the end of them.  This has 
the advantage of making them return to normal identifiers in 
preprocessor output.

-- 
Nathan Sidwell


[-- Attachment #2: 14-family-keywords.diff --]
[-- Type: text/x-patch, Size: 2841 bytes --]

diff --git c/gcc/c-family/c-common.c w/gcc/c-family/c-common.c
index d56238aeb01..33d6ad73125 100644
--- c/gcc/c-family/c-common.c
+++ w/gcc/c-family/c-common.c
@@ -540,6 +540,12 @@ const struct c_common_resword c_common_reswords[] =
   { "concept",		RID_CONCEPT,	D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
   { "requires", 	RID_REQUIRES,	D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
 
+  /* Modules-related keywords, these are internal unspellable tokens,
+     created by the preprocessor.  */
+  { "module ",		RID__MODULE,	D_CXX_MODULES_FLAGS | D_CXXWARN },
+  { "import ",		RID__IMPORT,	D_CXX_MODULES_FLAGS | D_CXXWARN },
+  { "export ",		RID__EXPORT,	D_CXX_MODULES_FLAGS | D_CXXWARN },
+
   /* Coroutines-related keywords */
   { "co_await",		RID_CO_AWAIT,	D_CXX_COROUTINES_FLAGS | D_CXXWARN },
   { "co_yield",		RID_CO_YIELD,	D_CXX_COROUTINES_FLAGS | D_CXXWARN },
diff --git c/gcc/c-family/c-common.h w/gcc/c-family/c-common.h
index 18b489d55a3..eb9070b4c6c 100644
--- c/gcc/c-family/c-common.h
+++ w/gcc/c-family/c-common.h
@@ -190,6 +190,9 @@ enum rid
   /* C++ concepts */
   RID_CONCEPT, RID_REQUIRES,
 
+  /* C++ modules.  */
+  RID__MODULE, RID__IMPORT, RID__EXPORT, /* Internal tokens.  */
+
   /* C++ coroutines */
   RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN,
 
@@ -438,9 +445,11 @@ extern machine_mode c_default_pointer_mode;
 #define D_CXX_CHAR8_T	0X1000	/* In C++, only with -fchar8_t.  */
 #define D_CXX20		0x2000  /* In C++, C++20 only.  */
 #define D_CXX_COROUTINES 0x4000  /* In C++, only with coroutines.  */
+#define D_CXX_MODULES	0x8000  /* In C++, only with modules.  */
 
 #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
+#define D_CXX_MODULES_FLAGS (D_CXXONLY | D_CXX_MODULES)
 #define D_CXX_COROUTINES_FLAGS (D_CXXONLY | D_CXX_COROUTINES)
 
 /* The reserved keyword table.  */
diff --git c/gcc/c-family/c-cppbuiltin.c w/gcc/c-family/c-cppbuiltin.c
index e5ebb79e22a..12043aa4702 100644
--- c/gcc/c-family/c-cppbuiltin.c
+++ w/gcc/c-family/c-cppbuiltin.c
@@ -1013,6 +1013,10 @@ c_cpp_builtins (cpp_reader *pfile)
           else
             cpp_define (pfile, "__cpp_concepts=201507L");
         }
+      if (flag_modules)
+	/* The std-defined value is 201907L, but I don't think we can
+	   claim victory yet.  201810 is the p1103 date. */
+	cpp_define (pfile, "__cpp_modules=201810L");
       if (flag_coroutines)
 	cpp_define (pfile, "__cpp_impl_coroutine=201902L"); /* n4861, DIS */
       if (flag_tm)
diff --git c/gcc/cp/lex.c w/gcc/cp/lex.c
index 8a69bc4f170..013cbadf625 100644
--- c/gcc/cp/lex.c
+++ w/gcc/cp/lex.c
@@ -235,6 +236,8 @@ init_reswords (void)
     mask |= D_CXX_CONCEPTS;
   if (!flag_coroutines)
     mask |= D_CXX_COROUTINES;
+  if (!flag_modules)
+    mask |= D_CXX_MODULES;
   if (!flag_tm)
     mask |= D_TRANSMEM;
   if (!flag_char8_t)


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

* [15/32] new C++ lexer
       [not found]                         ` <6666545b-0583-4812-4745-d51994465818@acm.org>
@ 2020-11-03 21:15                           ` Nathan Sidwell
       [not found]                           ` <d6ae6a29-0da0-06a5-d86c-1b0dd4bd96e4@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Importation of header-units requires the tokenizer to recognize such 
imports during tokenization so that their macros become available.  The 
C++ parser currently (and continues to) tokenize the entire file before 
beginning c++ parsing.

This implements an explicit coroutine to manage that recognition.  It is 
used both for C++ parsing proper and for just preprocessing.  When a 
module-significant control line is observed, we call into the module 
machinery to handle it.  Usually, we'll also call again later when 
parsing that declaration.


-- 
Nathan Sidwell


[-- Attachment #2: 15-c++-lexer.diff --]
[-- Type: text/x-patch, Size: 5633 bytes --]

diff --git c/gcc/cp/lex.c w/gcc/cp/lex.c
index 8a69bc4f170..013cbadf625 100644
--- c/gcc/cp/lex.c
+++ w/gcc/cp/lex.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-objc.h"
 #include "gcc-rich-location.h"
 #include "cp-name-hint.h"
+#include "langhooks.h"
 
 static int interface_strcmp (const char *);
 static void init_cp_pragma (void);
@@ -378,7 +381,206 @@ interface_strcmp (const char* s)
   return 1;
 }
 
-\f
+/* We've just read a cpp-token, figure out our next state.  Hey, this
+   is a hand-coded co-routine!  */
+
+struct token_coro
+{
+  enum state
+  {
+   idle,
+   module_first,
+   module_cont,
+   module_end,
+  };
+
+  enum state state : 8;
+  bool is_import : 1;
+  bool got_export : 1;
+  bool got_colon : 1;
+  bool want_dot : 1;
+
+  location_t token_loc;
+  cpp_reader *reader;
+  module_state *module;
+  module_state *import;
+
+  token_coro (cpp_reader *reader)
+    : state (idle), is_import (false),
+    got_export (false), got_colon (false), want_dot (false),
+    token_loc (UNKNOWN_LOCATION),
+    reader (reader), module (NULL), import (NULL)
+  {
+  };
+
+  /* Process the next token.  Note we cannot see CPP_EOF inside a
+     pragma -- a CPP_PRAGMA_EOL always happens.  */
+  uintptr_t resume (int type, int keyword, tree value, location_t loc)
+  {
+    unsigned res = 0;
+
+    switch (state)
+      {
+      case idle:
+	if (type == CPP_KEYWORD)
+	  switch (keyword)
+	    {
+	    default:
+	      break;
+
+	    case RID__EXPORT:
+	      got_export = true;
+	      res = lang_hooks::PT_begin_pragma;
+	      break;
+
+	    case RID__IMPORT:
+	      is_import = true;
+	      /* FALLTHRU */
+	    case RID__MODULE:
+	      state = module_first;
+	      want_dot = false;
+	      got_colon = false;
+	      token_loc = loc;
+	      import = NULL;
+	      if (!got_export)
+		res = lang_hooks::PT_begin_pragma;
+	      break;
+	    }
+	break;
+
+      case module_first:
+	if (is_import && type == CPP_HEADER_NAME)
+	  {
+	    /* A header name.  The preprocessor will have already
+	       done include searching and canonicalization.  */
+	    state = module_end;
+	    goto header_unit;
+	  }
+	
+	if (type == CPP_PADDING || type == CPP_COMMENT)
+	  break;
+
+	state = module_cont;
+	if (type == CPP_COLON && module)
+	  {
+	    got_colon = true;
+	    import = module;
+	    break;
+	  }
+	/* FALLTHROUGH  */
+
+      case module_cont:
+	switch (type)
+	  {
+	  case CPP_PADDING:
+	  case CPP_COMMENT:
+	    break;
+
+	  default:
+	    /* If we ever need to pay attention to attributes for
+	       header modules, more logic will be needed.  */
+	    state = module_end;
+	    break;
+
+	  case CPP_COLON:
+	    if (got_colon)
+	      state = module_end;
+	    got_colon = true;
+	    /* FALLTHROUGH  */
+	  case CPP_DOT:
+	    if (!want_dot)
+	      state = module_end;
+	    want_dot = false;
+	    break;
+
+	  case CPP_PRAGMA_EOL:
+	    goto module_end;
+
+	  case CPP_NAME:
+	    if (want_dot)
+	      {
+		/* Got name instead of [.:].  */
+		state = module_end;
+		break;
+	      }
+	  header_unit:
+	    import = get_module (value, import, got_colon);
+	    want_dot = true;
+	    break;
+	  }
+	break;
+
+      case module_end:
+	if (type == CPP_PRAGMA_EOL)
+	  {
+	  module_end:;
+	    /* End of the directive, handle the name.  */
+	    if (import)
+	      if (module_state *m
+		  = preprocess_module (import, token_loc, module != NULL,
+				       is_import, got_export, reader))
+		if (!module)
+		  module = m;
+
+	    is_import = got_export = false;
+	    state = idle;
+	  }
+	break;
+      }
+
+    return res;
+  }
+};
+
+/* Initialize or teardown.  */
+
+uintptr_t
+module_token_cdtor (cpp_reader *pfile, uintptr_t data_)
+{
+  if (token_coro *coro = reinterpret_cast<token_coro *> (data_))
+    {
+      preprocessed_module (pfile);
+      delete coro;
+      data_ = 0;
+    }
+  else if (modules_p ())
+    data_ = reinterpret_cast <uintptr_t > (new token_coro (pfile));
+
+  return data_;
+}
+
+uintptr_t
+module_token_lang (int type, int keyword, tree value, location_t loc,
+		   uintptr_t data_)
+{
+  token_coro *coro = reinterpret_cast <token_coro *> (data_);
+  return coro->resume (type, keyword, value, loc);
+}
+
+uintptr_t
+module_token_pre (cpp_reader *pfile, const cpp_token *tok, uintptr_t data_)
+{
+  if (!tok)
+    return module_token_cdtor (pfile, data_);
+
+  int type = tok->type;
+  int keyword = RID_MAX;
+  tree value = NULL_TREE;
+
+  if (tok->type == CPP_NAME)
+    {
+      value = HT_IDENT_TO_GCC_IDENT (HT_NODE (tok->val.node.node));
+      if (IDENTIFIER_KEYWORD_P (value))
+	{
+	  keyword = C_RID_CODE (value);
+	  type = CPP_KEYWORD;
+	}
+    }
+  else if (tok->type == CPP_HEADER_NAME)
+    value = build_string (tok->val.str.len, (const char *)tok->val.str.text);
+
+  return module_token_lang (type, keyword, value, tok->src_loc, data_);
+}
 
 /* Parse a #pragma whose sole argument is a string constant.
    If OPT is true, the argument is optional.  */
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -6742,6 +6940,10 @@ extern void set_identifier_kind			(tree, cp_identifier_kind);
 extern bool cxx_init				(void);
 extern void cxx_finish				(void);
 extern bool in_main_input_context		(void);
+extern uintptr_t module_token_pre (cpp_reader *, const cpp_token *, uintptr_t);
+extern uintptr_t module_token_cdtor (cpp_reader *, uintptr_t);
+extern uintptr_t module_token_lang (int type, int keyword, tree value,
+				    location_t, uintptr_t);
 
 /* in method.c */
 extern void init_method				(void);


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

* [16/32] new C++ infrastructure
       [not found]                           ` <d6ae6a29-0da0-06a5-d86c-1b0dd4bd96e4@acm.org>
@ 2020-11-03 21:15                             ` Nathan Sidwell
       [not found]                             ` <d3149435-8b5e-7c79-f4bb-80238fdfcf72@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

We have to expose a few entry points in the c++ handling.  when loading 
a module we need to do template argument comparisons and not resolve 
typenames.  I added a comparing_typenames global flag that;ll end up 
doing a but more than the 'comparing_specializations' flag we already have.

Some of the node creation routines need lower level access.

The rtti machinery needs to be driven externally.  We need to create 
template type parameters, and we need to create explicitly dependent 
array types (we can't call dependent_type_p in the middle of loading 
something).

Oh, this also has some pieces handling parameter packs as 
structurally-compared types.  IIRC when we fixed some of that in the 
GCC10 release, we tried to handle them as not-types.  I never addressed 
that on the modules branch.

nathan

-- 
Nathan Sidwell


[-- Attachment #2: 16-c++-infra.diff --]
[-- Type: text/x-patch, Size: 13642 bytes --]

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -5327,6 +5500,10 @@ extern int function_depth;
    in structrual_comptypes.  */
 extern int comparing_specializations;
 
+/* Nonzero if we are inside eq_specializations, which affects
+   resolving of typenames in structural_comptypes.  */
+extern int comparing_typenames;
+
 /* In parser.c.  */
 
 /* Nonzero if we are parsing an unevaluated operand: an operand to
@@ -6457,6 +6651,7 @@ extern bool check_omp_return			(void);
 extern tree make_typename_type			(tree, tree, enum tag_types, tsubst_flags_t);
 extern tree build_typename_type			(tree, tree, tree, tag_types);
 extern tree make_unbound_class_template		(tree, tree, tree, tsubst_flags_t);
+extern tree make_unbound_class_template_raw	(tree, tree, tree);
 extern tree build_library_fn_ptr		(const char *, tree, int);
 extern tree build_cp_library_fn_ptr		(const char *, tree, int);
 extern tree push_library_fn			(tree, tree, tree, int);
@@ -6731,8 +6926,11 @@ extern tree unqualified_fn_lookup_error		(cp_expr);
 extern tree make_conv_op_name			(tree);
 extern tree build_lang_decl			(enum tree_code, tree, tree);
 extern tree build_lang_decl_loc			(location_t, enum tree_code, tree, tree);
+extern bool maybe_add_lang_decl_raw		(tree, bool decomp_p);
+extern bool maybe_add_lang_type_raw		(tree);
 extern void retrofit_lang_decl			(tree);
 extern void fit_decomposition_lang_decl		(tree, tree);
+extern void fit_ptrmem_type_decl		(tree, tree);
 extern tree copy_decl				(tree CXX_MEM_STAT_INFO);
 extern tree copy_type				(tree CXX_MEM_STAT_INFO);
 extern tree cxx_make_type			(enum tree_code CXX_MEM_STAT_INFO);
@@ -6793,6 +7089,7 @@ extern void maybe_show_extern_c_location (void);
 extern bool literal_integer_zerop (const_tree);
 
 /* in pt.c */
+extern tree canonical_type_parameter		(tree);
 extern void push_access_scope			(tree);
 extern void pop_access_scope			(tree);
 extern bool check_template_shadow		(tree);
@@ -6986,6 +7293,7 @@ extern GTY(()) vec<tree, va_gc> *unemitted_tinfo_decls;
 
 extern void init_rtti_processing		(void);
 extern tree build_typeid			(tree, tsubst_flags_t);
+extern tree get_tinfo_decl_direct	        (tree, tree, int);
 extern tree get_tinfo_decl			(tree);
 extern tree get_typeid				(tree, tsubst_flags_t);
 extern tree build_headof			(tree);
@@ -6993,6 +7301,8 @@ extern tree build_dynamic_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern void emit_support_tinfos			(void);
 extern bool emit_tinfo_decl			(tree);
+extern unsigned get_pseudo_tinfo_index		(tree);
+extern tree get_pseudo_tinfo_type		(unsigned);
 
 /* in search.c */
 extern bool accessible_base_p			(tree, tree, bool);
@@ -7340,7 +7650,7 @@ extern bool is_local_temp			(tree);
 extern tree build_aggr_init_expr		(tree, tree);
 extern tree get_target_expr			(tree);
 extern tree get_target_expr_sfinae		(tree, tsubst_flags_t);
-extern tree build_cplus_array_type		(tree, tree);
+extern tree build_cplus_array_type		(tree, tree, int is_dep = -1);
 extern tree build_array_of_n_type		(tree, int);
 extern bool array_of_runtime_bound_p		(tree);
 extern bool vla_type_p				(tree);
diff --git c/gcc/cp/lex.c w/gcc/cp/lex.c
index 8a69bc4f170..013cbadf625 100644
--- c/gcc/cp/lex.c
+++ w/gcc/cp/lex.c
@@ -678,7 +880,7 @@ build_lang_decl_loc (location_t loc, enum tree_code code, tree name, tree type)
 /* Maybe add a raw lang_decl to T, a decl.  Return true if it needed
    one.  */
 
-static bool
+bool
 maybe_add_lang_decl_raw (tree t, bool decomp_p)
 {
   size_t size;
@@ -803,6 +1005,9 @@ cxx_dup_lang_specific_decl (tree node)
   struct lang_decl *ld = (struct lang_decl *) ggc_internal_alloc (size);
   memcpy (ld, DECL_LANG_SPECIFIC (node), size);
   DECL_LANG_SPECIFIC (node) = ld;
+  DECL_MODULE_ENTITY_P (node) = false;
+  DECL_MODULE_IMPORT_P (node) = false;
+  DECL_ATTACHED_DECLS_P (node) = false;
 
   if (GATHER_STATISTICS)
     {
@@ -831,8 +1036,7 @@ copy_lang_type (tree node)
   if (! TYPE_LANG_SPECIFIC (node))
     return;
 
-  struct lang_type *lt
-    = (struct lang_type *) ggc_internal_alloc (sizeof (struct lang_type));
+  auto *lt = (struct lang_type *) ggc_internal_alloc (sizeof (struct lang_type));
 
   memcpy (lt, TYPE_LANG_SPECIFIC (node), (sizeof (struct lang_type)));
   TYPE_LANG_SPECIFIC (node) = lt;
@@ -858,15 +1062,15 @@ copy_type (tree type MEM_STAT_DECL)
 
 /* Add a raw lang_type to T, a type, should it need one.  */
 
-static bool
+bool
 maybe_add_lang_type_raw (tree t)
 {
   if (!RECORD_OR_UNION_CODE_P (TREE_CODE (t)))
     return false;
   
-  TYPE_LANG_SPECIFIC (t)
-    = (struct lang_type *) (ggc_internal_cleared_alloc
-			    (sizeof (struct lang_type)));
+  auto *lt = (struct lang_type *) (ggc_internal_cleared_alloc
+				   (sizeof (struct lang_type)));
+  TYPE_LANG_SPECIFIC (t) = lt;
 
   if (GATHER_STATISTICS)
     {
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index aa162d2a4f9..497ac5aafec 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -1709,9 +1708,11 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
   return spec;
 }
 
-/* Returns true iff two spec_entry nodes are equivalent.  */
-
+/* Restricts tree and type comparisons.  */
 int comparing_specializations;
+int comparing_typenames;
+
+/* Returns true iff two spec_entry nodes are equivalent.  */
 
 bool
 spec_hasher::equal (spec_entry *e1, spec_entry *e2)
@@ -1719,6 +1720,7 @@ spec_hasher::equal (spec_entry *e1, spec_entry *e2)
   int equal;
 
   ++comparing_specializations;
+  ++comparing_typenames;
   equal = (e1->tmpl == e2->tmpl
 	   && comp_template_args (e1->args, e2->args));
   if (equal && flag_concepts
@@ -1734,6 +1736,7 @@ spec_hasher::equal (spec_entry *e1, spec_entry *e2)
       equal = equivalent_constraints (c1, c2);
     }
   --comparing_specializations;
+  --comparing_typenames;
 
   return equal;
 }
@@ -4435,7 +4438,7 @@ build_template_parm_index (int index,
    parameter.  Returns the canonical type parameter, which may be TYPE
    if no such parameter existed.  */
 
-static tree
+tree
 canonical_type_parameter (tree type)
 {
   int idx = TEMPLATE_TYPE_IDX (type);
@@ -13205,19 +13244,24 @@ tsubst_argument_pack (tree orig_arg, tree args, tsubst_flags_t complain,
 		      tree in_decl)
 {
   /* Substitute into each of the arguments.  */
-  tree new_arg = TYPE_P (orig_arg)
-    ? cxx_make_type (TREE_CODE (orig_arg))
-    : make_node (TREE_CODE (orig_arg));
-
   tree pack_args = tsubst_template_args (ARGUMENT_PACK_ARGS (orig_arg),
 					 args, complain, in_decl);
-  if (pack_args == error_mark_node)
-    new_arg = error_mark_node;
-  else
-    SET_ARGUMENT_PACK_ARGS (new_arg, pack_args);
+  tree new_arg = error_mark_node;
+  if (pack_args != error_mark_node)
+    {
+      if (TYPE_P (orig_arg))
+	{
+	  new_arg = cxx_make_type (TREE_CODE (orig_arg));
+	  SET_TYPE_STRUCTURAL_EQUALITY (new_arg);
+	}
+      else
+	{
+	  new_arg = make_node (TREE_CODE (orig_arg));
+	  TREE_CONSTANT (new_arg) = TREE_CONSTANT (orig_arg);
+	}
 
-  if (TREE_CODE (new_arg) == NONTYPE_ARGUMENT_PACK)
-    TREE_CONSTANT (new_arg) = TREE_CONSTANT (orig_arg);
+      SET_ARGUMENT_PACK_ARGS (new_arg, pack_args);
+    }
 
   return new_arg;
 }
diff --git c/gcc/cp/typeck.c w/gcc/cp/typeck.c
index d3b701610cf..49b895349c8 100644
--- c/gcc/cp/typeck.c
+++ w/gcc/cp/typeck.c
@@ -1256,16 +1256,15 @@ structural_comptypes (tree t1, tree t2, int strict)
 
   gcc_assert (TYPE_P (t1) && TYPE_P (t2));
 
-  if (!comparing_specializations)
-    {
-      /* TYPENAME_TYPEs should be resolved if the qualifying scope is the
-	 current instantiation.  */
-      if (TREE_CODE (t1) == TYPENAME_TYPE)
-	t1 = resolve_typename_type (t1, /*only_current_p=*/true);
-
-      if (TREE_CODE (t2) == TYPENAME_TYPE)
-	t2 = resolve_typename_type (t2, /*only_current_p=*/true);
-    }
+  /* TYPENAME_TYPEs should be resolved if the qualifying scope is the
+     current instantiation, and we don't care about typename
+     structural equality.  The comparing_typenames check is after the
+     code check, in order to early-out the common case.  */
+  if (TREE_CODE (t1) == TYPENAME_TYPE && !comparing_typenames)
+    t1 = resolve_typename_type (t1, /*only_current_p=*/true);
+
+  if (TREE_CODE (t2) == TYPENAME_TYPE && !comparing_typenames)
+    t2 = resolve_typename_type (t2, /*only_current_p=*/true);
 
   if (TYPE_PTRMEMFUNC_P (t1))
     t1 = TYPE_PTRMEMFUNC_FN_TYPE (t1);
diff --git c/gcc/cp/rtti.c w/gcc/cp/rtti.c
index 887aae31bf6..0be9eff54ad 100644
--- c/gcc/cp/rtti.c
+++ w/gcc/cp/rtti.c
@@ -123,7 +123,6 @@ static GTY (()) vec<tinfo_s, va_gc> *tinfo_descs;
 
 static tree ifnonnull (tree, tree, tsubst_flags_t);
 static tree tinfo_name (tree, bool);
-static tree get_tinfo_decl_direct (tree type, tree name, int pseudo_ix);
 static tree build_dynamic_cast_1 (location_t, tree, tree, tsubst_flags_t);
 static tree throw_bad_cast (void);
 static tree throw_bad_typeid (void);
@@ -431,7 +434,7 @@ get_tinfo_decl (tree type)
 /* Get or create a tinfo VAR_DECL directly from the provided information.
    The caller must have already checked it is valid to do so.  */
 
-static tree
+tree
 get_tinfo_decl_direct (tree type, tree name, int pseudo_ix)
 {
   /* For a class type, the variable is cached in the type node
@@ -1491,6 +1495,36 @@ get_tinfo_desc (unsigned ix)
   return res;
 }
 
+/* Return an identifying index for the pseudo type_info TYPE.
+   We wrote the index at the end of the name, so just scan it from
+   there.  This isn't critical, as it's only on the first use of this
+   type during module stream out.  */
+
+unsigned
+get_pseudo_tinfo_index (tree type)
+{
+  tree name = DECL_NAME (TYPE_NAME (type));
+  unsigned ix = 0, scale = 1;
+  size_t len = IDENTIFIER_LENGTH (name);
+  const char *ptr = IDENTIFIER_POINTER (name) + len;
+
+  for (; *--ptr != '_'; scale *= 10)
+    {
+      len--;
+      gcc_checking_assert (len && ISDIGIT (*ptr));
+      ix += (*ptr - '0') * scale;
+    }
+
+  gcc_assert (len != IDENTIFIER_LENGTH (name));
+  return ix;
+}
+
+tree
+get_pseudo_tinfo_type (unsigned ix)
+{
+  return get_tinfo_desc (ix)->type;
+}
+
 /* We lazily create the type info types.  */
 
 static void
diff --git c/gcc/cp/tree.c w/gcc/cp/tree.c
index 3087c4ab52c..d125fa5e793 100644
--- c/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -998,7 +998,7 @@ build_min_array_type (tree elt_type, tree index_type)
    build_cplus_array_type.  */
 
 static void
-set_array_type_canon (tree t, tree elt_type, tree index_type)
+set_array_type_canon (tree t, tree elt_type, tree index_type, bool dep)
 {
   /* Set the canonical type for this new node.  */
   if (TYPE_STRUCTURAL_EQUALITY_P (elt_type)
@@ -1009,30 +1009,33 @@ set_array_type_canon (tree t, tree elt_type, tree index_type)
     TYPE_CANONICAL (t)
       = build_cplus_array_type (TYPE_CANONICAL (elt_type),
 				index_type
-				? TYPE_CANONICAL (index_type) : index_type);
+				? TYPE_CANONICAL (index_type) : index_type,
+				dep);
   else
     TYPE_CANONICAL (t) = t;
 }
 
 /* Like build_array_type, but handle special C++ semantics: an array of a
    variant element type is a variant of the array of the main variant of
-   the element type.  */
+   the element type.  IS_DEPENDENT is -ve if we should determine the
+   dependency.  Otherwise its bool value indicates dependency.  */
 
 tree
-build_cplus_array_type (tree elt_type, tree index_type)
+build_cplus_array_type (tree elt_type, tree index_type, int dependent)
 {
   tree t;
 
   if (elt_type == error_mark_node || index_type == error_mark_node)
     return error_mark_node;
 
-  bool dependent = (uses_template_parms (elt_type)
-		    || (index_type && uses_template_parms (index_type)));
+  if (dependent < 0)
+    dependent = (uses_template_parms (elt_type)
+		 || (index_type && uses_template_parms (index_type)));
 
   if (elt_type != TYPE_MAIN_VARIANT (elt_type))
     /* Start with an array of the TYPE_MAIN_VARIANT.  */
     t = build_cplus_array_type (TYPE_MAIN_VARIANT (elt_type),
-				index_type);
+				index_type, dependent);
   else if (dependent)
     {
       /* Since type_hash_canon calls layout_type, we need to use our own
@@ -1062,7 +1065,11 @@ build_cplus_array_type (tree elt_type, tree index_type)
 	  *e = t;
 
 	  /* Set the canonical type for this new node.  */
-	  set_array_type_canon (t, elt_type, index_type);
+	  set_array_type_canon (t, elt_type, index_type, dependent);
+
+	  /* Mark it as dependent now, this saves time later.  */
+	  TYPE_DEPENDENT_P_VALID (t) = true;
+	  TYPE_DEPENDENT_P (t) = true;
 	}
     }
   else
@@ -1083,7 +1090,7 @@ build_cplus_array_type (tree elt_type, tree index_type)
       if (!t)
 	{
 	  t = build_min_array_type (elt_type, index_type);
-	  set_array_type_canon (t, elt_type, index_type);
+	  set_array_type_canon (t, elt_type, index_type, dependent);
 	  if (!dependent)
 	    {
 	      layout_type (t);
@@ -1319,7 +1326,8 @@ cp_build_qualified_type_real (tree type,
 
       if (!t)
 	{
-	  t = build_cplus_array_type (element_type, TYPE_DOMAIN (type));
+	  t = build_cplus_array_type (element_type, TYPE_DOMAIN (type),
+				      TYPE_DEPENDENT_P (type));
 
 	  /* Keep the typedef name.  */
 	  if (TYPE_NAME (t) != TYPE_NAME (type))
@@ -1555,7 +1563,7 @@ strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
     case ARRAY_TYPE:
       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
-      result = build_cplus_array_type (type, t0);
+      result = build_cplus_array_type (type, t0, TYPE_DEPENDENT_P (t));
       break;
     case FUNCTION_TYPE:
     case METHOD_TYPE:


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

* [17/32] new C++ constexpr bits
       [not found]                             ` <d3149435-8b5e-7c79-f4bb-80238fdfcf72@acm.org>
@ 2020-11-03 21:15                               ` Nathan Sidwell
       [not found]                               ` <1ab99df5-3997-0895-c979-f8529f476df7@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:15 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

We need to load and save constexpr bodies.  This exposes that hash table 
from constexpr


-- 
Nathan Sidwell


[-- Attachment #2: 17-c++-infra-constexpr.diff --]
[-- Type: text/x-patch, Size: 6540 bytes --]

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -7862,9 +8177,20 @@ extern void vtv_recover_class_info              (void);
 extern void vtv_build_vtable_verify_fndecl      (void);
 
 /* In constexpr.c */
+/* Representation of entries in the constexpr function definition table.  */
+
+struct GTY((for_user)) constexpr_fundef {
+  tree decl;
+  tree body;
+  tree parms;
+  tree result;
+};
+
 extern void fini_constexpr			(void);
 extern bool literal_type_p                      (tree);
-extern tree register_constexpr_fundef           (tree, tree);
+extern tree check_constexpr_fundef           	(tree, tree);
+extern tree register_constexpr_fundef           (const constexpr_fundef &);
+extern constexpr_fundef *retrieve_constexpr_fundef		(tree);
 extern bool is_valid_constexpr_fn		(tree, bool);
 extern bool check_constexpr_ctor_body           (tree, tree, bool);
 extern tree constexpr_fn_retval		(tree);
diff --git c/gcc/cp/constexpr.c w/gcc/cp/constexpr.c
index b46824f128d..8e94162e0c0 100644
--- c/gcc/cp/constexpr.c
+++ w/gcc/cp/constexpr.c
@@ -133,19 +133,10 @@ ensure_literal_type_for_constexpr_object (tree decl)
   return decl;
 }
 
-/* Representation of entries in the constexpr function definition table.  */
-
-struct GTY((for_user)) constexpr_fundef {
-  tree decl;
-  tree body;
-  tree parms;
-  tree result;
-};
-
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
 {
-  static hashval_t hash (constexpr_fundef *);
-  static bool equal (constexpr_fundef *, constexpr_fundef *);
+  static hashval_t hash (const constexpr_fundef *);
+  static bool equal (const constexpr_fundef *, const constexpr_fundef *);
 };
 
 /* This table holds all constexpr function definitions seen in
@@ -158,7 +149,8 @@ static GTY (()) hash_table<constexpr_fundef_hasher> *constexpr_fundef_table;
    same constexpr function.  */
 
 inline bool
-constexpr_fundef_hasher::equal (constexpr_fundef *lhs, constexpr_fundef *rhs)
+constexpr_fundef_hasher::equal (const constexpr_fundef *lhs,
+				const constexpr_fundef *rhs)
 {
   return lhs->decl == rhs->decl;
 }
@@ -167,20 +159,20 @@ constexpr_fundef_hasher::equal (constexpr_fundef *lhs, constexpr_fundef *rhs)
    Return a hash value for the entry pointed to by Q.  */
 
 inline hashval_t
-constexpr_fundef_hasher::hash (constexpr_fundef *fundef)
+constexpr_fundef_hasher::hash (const constexpr_fundef *fundef)
 {
   return DECL_UID (fundef->decl);
 }
 
 /* Return a previously saved definition of function FUN.   */
 
-static constexpr_fundef *
+constexpr_fundef *
 retrieve_constexpr_fundef (tree fun)
 {
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
+  constexpr_fundef fundef = { fun, NULL_TREE, NULL_TREE, NULL_TREE };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -669,7 +661,7 @@ get_function_named_in_call (tree t)
   return fun;
 }
 
-/* Subroutine of register_constexpr_fundef.  BODY is the body of a function
+/* Subroutine of check_constexpr_fundef.  BODY is the body of a function
    declared to be constexpr, or a sub-statement thereof.  Returns the
    return value if suitable, error_mark_node for a statement not allowed in
    a constexpr function, or NULL_TREE if no return value was found.  */
@@ -738,7 +730,7 @@ constexpr_fn_retval (tree body)
     }
 }
 
-/* Subroutine of register_constexpr_fundef.  BODY is the DECL_SAVED_TREE of
+/* Subroutine of check_constexpr_fundef.  BODY is the DECL_SAVED_TREE of
    FUN; do the necessary transformations to turn it into a single expression
    that we can store in the hash table.  */
 
@@ -868,17 +860,14 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
 }
 
 /* We are processing the definition of the constexpr function FUN.
-   Check that its BODY fulfills the propriate requirements and
+   Check that its BODY fulfills the apropriate requirements and
    enter it in the constexpr function definition table.
    For constructor BODY is actually the TREE_LIST of the
    member-initializer list.  */
 
 tree
-register_constexpr_fundef (tree fun, tree body)
+check_constexpr_fundef (tree fun, tree body)
 {
-  constexpr_fundef entry;
-  constexpr_fundef **slot;
-
   if (!is_valid_constexpr_fn (fun, !DECL_GENERATED_P (fun)))
     return NULL;
 
@@ -903,37 +892,47 @@ register_constexpr_fundef (tree fun, tree body)
   if (!potential && !DECL_GENERATED_P (fun))
     return NULL;
 
-  /* Create the constexpr function table if necessary.  */
-  if (constexpr_fundef_table == NULL)
-    constexpr_fundef_table
-      = hash_table<constexpr_fundef_hasher>::create_ggc (101);
-
-  entry.decl = fun;
-  tree saved_fn = current_function_decl;
+  constexpr_fundef entry = {fun, NULL_TREE, NULL_TREE, NULL_TREE};
   bool clear_ctx = false;
-  current_function_decl = fun;
   if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
     {
       clear_ctx = true;
       DECL_CONTEXT (DECL_RESULT (fun)) = fun;
     }
-  entry.body = copy_fn (fun, entry.parms, entry.result);
+  tree saved_fn = current_function_decl;
+  current_function_decl = fun;
+  entry.body = copy_fn (entry.decl, entry.parms, entry.result);
   current_function_decl = saved_fn;
-  slot = constexpr_fundef_table->find_slot (&entry, INSERT);
   if (clear_ctx)
-    DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
-
+    DECL_CONTEXT (DECL_RESULT (entry.decl)) = NULL_TREE;
   if (!potential)
     /* For a template instantiation, we want to remember the pre-generic body
        for explain_invalid_constexpr_fn, but do tell cxx_eval_call_expression
        that it doesn't need to bother trying to expand the function.  */
     entry.result = error_mark_node;
 
+  return register_constexpr_fundef (entry);
+}
+
+/* BODY is a validated and massaged definition of a constexpr
+   function.  Register it in the hash table.  */
+
+tree
+register_constexpr_fundef (const constexpr_fundef &value)
+{
+  /* Create the constexpr function table if necessary.  */
+  if (constexpr_fundef_table == NULL)
+    constexpr_fundef_table
+      = hash_table<constexpr_fundef_hasher>::create_ggc (101);
+
+  constexpr_fundef **slot = constexpr_fundef_table->find_slot
+    (const_cast<constexpr_fundef *> (&value), INSERT);
+
   gcc_assert (*slot == NULL);
   *slot = ggc_alloc<constexpr_fundef> ();
-  **slot = entry;
+  **slot = value;
 
-  return fun;
+  return value.decl;
 }
 
 /* FUN is a non-constexpr function called in a context that requires a


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

* [18/32] new C++ template bits
       [not found]                               ` <1ab99df5-3997-0895-c979-f8529f476df7@acm.org>
@ 2020-11-03 21:16                                 ` Nathan Sidwell
       [not found]                                 ` <97e9477b-7173-b7f9-a884-616b972c57ba@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:16 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

We also need to manipulate the instantiation tables.  This exposes that 
bit of the template machinery.  (other patches address instantiations 
themselves)

-- 
Nathan Sidwell


[-- Attachment #2: 18-c++-infra-template.diff --]
[-- Type: text/x-patch, Size: 4860 bytes --]

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -5386,6 +5563,14 @@ public:
   hash_map<tree, tree> *saved;
 };
 
+/* Entry in the specialization hash table.  */
+struct GTY((for_user)) spec_entry
+{
+  tree tmpl;  /* The general template this is a specialization of.  */
+  tree args;  /* The args for this (maybe-partial) specialization.  */
+  tree spec;  /* The specialization itself.  */
+};
+
 /* in class.c */
 
 extern int current_class_depth;
@@ -6976,6 +7273,16 @@ extern bool copy_guide_p			(const_tree);
 extern bool template_guide_p			(const_tree);
 extern bool builtin_guide_p			(const_tree);
 extern void store_explicit_specifier		(tree, tree);
+extern void walk_specializations		(bool,
+						 void (*)(bool, spec_entry *,
+							  void *),
+						 void *);
+extern tree check_mergeable_specialization	(bool, spec_entry *);
+extern tree match_mergeable_specialization	(bool is_decl, tree tmpl,
+						 tree args, tree spec);
+extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec);
+extern void add_mergeable_specialization        (tree tmpl, tree args,
+						 tree spec, unsigned);
 extern tree add_outermost_template_args		(tree, tree);
 extern tree add_extra_args			(tree, tree);
 extern tree build_extra_args			(tree, tree, tsubst_flags_t);
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index aa162d2a4f9..497ac5aafec 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -103,13 +106,6 @@ local_specialization_stack::~local_specialization_stack ()
 /* True if we've recursed into fn_type_unification too many times.  */
 static bool excessive_deduction_depth;
 
-struct GTY((for_user)) spec_entry
-{
-  tree tmpl;
-  tree args;
-  tree spec;
-};
-
 struct spec_hasher : ggc_ptr_hash<spec_entry>
 {
   static hashval_t hash (spec_entry *);
@@ -29613,6 +29695,104 @@ declare_integer_pack (void)
 			      CP_BUILT_IN_INTEGER_PACK);
 }
 
+/* Collect the specializations and explicit instantitions generated
+   in this module  */
+
+void
+walk_specializations (bool decls_p,
+		      void (*fn) (bool decls_p, spec_entry *entry, void *data),
+		      void *data)
+{
+  spec_hash_table *table = decls_p ? decl_specializations
+    : type_specializations;
+  spec_hash_table::iterator end (table->end ());
+  for (spec_hash_table::iterator iter (table->begin ()); iter != end; ++iter)
+    fn (decls_p, *iter, data);
+}
+
+tree
+check_mergeable_specialization (bool decl_p, spec_entry *elt)
+{
+  hash_table<spec_hasher> *specializations
+    = decl_p ? decl_specializations : type_specializations;
+  hashval_t hash = spec_hasher::hash (elt);
+  spec_entry **slot = specializations->find_slot_with_hash (elt,
+							    hash, NO_INSERT);
+  return slot ? (*slot)->spec : NULL_TREE;
+}
+
+/* Lookup the specialization of TMPL,ARGS in the decl or type
+   specialization table.  Return what's there, or add SPEC and return
+   NULL.  */
+
+tree
+match_mergeable_specialization (bool decl_p, tree tmpl, tree args, tree spec)
+{
+  gcc_checking_assert (spec);
+  spec_entry elt = {tmpl, args, spec};
+  hash_table<spec_hasher> *specializations
+    = decl_p ? decl_specializations : type_specializations;
+  hashval_t hash = spec_hasher::hash (&elt);
+  spec_entry **slot = specializations->find_slot_with_hash (&elt, hash, INSERT);
+  spec_entry *entry = slot ? *slot: NULL;
+  
+  if (entry)
+    return entry->spec;
+
+  entry = ggc_alloc<spec_entry> ();
+  *entry = elt;
+  *slot = entry;
+
+  return NULL_TREE;
+}
+
+/* Return flags encoding whether SPEC is on the instantiation and/or
+   specialization lists of TMPL.  */
+
+unsigned
+get_mergeable_specialization_flags (tree tmpl, tree decl)
+{
+  unsigned flags = 0;
+
+  for (tree inst = DECL_TEMPLATE_INSTANTIATIONS (tmpl);
+       inst; inst = TREE_CHAIN (inst))
+    if (TREE_VALUE (inst) == decl)
+      {
+	flags |= 1;
+	break;
+      }
+
+  if (CLASS_TYPE_P (TREE_TYPE (decl))
+      && CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl))
+      && CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl)) == 2)
+    /* Only need to search if DECL is a partial specialization.  */
+    for (tree part = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
+	 part; part = TREE_CHAIN (part))
+      if (TREE_VALUE (part) == decl)
+	{
+	  flags |= 2;
+	  break;
+	}
+
+  return flags;
+}
+
+void
+add_mergeable_specialization (tree tmpl, tree args, tree decl, unsigned flags)
+{
+  if (flags & 1)
+    DECL_TEMPLATE_INSTANTIATIONS (tmpl)
+      = tree_cons (args, decl, DECL_TEMPLATE_INSTANTIATIONS (tmpl));
+
+  if (flags & 2)
+    {
+      DECL_TEMPLATE_SPECIALIZATIONS (tmpl)
+	= tree_cons (args, decl, DECL_TEMPLATE_SPECIALIZATIONS (tmpl));
+      TREE_TYPE (DECL_TEMPLATE_SPECIALIZATIONS (tmpl))
+	= TREE_TYPE (DECL_TEMPLATE_RESULT (decl));
+    }
+}
+
 /* Set up the hash tables for template instantiations.  */
 
 void


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

* [19/32] global trees
       [not found]                                 ` <97e9477b-7173-b7f9-a884-616b972c57ba@acm.org>
@ 2020-11-03 21:16                                   ` Nathan Sidwell
  2020-11-06 20:29                                     ` Jeff Law
       [not found]                                   ` <01f091a5-cd8b-60b6-9552-2318ecd07025@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:16 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

We directly reference global trees, and expect them to be immutable. 
This reorders the global tree arrays, moving the mutable ones after a 
High Water Mark.  Those after the HWM are not directly accessed in the 
module machinery (we'll reference by name or equivalent).


-- 
Nathan Sidwell


[-- Attachment #2: 19-global-trees.diff --]
[-- Type: text/x-patch, Size: 2065 bytes --]

diff --git c/gcc/c-family/c-common.h w/gcc/c-family/c-common.h
index 18b489d55a3..eb9070b4c6c 100644
--- c/gcc/c-family/c-common.h
+++ w/gcc/c-family/c-common.h
@@ -357,13 +360,17 @@ enum c_tree_index
 
     CTI_DEFAULT_FUNCTION_TYPE,
 
+    CTI_NULL,
+
     /* These are not types, but we have to look them up all the time.  */
     CTI_FUNCTION_NAME_DECL,
     CTI_PRETTY_FUNCTION_NAME_DECL,
     CTI_C99_FUNCTION_NAME_DECL,
-    CTI_SAVED_FUNCTION_NAME_DECLS,
 
-    CTI_NULL,
+    CTI_MODULE_HWM,
+    /* Below here entities change during compilation.  */
+
+    CTI_SAVED_FUNCTION_NAME_DECLS,
 
     CTI_MAX
 };
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -127,12 +127,8 @@ enum cp_tree_index
     CPTI_INIT_LIST_TYPE,
     CPTI_VTBL_TYPE,
     CPTI_VTBL_PTR_TYPE,
-    CPTI_STD,
-    CPTI_ABI,
     CPTI_GLOBAL,
     CPTI_GLOBAL_TYPE,
-    CPTI_CONST_TYPE_INFO_TYPE,
-    CPTI_TYPE_INFO_PTR_TYPE,
     CPTI_ABORT_FNDECL,
     CPTI_AGGR_TAG,
     CPTI_CONV_OP_MARKER,
@@ -189,8 +185,28 @@ enum cp_tree_index
     CPTI_NOEXCEPT_FALSE_SPEC,
     CPTI_NOEXCEPT_DEFERRED_SPEC,
 
+    CPTI_NULLPTR,
+    CPTI_NULLPTR_TYPE,
+
+    CPTI_ANY_TARG,
+
+    CPTI_MODULE_HWM,
+    /* Nodes after here change during compilation, or should not be in
+       the module's global tree table.  */
+
+    /* We must find these via the global namespace.  */
+    CPTI_STD,
+    CPTI_ABI,
+
+    /* These are created at init time, but the library/headers provide
+       definitions.  */
+    CPTI_ALIGN_TYPE,
+    CPTI_CONST_TYPE_INFO_TYPE,
+    CPTI_TYPE_INFO_PTR_TYPE,
     CPTI_TERMINATE_FN,
     CPTI_CALL_UNEXPECTED_FN,
+
+    /* These are lazily inited.  */
     CPTI_GET_EXCEPTION_PTR_FN,
     CPTI_BEGIN_CATCH_FN,
     CPTI_END_CATCH_FN,
@@ -203,13 +219,6 @@ enum cp_tree_index
     CPTI_DSO_HANDLE,
     CPTI_DCAST,
 
-    CPTI_NULLPTR,
-    CPTI_NULLPTR_TYPE,
-
-    CPTI_ALIGN_TYPE,
-
-    CPTI_ANY_TARG,
-
     CPTI_SOURCE_LOCATION_IMPL,
 
     CPTI_FALLBACK_DFLOAT32_TYPE,


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

* [20/32] global constructor
       [not found]                                   ` <01f091a5-cd8b-60b6-9552-2318ecd07025@acm.org>
@ 2020-11-03 21:16                                     ` Nathan Sidwell
       [not found]                                     ` <a70c9177-136e-0d52-1c96-c8093588f57b@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:16 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

the order of global construction is well defined in a module import 
graph.  Each module has a globally callable idempotent constructor 
function.  Every importer calls the ctors of its imports (I optimize 
that graph walk to avoid calling functions known to be called indirectly.)

These are the changes to the global ctor emission.

-- 
Nathan Sidwell


[-- Attachment #2: 20-c++-dynctor.diff --]
[-- Type: text/x-patch, Size: 5858 bytes --]

diff --git c/gcc/cp/decl2.c w/gcc/cp/decl2.c
index 71107e03010..60bc6444d7c 100644
--- c/gcc/cp/decl2.c
+++ w/gcc/cp/decl2.c
@@ -3636,35 +3636,45 @@ generate_tls_wrapper (tree fn)
 static tree
 start_objects (int method_type, int initp)
 {
-  tree body;
-  tree fndecl;
-  char type[14];
-
   /* Make ctor or dtor function.  METHOD_TYPE may be 'I' or 'D'.  */
+  int module_init = 0;
 
-  if (initp != DEFAULT_INIT_PRIORITY)
+  if (initp == DEFAULT_INIT_PRIORITY && method_type == 'I')
+    module_init = module_initializer_kind ();
+
+  tree name = NULL_TREE;
+  if (module_init > 0)
+    name = mangle_module_global_init (0);
+  else
     {
-      char joiner;
+      char type[14];
 
+      unsigned len = sprintf (type, "sub_%c", method_type);
+      if (initp != DEFAULT_INIT_PRIORITY)
+	{
+	  char joiner = '_';
 #ifdef JOINER
-      joiner = JOINER;
-#else
-      joiner = '_';
+	  joiner = JOINER;
 #endif
+	  type[len++] = joiner;
+	  sprintf (type + len, "%.5u", initp);
+	}
+      name = get_file_function_name (type);
+    }
 
-      sprintf (type, "sub_%c%c%.5u", method_type, joiner, initp);
+  tree fntype =	build_function_type (void_type_node, void_list_node);
+  tree fndecl = build_lang_decl (FUNCTION_DECL, name, fntype);
+  DECL_CONTEXT (fndecl) = FROB_CONTEXT (global_namespace);
+  if (module_init > 0)
+    {
+      SET_DECL_ASSEMBLER_NAME (fndecl, name);
+      TREE_PUBLIC (fndecl) = true;
+      determine_visibility (fndecl);
     }
   else
-    sprintf (type, "sub_%c", method_type);
-
-  fndecl = build_lang_decl (FUNCTION_DECL,
-			    get_file_function_name (type),
-			    build_function_type_list (void_type_node,
-						      NULL_TREE));
+    TREE_PUBLIC (fndecl) = 0;
   start_preparsed_function (fndecl, /*attrs=*/NULL_TREE, SF_PRE_PARSED);
 
-  TREE_PUBLIC (current_function_decl) = 0;
-
   /* Mark as artificial because it's not explicitly in the user's
      source code.  */
   DECL_ARTIFICIAL (current_function_decl) = 1;
@@ -3678,7 +3688,35 @@ start_objects (int method_type, int initp)
   else
     DECL_GLOBAL_DTOR_P (current_function_decl) = 1;
 
-  body = begin_compound_stmt (BCS_FN_BODY);
+  tree body = begin_compound_stmt (BCS_FN_BODY);
+
+  if (module_init > 0)
+    {
+      // 'static bool __in_chrg = false;
+      // if (__inchrg) return;
+      // __inchrg = true
+      tree var = build_lang_decl (VAR_DECL, in_charge_identifier,
+				  boolean_type_node);
+      DECL_CONTEXT (var) = fndecl;
+      DECL_ARTIFICIAL (var) = true;
+      TREE_STATIC (var) = true;
+      pushdecl (var);
+      cp_finish_decl (var, NULL_TREE, false, NULL_TREE, 0);
+
+      tree if_stmt = begin_if_stmt ();
+      finish_if_stmt_cond (var, if_stmt);
+      finish_return_stmt (NULL_TREE);
+      finish_then_clause (if_stmt);
+      finish_if_stmt (if_stmt);
+
+      tree assign = build2 (MODIFY_EXPR, boolean_type_node,
+			    var, boolean_true_node);
+      TREE_SIDE_EFFECTS (assign) = true;
+      finish_expr_stmt (assign);
+    }
+
+  if (module_init)
+    module_add_import_initializers ();
 
   return body;
 }
@@ -3689,11 +3727,9 @@ start_objects (int method_type, int initp)
 static void
 finish_objects (int method_type, int initp, tree body)
 {
-  tree fn;
-
   /* Finish up.  */
   finish_compound_stmt (body);
-  fn = finish_function (/*inline_p=*/false);
+  tree fn = finish_function (/*inline_p=*/false);
 
   if (method_type == 'I')
     {
@@ -4228,50 +4264,50 @@ static void
 generate_ctor_or_dtor_function (bool constructor_p, int priority,
 				location_t *locus)
 {
-  char function_key;
-  tree fndecl;
-  tree body;
-  size_t i;
-
   input_location = *locus;
-  /* ??? */
-  /* Was: locus->line++; */
 
   /* We use `I' to indicate initialization and `D' to indicate
      destruction.  */
-  function_key = constructor_p ? 'I' : 'D';
+  char function_key = constructor_p ? 'I' : 'D';
 
   /* We emit the function lazily, to avoid generating empty
      global constructors and destructors.  */
-  body = NULL_TREE;
+  tree body = NULL_TREE;
 
-  /* For Objective-C++, we may need to initialize metadata found in this module.
-     This must be done _before_ any other static initializations.  */
-  if (c_dialect_objc () && (priority == DEFAULT_INIT_PRIORITY)
-      && constructor_p && objc_static_init_needed_p ())
+  if (constructor_p && priority == DEFAULT_INIT_PRIORITY)
     {
-      body = start_objects (function_key, priority);
-      objc_generate_static_init_call (NULL_TREE);
+      bool objc = c_dialect_objc () && objc_static_init_needed_p ();
+
+      /* We may have module initialization to emit and/or insert
+	 before other intializations.  */
+      if (module_initializer_kind () || objc)
+	body = start_objects (function_key, priority);
+
+      /* For Objective-C++, we may need to initialize metadata found
+         in this module.  This must be done _before_ any other static
+         initializations.  */
+      if (objc)
+	objc_generate_static_init_call (NULL_TREE);
     }
 
   /* Call the static storage duration function with appropriate
      arguments.  */
+  tree fndecl;
+  size_t i;
   FOR_EACH_VEC_SAFE_ELT (ssdf_decls, i, fndecl)
     {
       /* Calls to pure or const functions will expand to nothing.  */
       if (! (flags_from_decl_or_type (fndecl) & (ECF_CONST | ECF_PURE)))
 	{
-	  tree call;
-
 	  if (! body)
 	    body = start_objects (function_key, priority);
 
-	  call = cp_build_function_call_nary (fndecl, tf_warning_or_error,
-					      build_int_cst (NULL_TREE,
-							     constructor_p),
-					      build_int_cst (NULL_TREE,
-							     priority),
-					      NULL_TREE);
+	  tree call = cp_build_function_call_nary (fndecl, tf_warning_or_error,
+						   build_int_cst (NULL_TREE,
+								  constructor_p),
+						   build_int_cst (NULL_TREE,
+								  priority),
+						   NULL_TREE);
 	  finish_expr_stmt (call);
 	}
     }


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

* [21/32] miscelaneous
       [not found]                                     ` <a70c9177-136e-0d52-1c96-c8093588f57b@acm.org>
@ 2020-11-03 21:16                                       ` Nathan Sidwell
  2020-11-05 13:30                                         ` Richard Biener
       [not found]                                       ` <5c533ebe-440d-188e-5bdb-38c14898852c@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:16 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

These are changes to gcc/tree.h adding some raw accessors to nodes, 
which seemed preferable to direct field access.  I also needed access to 
the integral constant cache


-- 
Nathan Sidwell


[-- Attachment #2: 21-core-raw.diff --]
[-- Type: text/x-patch, Size: 3766 bytes --]

diff --git c/gcc/tree.h w/gcc/tree.h
index 7f0aa5b8d1d..13062f52919 100644
--- c/gcc/tree.h
+++ w/gcc/tree.h
@@ -2521,25 +2521,28 @@ extern tree vector_element_bits_tree (const_tree);
 #define DECL_SIZE(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.size)
 /* Likewise for the size in bytes.  */
 #define DECL_SIZE_UNIT(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.size_unit)
+#define DECL_ALIGN_RAW(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.align)
 /* Returns the alignment required for the datum, in bits.  It must
    be a power of two, but an "alignment" of zero is supported
    (e.g. as "uninitialized" sentinel).  */
-#define DECL_ALIGN(NODE) \
-    (DECL_COMMON_CHECK (NODE)->decl_common.align \
-     ? ((unsigned)1) << ((NODE)->decl_common.align - 1) : 0)
+#define DECL_ALIGN(NODE)					\
+  (DECL_ALIGN_RAW (NODE)					\
+   ? ((unsigned)1) << (DECL_ALIGN_RAW (NODE) - 1) : 0)
 /* Specify that DECL_ALIGN(NODE) is X.  */
 #define SET_DECL_ALIGN(NODE, X) \
-    (DECL_COMMON_CHECK (NODE)->decl_common.align = ffs_hwi (X))
+  (DECL_ALIGN_RAW (NODE) = ffs_hwi (X))
 
 /* The minimum alignment necessary for the datum, in bits, without
    warning.  */
-#define DECL_WARN_IF_NOT_ALIGN(NODE) \
-    (DECL_COMMON_CHECK (NODE)->decl_common.warn_if_not_align \
-     ? ((unsigned)1) << ((NODE)->decl_common.warn_if_not_align - 1) : 0)
+#define DECL_WARN_IF_NOT_ALIGN_RAW(NODE)			\
+  (DECL_COMMON_CHECK (NODE)->decl_common.warn_if_not_align)
+#define DECL_WARN_IF_NOT_ALIGN(NODE)					\
+  (DECL_WARN_IF_NOT_ALIGN_RAW (NODE)					\
+   ? ((unsigned)1) << (DECL_WARN_IF_NOT_ALIGN_RAW (NODE) - 1) : 0)
 
 /* Specify that DECL_WARN_IF_NOT_ALIGN(NODE) is X.  */
-#define SET_DECL_WARN_IF_NOT_ALIGN(NODE, X) \
-    (DECL_COMMON_CHECK (NODE)->decl_common.warn_if_not_align = ffs_hwi (X))
+#define SET_DECL_WARN_IF_NOT_ALIGN(NODE, X)		\
+  (DECL_WARN_IF_NOT_ALIGN_RAW (NODE) = ffs_hwi (X))
 
 /* The alignment of NODE, in bytes.  */
 #define DECL_ALIGN_UNIT(NODE) (DECL_ALIGN (NODE) / BITS_PER_UNIT)
@@ -5118,7 +5121,7 @@ extern const_tree strip_invariant_refs (const_tree);
 extern tree lhd_gcc_personality (void);
 extern void assign_assembler_name_if_needed (tree);
 extern bool warn_deprecated_use (tree, tree);
-extern void cache_integer_cst (tree);
+extern tree cache_integer_cst (tree, bool small = false);
 extern const char *combined_fn_name (combined_fn);
 
 /* Compare and hash for any structure which begins with a canonical
diff --git c/gcc/tree.c w/gcc/tree.c
index 9260772b846..2656a804ea2 100644
--- c/gcc/tree.c
+++ w/gcc/tree.c
@@ -1727,8 +1727,8 @@ wide_int_to_tree (tree type, const poly_wide_int_ref &value)
   return build_poly_int_cst (type, value);
 }
 
-void
-cache_integer_cst (tree t)
+tree
+cache_integer_cst (tree t, bool replace)
 {
   tree type = TREE_TYPE (t);
   int ix = -1;
@@ -1822,8 +1822,13 @@ cache_integer_cst (tree t)
 	  TYPE_CACHED_VALUES (type) = make_tree_vec (limit);
 	}
 
-      gcc_assert (TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) == NULL_TREE);
-      TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) = t;
+      if (tree r = TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix))
+	{
+	  gcc_assert (replace);
+	  t = r;
+	}
+      else
+	TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) = t;
     }
   else
     {
@@ -1831,12 +1836,18 @@ cache_integer_cst (tree t)
       tree *slot = int_cst_hash_table->find_slot (t, INSERT);
       /* If there is already an entry for the number verify it's the
          same.  */
-      if (*slot)
-	gcc_assert (wi::to_wide (tree (*slot)) == wi::to_wide (t));
+      if (tree r = *slot)
+	{
+	  gcc_assert (wi::to_wide (tree (r)) == wi::to_wide (t));
+	  if (replace)
+	    t = r;
+	}
       else
 	/* Otherwise insert this one into the hash table.  */
 	*slot = t;
     }
+
+  return t;
 }
 
 


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

* [22/32] miscelaneous c++ bits
       [not found]                                       ` <5c533ebe-440d-188e-5bdb-38c14898852c@acm.org>
@ 2020-11-03 21:16                                         ` Nathan Sidwell
  2020-11-13 13:41                                           ` Nathan Sidwell
  2020-11-13 14:03                                           ` [22.2/32] module flags Nathan Sidwell
       [not found]                                         ` <9e6ec23c-6b36-de70-7630-55562583696f@acm.org>
  1 sibling, 2 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:16 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

This is probably the messiest diff.

It's the remaining diff of cp-tree.h.
1) a new tree type MODULE_VECTOR.  This is a sparse array used for name 
lookup.  A namespace symbol table entry may contain one of these, which 
holds the bindings for each loaded module.  If a module doesn't bind 
that name, there'll be no slot for it.  The current TU always uses slot 
0.  The Global Module uses slot 1, and in a named-module partitions 
share slot 2.  Slots 1 & 2 are used for duplicate declaration matching.

I steal more flags from lang-decl-base.  Firstly, the selector is 
reduced from 16 to 3 bits.  We add a bunch of module-related flags

module_purview_p -- the declaration is owned by a module
module_import_p -- the declaration is from an import
module_entity_p -- the declaration is in the imported entity hash & array

module_pending_specializations_p - in a template we know there are 
specializations we've not loaded

module_pending_members_p - in a class we know there are members to be loaded

attached_decls_p -- this decl is in the attached-decls hash, and 
therefore has attached decls (to do with mangling lambdas initializing 
namespace-scope maybe-template variables)

It sprinkles set_originating_module and set_instantiating_module around 
a few places.  The originating module of a decl is the module that 
declared the decl.  The instantiating module is the module that caused 
code to be emitted for it.  Often these two modules are the same, but 
for templates they can of course be different.


-- 
Nathan Sidwell


[-- Attachment #2: 22-c++-other-bits.diff --]
[-- Type: text/x-patch, Size: 56979 bytes --]

diff --git c/gcc/cp/cp-tree.def w/gcc/cp/cp-tree.def
index a188576013b..ba02cdc2748 100644
--- c/gcc/cp/cp-tree.def
+++ w/gcc/cp/cp-tree.def
@@ -233,6 +233,9 @@ DEFTREECODE (TEMPLATE_ID_EXPR, "template_id_expr", tcc_expression, 2)
 /* One of a set of overloaded functions.  */
 DEFTREECODE (OVERLOAD, "overload", tcc_exceptional, 0)
 
+/* A vector of module overloads.  */
+DEFTREECODE (MODULE_VECTOR, "module_vector", tcc_exceptional, 0)
+
 /* A pseudo-destructor, of the form "OBJECT.~DESTRUCTOR" or
    "OBJECT.SCOPE::~DESTRUCTOR.  The first operand is the OBJECT.  The
    second operand (if non-NULL) is the SCOPE.  The third operand is
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index fdb8ee57f0b..e8e4d0af2d8 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -475,20 +484,22 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
       DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE)
       CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR)
+      DECL_MODULE_EXPORT_P (in _DECL)
       OVL_NESTED_P (in OVERLOAD)
       LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR)
       Reserved for DECL_MODULE_EXPORT (in DECL_)
    4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
       TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
 	  CALL_EXPR, or FIELD_DECL).
-      DECL_TINFO_P (in VAR_DECL)
+      DECL_TINFO_P (in VAR_DECL, TYPE_DECL)
       FUNCTION_REF_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       OVL_LOOKUP_P (in OVERLOAD)
-      LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, NAMESPACE_DECL)
+      LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
    5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
       CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
+      OVL_EXPORT_P (in OVL_USING_P OVERLOAD)
    6: TYPE_MARKED_P (in _TYPE)
       DECL_NONTRIVIALLY_INITIALIZED_P (in VAR_DECL)
       RANGE_FOR_IVDEP (in RANGE_FOR_STMT)
@@ -530,6 +541,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       DECL_ANON_UNION_VAR_P (in a VAR_DECL)
       DECL_SELF_REFERENCE_P (in a TYPE_DECL)
       DECL_INVALID_OVERRIDER_P (in a FUNCTION_DECL)
+      DECL_UNINSTANIATED_TEMPLATE_FRIEND_P (in TEMPLATE_DECL)
    5: DECL_INTERFACE_KNOWN.
    6: DECL_THIS_STATIC (in VAR_DECL, FUNCTION_DECL or PARM_DECL)
       DECL_FIELD_IS_BASE (in FIELD_DECL)
@@ -744,7 +756,8 @@ typedef struct ptrmem_cst * ptrmem_cst_t;
 /* Lookup walker marking.  */
 #define LOOKUP_SEEN_P(NODE) TREE_VISITED(NODE)
 #define LOOKUP_FOUND_P(NODE) \
-  TREE_LANG_FLAG_4 (TREE_CHECK3(NODE,RECORD_TYPE,UNION_TYPE,NAMESPACE_DECL))
+  TREE_LANG_FLAG_4 (TREE_CHECK4(NODE,RECORD_TYPE,UNION_TYPE,ENUMERAL_TYPE, \
+				NAMESPACE_DECL))
 
 /* These two accessors should only be used by OVL manipulators.
    Other users should use iterators and convenience functions.  */
@@ -763,6 +776,8 @@ typedef struct ptrmem_cst * ptrmem_cst_t;
 #define OVL_NESTED_P(NODE)	TREE_LANG_FLAG_3 (OVERLOAD_CHECK (NODE))
 /* If set, this overload was constructed during lookup.  */
 #define OVL_LOOKUP_P(NODE)	TREE_LANG_FLAG_4 (OVERLOAD_CHECK (NODE))
+/* If set, this OVL_USING_P overload is exported.  */
+#define OVL_EXPORT_P(NODE)	TREE_LANG_FLAG_5 (OVERLOAD_CHECK (NODE))
 
 /* The first decl of an overload.  */
 #define OVL_FIRST(NODE)	ovl_first (NODE)
@@ -822,6 +837,11 @@ class ovl_iterator {
 
     return fn;
   }
+  tree get_using () const
+  {
+    gcc_checking_assert (using_p ());
+    return ovl;
+  }
 
  public:
   /* Whether this overload was introduced by a using decl.  */
@@ -830,10 +850,21 @@ class ovl_iterator {
     return (TREE_CODE (ovl) == USING_DECL
 	    || (TREE_CODE (ovl) == OVERLOAD && OVL_USING_P (ovl)));
   }
+  /* Whether this using is being exported.  */
+  bool exporting_p () const
+  {
+    return OVL_EXPORT_P (get_using ());
+  }
+  
   bool hidden_p () const
   {
     return TREE_CODE (ovl) == OVERLOAD && OVL_HIDDEN_P (ovl);
   }
+  void set_dedup ()
+  {
+    if (TREE_CODE (ovl) == OVERLOAD)
+      OVL_DEDUP_P (ovl) = true;
+  }
 
  public:
   tree remove_node (tree head)
@@ -924,6 +955,84 @@ struct named_decl_hash : ggc_remove <tree> {
   static void mark_deleted (value_type) { gcc_unreachable (); }
 };
 
+/* Bindings for modules are held in a sparse array.  There is always a
+   current TU slot, others are allocated as needed.  By construction
+   of the importing mechanism we only ever need to append to the
+   array.  Rather than have straight index/slot tuples, we bunch them
+   up for greater packing.
+
+   The cluster representation packs well on a 64-bit system.  */
+
+#define MODULE_VECTOR_SLOTS_PER_CLUSTER 2
+struct mc_index {
+  unsigned short base;
+  unsigned short span;
+};
+
+struct GTY(()) module_cluster
+{
+  mc_index GTY((skip)) indices[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+  mc_slot slots[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+};
+
+/* These two fields overlay lang flags.  So don't use those.  */
+#define MODULE_VECTOR_ALLOC_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.clique)
+#define MODULE_VECTOR_NUM_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.base)
+#define MODULE_VECTOR_CLUSTER_BASE(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec)
+#define MODULE_VECTOR_CLUSTER_LAST(NODE) \
+  (&MODULE_VECTOR_CLUSTER (NODE, MODULE_VECTOR_NUM_CLUSTERS (NODE) - 1))
+#define MODULE_VECTOR_CLUSTER(NODE,IX) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec[IX])
+
+struct GTY(()) tree_module_vec {
+  struct tree_base base;
+  tree name;
+  module_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1];
+};
+
+/* The name of a module vector.  */
+#define MODULE_VECTOR_NAME(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->name)
+
+/* tree_module_vec does uses  base.u.dependence_info.base field for
+   length.  It does not have lang_flag etc available!  */
+
+/* These two flags note if a module-vector contains deduplicated
+   bindings (i.e. multiple declarations in different imports).  */
+/* This binding contains duplicate references to a global module
+   entity.  */
+#define MODULE_VECTOR_GLOBAL_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.static_flag)
+/* This binding contains duplicate references to a partioned module
+   entity.  */
+#define MODULE_VECTOR_PARTITION_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.volatile_flag)
+
+/* These two flags indicate the provenence of the bindings on this
+   particular vector slot.  We can of course determine this from slot
+   number, but that's a relatively expensive lookup.  This avoids
+   that when iterating.  */
+/* This slot is part of the global module (a header unit).  */
+#define MODULE_BINDING_GLOBAL_P(NODE) \
+  (OVERLOAD_CHECK (NODE)->base.static_flag)
+/* This slot is part of the current module (a partition or primary).  */
+#define MODULE_BINDING_PARTITION_P(NODE)		\
+  (OVERLOAD_CHECK (NODE)->base.volatile_flag)
+
+/* There are specializations of a template keyed to this binding.  */
+#define MODULE_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.public_flag)
+/* The key is in a header unit (not a named module partition or
+   primary).  */
+#define MODULE_VECTOR_PENDING_IS_HEADER_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.protected_flag)
+/* The key is in a named module (primary or partition).  */
+#define MODULE_VECTOR_PENDING_IS_PARTITION_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.private_flag)
+
 /* Simplified unique_ptr clone to release a tree vec on exit.  */
 
 class releasing_vec
@@ -1623,6 +1732,48 @@ check_constraint_info (tree t)
 #define CONSTRAINED_PARM_PROTOTYPE(NODE) \
   DECL_INITIAL (TYPE_DECL_CHECK (NODE))
 
+/* Module defines.  */
+// Too many _DECLS: FUNCTION,VAR,TYPE,TEMPLATE,CONCEPT or NAMESPACE
+#define DECL_MODULE_CHECK(NODE) (NODE)
+
+/* In the purview of a module (including header unit).  */
+#define DECL_MODULE_PURVIEW_P(N) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (N))->u.base.module_purview_p)
+
+/* True if the live version of the decl was imported.  */
+#define DECL_MODULE_IMPORT_P(NODE) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_import_p)
+
+/* True if this decl is in the entity hash & array.  This means that
+   some variant was imported, even if DECL_MODULE_IMPORT_P is false.  */
+#define DECL_MODULE_ENTITY_P(NODE) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_entity_p)
+
+/* True if there are unloaded specializations keyed to this template.  */
+#define DECL_MODULE_PENDING_SPECIALIZATIONS_P(NODE)	\
+  (DECL_LANG_SPECIFIC (TEMPLATE_DECL_CHECK (NODE))	\
+   ->u.base.module_pending_specializations_p)
+
+/* True if this class has unloaded members.  These should be loaded
+   before we do member lookups.  While this may be better on the
+   classtype, I think we can get this behaviour for enums too.  But
+   perhaps those need to be immediately loaded?  (Particularly if
+   unscoped).  */
+#define DECL_MODULE_PENDING_MEMBERS_P(NODE)		\
+  (DECL_LANG_SPECIFIC (TYPE_DECL_CHECK (NODE))		\
+   ->u.base.module_pending_members_p)
+
+/* Whether this is an exported DECL.  Held on any decl that can appear
+   at namespace scope (function, var, type, template, const or
+   namespace).  templates copy from their template_result, consts have
+   it for unscoped enums.  */
+#define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE)
+
+/* DECL that has attached decls for ODR-relatedness.  */
+#define DECL_ATTACHED_DECLS_P(NODE)			\
+  (DECL_LANG_SPECIFIC (NODE)->u.base.attached_decls_p)
+
+\f
 /* The list of local parameters introduced by this requires-expression,
    in the form of a chain of PARM_DECLs.  */
 #define REQUIRES_EXPR_PARMS(NODE) \
@@ -1644,6 +1795,7 @@ enum cp_tree_node_structure_enum {
   TS_CP_TPI,
   TS_CP_PTRMEM,
   TS_CP_OVERLOAD,
+  TS_CP_MODULE_VECTOR,
   TS_CP_BASELINK,
   TS_CP_TEMPLATE_DECL,
   TS_CP_DEFERRED_PARSE,
@@ -1665,6 +1817,7 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
   struct template_parm_index GTY ((tag ("TS_CP_TPI"))) tpi;
   struct ptrmem_cst GTY ((tag ("TS_CP_PTRMEM"))) ptrmem;
   struct tree_overload GTY ((tag ("TS_CP_OVERLOAD"))) overload;
+  struct tree_module_vec GTY ((tag ("TS_CP_MODULE_VECTOR"))) module_vec;
   struct tree_baselink GTY ((tag ("TS_CP_BASELINK"))) baselink;
   struct tree_template_decl GTY ((tag ("TS_CP_TEMPLATE_DECL"))) template_decl;
   struct tree_deferred_parse GTY ((tag ("TS_CP_DEFERRED_PARSE"))) deferred_parse;
@@ -2645,15 +2799,15 @@ enum lang_decl_selector
 /* Flags shared by all forms of DECL_LANG_SPECIFIC.
 
    Some of the flags live here only to make lang_decl_min/fn smaller.  Do
-   not make this struct larger than 32 bits; instead, make sel smaller.  */
+   not make this struct larger than 32 bits.  */
 
 struct GTY(()) lang_decl_base {
-  /* Larger than necessary for faster access.  */
-  ENUM_BITFIELD(lang_decl_selector) selector : 16;
+  ENUM_BITFIELD(lang_decl_selector) selector : 3;
   ENUM_BITFIELD(languages) language : 1;
   unsigned use_template : 2;
   unsigned not_really_extern : 1;	   /* var or fn */
   unsigned initialized_in_class : 1;	   /* var or fn */
+
   unsigned threadprivate_or_deleted_p : 1; /* var or fn */
   /* anticipated_p is no longer used for anticipated_decls (fn, type
      or template).  It is used as DECL_OMP_PRIVATIZED_MEMBER in
@@ -2662,11 +2816,22 @@ struct GTY(()) lang_decl_base {
   unsigned friend_or_tls : 1;		   /* var, fn, type or template */
   unsigned unknown_bound_p : 1;		   /* var */
   unsigned odr_used : 1;		   /* var or fn */
-  unsigned spare : 1;
   unsigned concept_p : 1;                  /* applies to vars and functions */
   unsigned var_declared_inline_p : 1;	   /* var */
   unsigned dependent_init_p : 1;	   /* var */
-  /* 2 spare bits */
+
+  unsigned module_purview_p : 1;	   /* in module purview (not GMF) */
+  unsigned module_import_p : 1;     	   /* from an import */
+  unsigned module_entity_p : 1;		   /* is in the entitity ary &
+					      hash.  */
+  /* Has specializations or members yet to load.  */
+  unsigned module_pending_specializations_p : 1;
+  unsigned module_pending_members_p : 1;
+
+  /* Is in the decl-attached hash table, (with attached decls).  */
+  unsigned attached_decls_p : 1;
+  
+  /* 10 spare bits.  */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -3099,6 +3264,13 @@ struct GTY(()) lang_decl {
   (DECL_LANG_SPECIFIC (FUNCTION_DECL_CHECK (NODE)) \
    ->u.base.friend_or_tls)
 
+/* True of a TEMPLATE_DECL that is a template class friend.  Such
+   decls are not pushed until instantiated (as they may depend on
+   parameters of the befriending class).  DECL_CHAIN is the
+   befriending class.  */
+#define DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P(NODE) \
+  (DECL_LANG_FLAG_4 (TEMPLATE_DECL_CHECK (NODE)))
+
 /* Nonzero if the thread-local variable was declared with __thread as
    opposed to thread_local.  */
 #define DECL_GNU_TLS_P(NODE)				\
@@ -3340,7 +3512,8 @@ struct GTY(()) lang_decl {
 
 /* 1 iff VAR_DECL node NODE is a type-info decl.  This flag is set for
    both the primary typeinfo object and the associated NTBS name.  */
-#define DECL_TINFO_P(NODE) TREE_LANG_FLAG_4 (VAR_DECL_CHECK (NODE))
+#define DECL_TINFO_P(NODE) \
+  TREE_LANG_FLAG_4 (TREE_CHECK2 (NODE,VAR_DECL,TYPE_DECL))
 
 /* 1 iff VAR_DECL node NODE is virtual table or VTT.  We forward to
    DECL_VIRTUAL_P from the common code, as that has the semantics we
@@ -6092,6 +6277,13 @@ struct GTY((chain_next ("%h.next"))) tinst_level {
      arguments.  */
   tree tldcl, targs;
 
+  /* For modules we need to know (a) the modules on the path of
+     instantiation and (b) the transitive imports along that path.
+     Note that these two bitmaps may be inherited from NEXT, if this
+     decl is in the same module as NEXT (or has no new information).  */
+  bitmap path;
+  bitmap visible;
+
  private:
   /* Return TRUE iff the original node is a split list.  */
   bool split_list_p () const { return targs; }
@@ -6187,6 +6379,7 @@ extern cp_parameter_declarator *no_parameters;
 
 /* Various dump ids.  */
 extern int class_dump_id;
+extern int module_dump_id;
 extern int raw_dump_id;
 
 /* in call.c */
@@ -6393,6 +6586,7 @@ extern void check_abi_tags			(tree);
 extern tree missing_abi_tags			(tree);
 extern void fixup_type_variants			(tree);
 extern void fixup_attribute_variants		(tree);
+extern void build_cdtor_clones 			(tree, bool, bool, bool);
 extern void clone_cdtor				(tree, bool);
 extern tree copy_operator_fn			(tree, tree_code code);
 extern void adjust_clone_args			(tree);
@@ -6775,6 +6977,100 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+/* In module.cc  */
+class module_state; /* Forward declare.  */
+inline bool modules_p () { return flag_modules != 0; }
+
+#define MK_MODULE (1 << 0)     /* This TU is a module.  */
+#define MK_GLOBAL (1 << 1)     /* Entities are in the global module.  */
+#define MK_INTERFACE (1 << 2)  /* This TU is an interface.  */
+#define MK_PARTITION (1 << 3)  /* This TU is a partition.  */
+#define MK_EXPORTING (1 << 4)  /* We are in an export region.  */
+extern unsigned module_kind;
+
+/*  MK_MODULE & MK_GLOBAL have the following combined meanings:
+ MODULE GLOBAL
+   0	  0    not a module
+   0      1    GMF of named module (we've not yet seen module-decl)
+   1      0    purview of named module
+   1      1    header unit.   */
+
+inline bool module_purview_p ()
+{ return module_kind & MK_MODULE; }
+inline bool global_purview_p ()
+{ return module_kind & MK_GLOBAL; }
+
+inline bool not_module_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == 0; }
+inline bool named_module_p ()
+{ /* This is a named module if exactly one of MODULE and GLOBAL is
+     set.  */
+  /* The divides are constant shifts!  */
+  return ((module_kind / MK_MODULE) ^ (module_kind / MK_GLOBAL)) & 1;
+}
+inline bool header_module_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == (MK_MODULE | MK_GLOBAL); }
+inline bool named_module_purview_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == MK_MODULE; }
+inline bool module_interface_p ()
+{ return module_kind & MK_INTERFACE; }
+inline bool module_partition_p ()
+{ return module_kind & MK_PARTITION; }
+inline bool module_has_cmi_p ()
+{ return module_kind & (MK_INTERFACE | MK_PARTITION); }
+
+/* We're currently exporting declarations.  */
+inline bool module_exporting_p ()
+{ return module_kind & MK_EXPORTING; }
+
+extern module_state *get_module (tree name, module_state *parent = NULL,
+				 bool partition = false);
+extern bool module_may_redeclare (tree decl);
+
+extern int module_initializer_kind ();
+extern void module_add_import_initializers ();
+
+/* Where the namespace-scope decl was originally declared.  */
+extern void set_originating_module (tree, bool friend_p = false);
+extern tree get_originating_module_decl (tree) ATTRIBUTE_PURE;
+extern int get_originating_module (tree, bool for_mangle = false) ATTRIBUTE_PURE;
+extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE;
+
+/* Where current instance of the decl got declared/defined/instantiated.  */
+extern void set_instantiating_module (tree);
+extern void set_defining_module (tree);
+extern void maybe_attach_decl (tree ctx, tree decl);
+
+extern void mangle_module (int m, bool include_partition);
+extern void mangle_module_fini ();
+extern void lazy_load_binding (unsigned mod, tree ns, tree id, mc_slot *mslot);
+extern void lazy_load_specializations (tree tmpl);
+extern void lazy_load_members (tree decl);
+extern bool lazy_specializations_p (unsigned, bool, bool);
+extern module_state *preprocess_module (module_state *, location_t,
+					bool in_purview, 
+					bool is_import, bool export_p,
+					cpp_reader *reader);
+extern void preprocessed_module (cpp_reader *reader);
+extern void import_module (module_state *, location_t, bool export_p,
+			   tree attr, cpp_reader *);
+extern void declare_module (module_state *, location_t, bool export_p,
+			    tree attr, cpp_reader *);
+
+extern void module_cpp_undef (cpp_reader *, location_t, cpp_hashnode *);
+extern cpp_macro *module_cpp_deferred_macro (cpp_reader *,
+					     location_t, cpp_hashnode *);
+extern void init_modules (cpp_reader *);
+extern void fini_modules ();
+extern void maybe_check_all_macros (cpp_reader *);
+extern void finish_module_processing (cpp_reader *);
+extern char const *module_name (unsigned, bool header_ok);
+extern bitmap get_import_bitmap ();
+extern bitmap module_visible_instantiation_path (bitmap *);
+extern void module_begin_main_file (cpp_reader *, line_maps *,
+				    const line_map_ordinary *);
+extern void module_preprocess_options (cpp_reader *);
+extern bool handle_module_option (unsigned opt, const char *arg, int value);
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);
@@ -7351,6 +7661,7 @@ extern tree hash_tree_cons			(tree, tree, tree);
 extern tree hash_tree_chain			(tree, tree);
 extern tree build_qualified_name		(tree, tree, tree, bool);
 extern tree build_ref_qualified_type		(tree, cp_ref_qualifier);
+extern tree make_module_vec			(tree, unsigned clusters);
 inline tree ovl_first				(tree) ATTRIBUTE_PURE;
 extern tree ovl_make				(tree fn,
 						 tree next = NULL_TREE);
@@ -7690,6 +8002,9 @@ extern tree mangle_template_parm_object		(tree);
 extern char *get_mangled_vtable_map_var_name    (tree);
 extern bool mangle_return_type_p		(tree);
 extern tree mangle_decomp			(tree, vec<tree> &);
+extern void mangle_module_substitution		(int);
+extern void mangle_identifier			(char, tree);
+extern tree mangle_module_global_init		(int);
 
 /* in dump.c */
 extern bool cp_dump_tree			(void *, tree);
@@ -7959,14 +8285,16 @@ type_unknown_p (const_tree expr)
 inline hashval_t
 named_decl_hash::hash (const value_type decl)
 {
-  tree name = OVL_NAME (decl);
+  tree name = (TREE_CODE (decl) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (decl) : OVL_NAME (decl));
   return name ? IDENTIFIER_HASH_VALUE (name) : 0;
 }
 
 inline bool
 named_decl_hash::equal (const value_type existing, compare_type candidate)
 {
-  tree name = OVL_NAME (existing);
+  tree name = (TREE_CODE (existing) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (existing) : OVL_NAME (existing));
   return candidate == name;
 }
 
diff --git c/gcc/cp/decl.c w/gcc/cp/decl.c
index 39f56b81275..1ae696a94ad 100644
--- c/gcc/cp/decl.c
+++ w/gcc/cp/decl.c
@@ -2008,6 +2008,39 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
   if (!validate_constexpr_redeclaration (olddecl, newdecl))
     return error_mark_node;
 
+  if (modules_p ()
+      && TREE_CODE (CP_DECL_CONTEXT (olddecl)) == NAMESPACE_DECL
+      && TREE_CODE (olddecl) != NAMESPACE_DECL
+      && !hiding)
+    {
+      if (DECL_ARTIFICIAL (olddecl))
+	{
+	  gcc_checking_assert (!(DECL_LANG_SPECIFIC (olddecl)
+				 && DECL_MODULE_IMPORT_P (olddecl)));
+	  if (!(global_purview_p () || not_module_p ()))
+	    error ("declaration %qD conflicts with builtin", newdecl);
+	  else
+	    DECL_MODULE_EXPORT_P (olddecl) = DECL_MODULE_EXPORT_P (newdecl);
+	}
+      else
+	{
+	  if (!module_may_redeclare (olddecl))
+	    {
+	      error ("declaration %qD conflicts with import", newdecl);
+	      inform (olddecl_loc, "import declared %q#D here", olddecl);
+
+	      return error_mark_node;
+	    }
+
+	  if (DECL_MODULE_EXPORT_P (newdecl)
+	      && !DECL_MODULE_EXPORT_P (olddecl))
+	    {
+	      error ("conflicting exporting declaration %qD", newdecl);
+	      inform (olddecl_loc, "previous declaration %q#D here", olddecl);
+	    }
+	}
+    }
+
   /* We have committed to returning OLDDECL at this point.  */
 
   /* If new decl is `static' and an `extern' was seen previously,
@@ -2218,6 +2251,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	    }
 	}
 
+      DECL_MODULE_IMPORT_P (olddecl)
+	= DECL_MODULE_IMPORT_P (old_result)
+	= DECL_MODULE_IMPORT_P (newdecl);
+
       return olddecl;
     }
 
@@ -2816,6 +2859,20 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       memcpy ((char *) olddecl + sizeof (struct tree_common),
 	      (char *) newdecl + sizeof (struct tree_common),
 	      sizeof (struct tree_decl_common) - sizeof (struct tree_common));
+
+      if (DECL_LANG_SPECIFIC (olddecl) && DECL_TEMPLATE_INFO (olddecl))
+	{
+	  /* Repropagate the module information to the template.  */
+	  tree tmpl = DECL_TI_TEMPLATE (olddecl);
+
+	  if (DECL_TEMPLATE_RESULT (tmpl) == olddecl)
+	    {
+	      DECL_MODULE_PURVIEW_P (tmpl) = DECL_MODULE_PURVIEW_P (olddecl);
+	      gcc_checking_assert (!DECL_MODULE_IMPORT_P (olddecl));
+	      DECL_MODULE_IMPORT_P (tmpl) = false;
+	    }
+	}
+
       switch (TREE_CODE (newdecl))
 	{
 	case LABEL_DECL:
@@ -4102,6 +4159,12 @@ make_unbound_class_template (tree context, tree name, tree parm_list,
       return tmpl;
     }
 
+  return make_unbound_class_template_raw (context, name, parm_list);
+}
+
+tree
+make_unbound_class_template_raw (tree context, tree name, tree parm_list)
+{
   /* Build the UNBOUND_CLASS_TEMPLATE.  */
   tree t = cxx_make_type (UNBOUND_CLASS_TEMPLATE);
   TYPE_CONTEXT (t) = FROB_CONTEXT (context);
@@ -4283,7 +4346,8 @@ cxx_init_decl_processing (void)
   gcc_assert (global_namespace == NULL_TREE);
   global_namespace = build_lang_decl (NAMESPACE_DECL, global_identifier,
 				      void_type_node);
-  TREE_PUBLIC (global_namespace) = 1;
+  TREE_PUBLIC (global_namespace) = true;
+  DECL_MODULE_EXPORT_P (global_namespace) = true;
   DECL_CONTEXT (global_namespace)
     = build_translation_unit_decl (get_identifier (main_input_filename));
   /* Remember whether we want the empty class passing ABI change warning
@@ -4579,6 +4643,9 @@ cxx_init_decl_processing (void)
   if (! supports_one_only ())
     flag_weak = 0;
 
+  if (modules_p ())
+    init_modules (parse_in);
+
   make_fname_decl = cp_make_fname_decl;
   start_fname_decls ();
 
@@ -5390,8 +5442,14 @@ start_decl (const cp_declarator *declarator,
 
   if ((DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL)
       && current_function_decl)
-    /* A function-scope decl of some namespace-scope decl.  */
-    DECL_LOCAL_DECL_P (decl) = true;
+    {
+      /* A function-scope decl of some namespace-scope decl.  */
+      DECL_LOCAL_DECL_P (decl) = true;
+      if (named_module_purview_p ())
+	error_at (declarator->id_loc,
+		  "block-scope extern declaration %q#D not permitted"
+		  " in module purview", decl);
+    }
 
   /* Enter this declaration into the symbol table.  Don't push the plain
      VAR_DECL for a variable template.  */
@@ -9810,6 +9868,8 @@ grokfndecl (tree ctype,
       && !processing_template_decl)
     deduce_noexcept_on_destructor (decl);
 
+  set_originating_module (decl);
+
   decl = check_explicit_specialization (orig_declarator, decl,
 					template_count,
 					2 * funcdef_flag +
@@ -10054,6 +10114,8 @@ grokvardecl (tree type,
       TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
     }
 
+  set_originating_module (decl);
+
   if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
     {
       if (DECL_EXTERNAL (decl) || TREE_STATIC (decl))
@@ -12907,6 +12969,8 @@ grokdeclarator (const cp_declarator *declarator,
                revert this subsequently if it determines that
                the clones should share a common implementation.  */
 	    DECL_ABSTRACT_P (decl) = true;
+
+	  set_originating_module (decl);
 	}
       else if (current_class_type
 	       && constructor_name_p (unqualified_id, current_class_type))
@@ -13431,6 +13495,8 @@ grokdeclarator (const cp_declarator *declarator,
 	      ;  /* We already issued a permerror.  */
 	    else if (decl && DECL_NAME (decl))
 	      {
+		set_originating_module (decl, true);
+		
 		if (initialized)
 		  /* Kludge: We need funcdef_flag to be true in do_friend for
 		     in-class defaulted functions, but that breaks grokfndecl.
@@ -15070,6 +15135,41 @@ xref_tag_1 (enum tag_types tag_code, tree name,
 	  inform (location_of (t), "previous declaration %qD", t);
 	  return error_mark_node;
 	}
+
+      if (modules_p ()
+	  && how == TAG_how::CURRENT_ONLY)
+	{
+	  tree decl = TYPE_NAME (t);
+	  if (!module_may_redeclare (decl))
+	    {
+	      error ("cannot declare %qD in a different module", decl);
+	      inform (DECL_SOURCE_LOCATION (decl), "declared here");
+	      return error_mark_node;
+	    }
+
+	  tree maybe_tmpl = decl;
+	  if (CLASS_TYPE_P (t) && CLASSTYPE_IS_TEMPLATE (t))
+	    maybe_tmpl = CLASSTYPE_TI_TEMPLATE (t);
+
+	  if (DECL_LANG_SPECIFIC (decl)
+	      && DECL_MODULE_IMPORT_P (decl)
+	      && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
+	    {
+	      /* Push it into this TU's symbol slot.  */
+	      gcc_checking_assert (current_namespace == CP_DECL_CONTEXT (decl));
+	      if (maybe_tmpl != decl)
+		/* We're in the template parm binding level.
+		   Pushtag has logic to slide under that, but we're
+		   not pushing a *new* type.  */
+		push_nested_namespace (CP_DECL_CONTEXT (decl));
+
+	      pushdecl (maybe_tmpl);
+	      if (maybe_tmpl != decl)
+		pop_nested_namespace (CP_DECL_CONTEXT (decl));
+	    }
+
+	  set_instantiating_module (maybe_tmpl);
+	}
     }
 
   return t;
@@ -15414,6 +15514,19 @@ start_enum (tree name, tree enumtype, tree underlying_type,
 		  "previous definition here");
 	  underlying_type = NULL_TREE;
 	}
+
+      if (flag_modules)
+	{
+	  if (!module_may_redeclare (TYPE_NAME (enumtype)))
+	    {
+	      error ("cannot define %qD in different module",
+		     TYPE_NAME (enumtype));
+	      inform (DECL_SOURCE_LOCATION (TYPE_NAME (enumtype)),
+		      "declared here");
+	      enumtype = error_mark_node;
+	    }
+	  set_instantiating_module (TYPE_NAME (enumtype));
+	}
     }
 
   if (!enumtype || TREE_CODE (enumtype) != ENUMERAL_TYPE
@@ -15674,6 +15787,11 @@ finish_enum_value_list (tree enumtype)
   else
     underlying_type = ENUM_UNDERLYING_TYPE (enumtype);
 
+  /* If the enum is exported, mark the consts too.  */
+  bool export_p = (UNSCOPED_ENUM_P (enumtype)
+		   && DECL_MODULE_EXPORT_P (TYPE_STUB_DECL (enumtype))
+		   && at_namespace_scope_p ());
+
   /* Convert each of the enumerators to the type of the underlying
      type of the enumeration.  */
   for (values = TYPE_VALUES (enumtype); values; values = TREE_CHAIN (values))
@@ -15696,8 +15814,10 @@ finish_enum_value_list (tree enumtype)
 	  TREE_TYPE (value) = enumtype;
 	}
       DECL_INITIAL (decl) = value;
+      if (export_p)
+	DECL_MODULE_EXPORT_P (decl) = true;
     }
 
   /* Fix up all variant types of this enum type.  */
   for (t = TYPE_MAIN_VARIANT (enumtype); t; t = TYPE_NEXT_VARIANT (t))
     TYPE_VALUES (t) = TYPE_VALUES (enumtype);
@@ -16866,7 +16988,7 @@ maybe_save_function_definition (tree fun)
       && DECL_DECLARED_CONSTEXPR_P (fun)
       && !cp_function_chain->invalid_constexpr
       && !DECL_CLONED_FUNCTION_P (fun))
-    register_constexpr_fundef (fun, DECL_SAVED_TREE (fun));
+    check_constexpr_fundef (fun, DECL_SAVED_TREE (fun));
 }
 
 /* Attempt to add a fix-it hint to RICHLOC suggesting the insertion
@@ -17307,10 +17429,25 @@ grokmethod (cp_decl_specifier_seq *declspecs,
 
   check_template_shadow (fndecl);
 
-  if (TREE_PUBLIC (fndecl))
-    DECL_COMDAT (fndecl) = 1;
-  DECL_DECLARED_INLINE_P (fndecl) = 1;
-  DECL_NO_INLINE_WARNING_P (fndecl) = 1;
+  /* p1779 ABI-Isolation makes inline not a default for in-class
+     definitions in named module purview.  If the user explicitly
+     made it inline, grokdeclarator will already have done the right
+     things.  */
+  if ((!named_module_purview_p ()
+       || flag_module_implicit_inline
+      /* Lambda's operator function remains inline.  */
+       || LAMBDA_TYPE_P (DECL_CONTEXT (fndecl)))
+      /* If the user explicitly asked for this to be inline, we don't
+	 need to do more, but more importantly we want to warn if we
+	 can't inline it.  */
+      && !DECL_DECLARED_INLINE_P (fndecl))
+    {
+      if (TREE_PUBLIC (fndecl))
+	DECL_COMDAT (fndecl) = 1;
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      /* It's ok if we can't inline this.  */
+      DECL_NO_INLINE_WARNING_P (fndecl) = 1;
+    }
 
   /* We process method specializations in finish_struct_1.  */
   if (processing_template_decl && !DECL_TEMPLATE_SPECIALIZATION (fndecl))
@@ -17581,6 +17718,7 @@ cp_tree_node_structure (union lang_tree_node * t)
     case DEFERRED_PARSE:	return TS_CP_DEFERRED_PARSE;
     case IDENTIFIER_NODE:	return TS_CP_IDENTIFIER;
     case LAMBDA_EXPR:		return TS_CP_LAMBDA_EXPR;
+    case MODULE_VECTOR:		return TS_CP_MODULE_VECTOR;
     case OVERLOAD:		return TS_CP_OVERLOAD;
     case PTRMEM_CST:		return TS_CP_PTRMEM;
     case STATIC_ASSERT:		return TS_CP_STATIC_ASSERT;
diff --git c/gcc/cp/decl2.c w/gcc/cp/decl2.c
index 71107e03010..60bc6444d7c 100644
--- c/gcc/cp/decl2.c
+++ w/gcc/cp/decl2.c
@@ -4470,6 +4506,10 @@ no_linkage_error (tree decl)
     /* In C++11 it's ok if the decl is defined.  */
     return;
 
+  if (DECL_LANG_SPECIFIC (decl) && DECL_MODULE_IMPORT_P (decl))
+    /* An imported decl is ok.  */
+    return;
+
   tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false);
   if (t == NULL_TREE)
     /* The type that got us on no_linkage_decls must have gotten a name for
@@ -4904,6 +4944,9 @@ c_parse_final_cleanups (void)
       instantiate_pending_templates (retries);
       ggc_collect ();
 
+      if (header_module_p ())
+	goto skip;
+
       /* Write out virtual tables as required.  Writing out the
 	 virtual table for a template class may cause the
 	 instantiation of members of that class.  If we write out
@@ -5102,10 +5147,13 @@ c_parse_final_cleanups (void)
 					 pending_statics->length ()))
 	reconsider = true;
 
+    skip:;
       retries++;
     }
   while (reconsider);
 
+  finish_module_processing (parse_in);
+
   lower_var_init ();
 
   generate_mangling_aliases ();
@@ -5121,6 +5169,10 @@ c_parse_final_cleanups (void)
 	     #pragma interface, etc.) we decided not to emit the
 	     definition here.  */
 	  && !DECL_INITIAL (decl)
+	  /* A defaulted fn in a header module can be synthesized on
+	     demand later.  (In non-header modules we should have
+	     synthesized it above.)  */
+	  && !(DECL_DEFAULTED_FN (decl) && header_module_p ())
 	  /* Don't complain if the template was defined.  */
 	  && !(DECL_TEMPLATE_INSTANTIATION (decl)
 	       && DECL_INITIAL (DECL_TEMPLATE_RESULT
@@ -5154,9 +5206,8 @@ c_parse_final_cleanups (void)
     splay_tree_foreach (priority_info_map,
 			generate_ctor_and_dtor_functions_for_priority,
 			/*data=*/&locus_at_end_of_parsing);
-  else if (c_dialect_objc () && objc_static_init_needed_p ())
-    /* If this is obj-c++ and we need a static init, call
-       generate_ctor_or_dtor_function.  */
+  else if ((c_dialect_objc () && objc_static_init_needed_p ())
+	   || module_initializer_kind ())
     generate_ctor_or_dtor_function (/*constructor_p=*/true,
 				    DEFAULT_INIT_PRIORITY,
 				    &locus_at_end_of_parsing);
@@ -5165,6 +5216,8 @@ c_parse_final_cleanups (void)
   if (priority_info_map)
     splay_tree_delete (priority_info_map);
 
+  fini_modules ();
+
   /* Generate any missing aliases.  */
   maybe_apply_pending_pragma_weaks ();
 
diff --git c/gcc/cp/error.c w/gcc/cp/error.c
index 396558be17f..ad998791bc6 100644
--- c/gcc/cp/error.c
+++ w/gcc/cp/error.c
@@ -179,6 +179,36 @@ cxx_initialize_diagnostics (diagnostic_context *context)
   pp->m_format_postprocessor = new cxx_format_postprocessor ();
 }
 
+static void
+dump_module_suffix (cxx_pretty_printer *pp, tree decl)
+{
+  if (!modules_p ())
+    return;
+
+  if (!DECL_CONTEXT (decl))
+    return;
+
+  if (TREE_CODE (decl) != CONST_DECL
+      || !UNSCOPED_ENUM_P (DECL_CONTEXT (decl)))
+    {
+      if (!DECL_NAMESPACE_SCOPE_P (decl))
+	return;
+
+      if (TREE_CODE (decl) == NAMESPACE_DECL
+	  && !DECL_NAMESPACE_ALIAS (decl)
+	  && (TREE_PUBLIC (decl) || !TREE_PUBLIC (CP_DECL_CONTEXT (decl))))
+	return;
+    }
+
+  if (unsigned m = get_originating_module (decl))
+    if (const char *n = module_name (m, false))
+      {
+	pp_character (pp, '@');
+	pp->padding = pp_none;
+	pp_string (pp, n);
+      }
+}
+
 /* Dump a scope, if deemed necessary.  */
 
 static void
@@ -770,6 +800,8 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
   else
     pp_cxx_tree_identifier (pp, DECL_NAME (decl));
 
+  dump_module_suffix (pp, decl);
+
   if (tmplate)
     dump_template_parms (pp, TYPE_TEMPLATE_INFO (t),
 			 !CLASSTYPE_USE_TEMPLATE (t),
@@ -1074,6 +1106,9 @@ dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
     pp_string (pp, M_("<structured bindings>"));
   else
     pp_string (pp, M_("<anonymous>"));
+
+  dump_module_suffix (pp, t);
+
   if (flags & TFF_DECL_SPECIFIERS)
     dump_type_suffix (pp, type, flags);
 }
@@ -1891,6 +1926,8 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
   else
     dump_decl (pp, name, flags);
 
+  dump_module_suffix (pp, t);
+
   if (DECL_TEMPLATE_INFO (t)
       && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
diff --git c/gcc/cp/lambda.c w/gcc/cp/lambda.c
index 1a1647f465e..f6746d7304b 100644
--- c/gcc/cp/lambda.c
+++ w/gcc/cp/lambda.c
@@ -1114,6 +1114,8 @@ maybe_add_lambda_conv_op (tree type)
     while (src)
       {
 	tree new_node = copy_node (src);
+	/* We set DECL_CONTEXT of NEW_NODE to the statfn below.
+	   Notice this is creating a recursive type!  */
 
 	/* Clear TREE_ADDRESSABLE on thunk arguments.  */
 	TREE_ADDRESSABLE (new_node) = 0;
@@ -1393,6 +1395,12 @@ record_lambda_scope (tree lambda)
 {
   LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope;
   LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++;
+  if (lambda_scope)
+    {
+      tree closure = LAMBDA_EXPR_CLOSURE (lambda);
+      gcc_checking_assert (closure);
+      maybe_attach_decl (lambda_scope, TYPE_NAME (closure));
+    }
 }
 
 /* This lambda is an instantiation of a lambda in a template default argument
diff --git c/gcc/cp/mangle.c w/gcc/cp/mangle.c
index 9fd30011288..6f9c93cb571 100644
--- c/gcc/cp/mangle.c
+++ w/gcc/cp/mangle.c
@@ -117,6 +117,9 @@ struct GTY(()) globals {
 
   /* True if the mangling will be different in C++17 mode.  */
   bool need_cxx17_warning;
+
+  /* True if we mangled a module name.  */
+  bool mod;
 };
 
 static GTY (()) globals G;
@@ -832,6 +835,62 @@ write_encoding (const tree decl)
     }
 }
 
+/* Interface to substitution and identifer mangling, used by the
+   module name mangler.  */
+
+void
+mangle_module_substitution (int v)
+{
+  if (v < 10)
+    {
+      write_char ('_');
+      write_char ('0' + v);
+    }
+  else
+    {
+      write_char ('W');
+      write_unsigned_number (v - 10);
+      write_char ('_');
+    }
+}
+
+void
+mangle_identifier (char c, tree id)
+{
+  if (c)
+    write_char (c);
+  write_source_name (id);
+}
+
+/* If the outermost non-namespace context (including DECL itself) is
+   a module-linkage decl, mangle the module information.  For module
+   global initializers we need to include the partition part.
+
+   <module-name> ::= W <module-id>+ E
+   <module-id> :: <unqualified-name>
+               || _ <digit>  ;; short backref
+	       || W <number> _  ;; long backref
+               || P <module-id> ;; partition introducer
+*/
+
+static void
+write_module (int m, bool include_partition)
+{
+  G.mod = true;
+
+  write_char ('W');
+  mangle_module (m, include_partition);
+  write_char ('E');
+}
+
+static void
+maybe_write_module (tree decl)
+{
+  int m = get_originating_module (decl, true);
+  if (m >= 0)
+    write_module (m, false);
+}
+
 /* Lambdas can have a bit more context for mangling, specifically VAR_DECL
    or PARM_DECL context, which doesn't belong in DECL_CONTEXT.  */
 
@@ -894,6 +953,9 @@ write_name (tree decl, const int ignore_local_scope)
       decl = TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
     }
 
+  if (modules_p ())
+    maybe_write_module (decl);
+
   context = decl_mangling_context (decl);
 
   gcc_assert (context != NULL_TREE);
@@ -3806,14 +3868,13 @@ start_mangling (const tree entity)
   G.entity = entity;
   G.need_abi_warning = false;
   G.need_cxx17_warning = false;
+  G.mod = false;
   obstack_free (&name_obstack, name_base);
   mangle_obstack = &name_obstack;
   name_base = obstack_alloc (&name_obstack, 0);
 }
 
-/* Done with mangling. If WARN is true, and the name of G.entity will
-   be mangled differently in a future version of the ABI, issue a
-   warning.  */
+/* Done with mangling.  Release the data.  */
 
 static void
 finish_mangling_internal (void)
@@ -3821,6 +3882,9 @@ finish_mangling_internal (void)
   /* Clear all the substitutions.  */
   vec_safe_truncate (G.substitutions, 0);
 
+  if (G.mod)
+    mangle_module_fini ();
+
   /* Null-terminate the string.  */
   write_char ('\0');
 }
@@ -3865,6 +3929,20 @@ init_mangle (void)
   subst_identifiers[SUBID_BASIC_IOSTREAM] = get_identifier ("basic_iostream");
 }
 
+/* Generate a mangling for MODULE's global initializer fn.  */
+
+tree
+mangle_module_global_init (int module)
+{
+  start_mangling (NULL_TREE);
+
+  write_string ("_ZGI");
+  write_module (module, true);
+  write_char ('v');
+
+  return finish_mangling_get_identifier ();
+}
+
 /* Generate the mangled name of DECL.  */
 
 static tree
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index aa162d2a4f9..497ac5aafec 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -40,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "type-utils.h"
 #include "gimplify.h"
+#include "bitmap.h"
 #include "gcc-rich-location.h"
 #include "selftest.h"
 #include "target.h"
@@ -965,6 +961,9 @@ maybe_new_partial_specialization (tree type)
       TREE_PRIVATE (d) = (current_access_specifier == access_private_node);
       TREE_PROTECTED (d) = (current_access_specifier == access_protected_node);
 
+      set_instantiating_module (d);
+      DECL_MODULE_EXPORT_P (d) = DECL_MODULE_EXPORT_P (tmpl);
+
       return t;
     }
 
@@ -4926,6 +4929,17 @@ 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 (modules_p ())
+    {
+      /* Propagate module information from the decl.  */
+      DECL_MODULE_EXPORT_P (tmpl) = DECL_MODULE_EXPORT_P (decl);
+      if (DECL_LANG_SPECIFIC (decl))
+	{
+	  DECL_MODULE_PURVIEW_P (tmpl) = DECL_MODULE_PURVIEW_P (decl);
+	  gcc_checking_assert (!DECL_MODULE_IMPORT_P (decl));
+	}
+    }
+
   return tmpl;
 }
 
@@ -6047,6 +6061,14 @@ push_template_decl (tree decl, bool is_friend)
 	      tmpl = NULL_TREE;
 	    }
 	}
+      else if (is_friend)
+	{
+	  /* Record this decl as belonging to the current class.  It's
+	     not chained onto anything else.  */
+	  DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (tmpl) = true;
+	  gcc_checking_assert (!DECL_CHAIN (tmpl));
+	  DECL_CHAIN (tmpl) = current_scope ();
+	}
     }
   else if (tmpl)
     /* The type may have been completed, or (erroneously) changed.  */
@@ -9770,6 +9792,15 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
 	return error_mark_node;
 
       gen_tmpl = most_general_template (templ);
+      if (flag_modules)
+	{
+	  tree origin = get_originating_module_decl (gen_tmpl);
+	  load_pending_specializations (CP_DECL_CONTEXT (origin),
+					DECL_NAME (origin));
+	  if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
+	    lazy_load_specializations (gen_tmpl);
+	}
+
       parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
       parm_depth = TMPL_PARMS_DEPTH (parmlist);
       arg_depth = TMPL_ARGS_DEPTH (arglist);
@@ -9990,6 +10021,12 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
 	    = DECL_SOURCE_LOCATION (TYPE_STUB_DECL (template_type));
 	}
 
+      set_instantiating_module (type_decl);
+      /* Although GEN_TMPL is the TEMPLATE_DECL, it has the same value
+	 of export flag.  We want to propagate this because it might
+	 be a friend declaration that pushes a new hidden binding.  */
+      DECL_MODULE_EXPORT_P (type_decl) = DECL_MODULE_EXPORT_P (gen_tmpl);
+
       if (CLASS_TYPE_P (template_type))
 	{
 	  TREE_PRIVATE (type_decl)
@@ -10896,6 +10933,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
   new_level->errors = errorcount + sorrycount;
   new_level->next = NULL;
   new_level->refcount = 0;
+  new_level->path = new_level->visible = nullptr;
   set_refcount_ptr (new_level->next, current_tinst_level);
   set_refcount_ptr (current_tinst_level, new_level);
 
@@ -11046,6 +11084,7 @@ tsubst_friend_function (tree decl, tree args)
   DECL_USE_TEMPLATE (new_friend) = 0;
   if (TREE_CODE (new_friend) == TEMPLATE_DECL)
     {
+      DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (new_friend) = false;
       DECL_USE_TEMPLATE (DECL_TEMPLATE_RESULT (new_friend)) = 0;
       DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (new_friend))
 	= DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (decl));
@@ -13891,6 +13935,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
   if (!DECL_DELETED_FN (r))
     DECL_INITIAL (r) = NULL_TREE;
   DECL_CONTEXT (r) = ctx;
+  set_instantiating_module (r);
 
   /* Handle explicit(dependent-expr).  */
   if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t))
@@ -14214,7 +14259,25 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain,
   TREE_TYPE (r) = TREE_TYPE (inner);
   DECL_CONTEXT (r) = DECL_CONTEXT (inner);
 
+  if (modules_p ())
+    {
+      /* Propagate module information from the decl.  */
+      DECL_MODULE_EXPORT_P (r) = DECL_MODULE_EXPORT_P (inner);
+      if (DECL_LANG_SPECIFIC (inner))
+	{
+	  DECL_MODULE_PURVIEW_P (r) = DECL_MODULE_PURVIEW_P (inner);
+	  /* If this is a constrained template, the above tsubst of
+	     inner can find the unconstrained template, which may have
+	     come from an import.  This is ok, because we don't
+	     register this instantiation (see below).  */
+	  gcc_checking_assert (!DECL_MODULE_IMPORT_P (inner)
+			       || (TEMPLATE_PARMS_CONSTRAINTS
+				   (DECL_TEMPLATE_PARMS (t))));
+	  DECL_MODULE_IMPORT_P (r) = false;
+	}
+    }
+
   DECL_TEMPLATE_INSTANTIATIONS (r) = NULL_TREE;
   DECL_TEMPLATE_SPECIALIZATIONS (r) = NULL_TREE;
 
   if (PRIMARY_TEMPLATE_P (t))
@@ -14769,6 +14831,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	SET_DECL_ASSEMBLER_NAME (r, NULL_TREE);
 	if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_DECL_WRTL))
 	  SET_DECL_RTL (r, NULL);
+	set_instantiating_module (r);
+
 	/* The initializer must not be expanded until it is required;
 	   see [temp.inst].  */
 	DECL_INITIAL (r) = NULL_TREE;
@@ -20843,6 +20914,15 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 		(DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl)),
 		 targ_ptr));
 
+  if (flag_modules)
+    {
+      tree origin = get_originating_module_decl (gen_tmpl);
+      load_pending_specializations (CP_DECL_CONTEXT (origin),
+				    DECL_NAME (origin));
+      if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
+	lazy_load_specializations (gen_tmpl);
+    }
+
   /* It would be nice to avoid hashing here and then again in tsubst_decl,
      but it doesn't seem to be on the hot path.  */
   spec = retrieve_specialization (gen_tmpl, targ_ptr, 0);
@@ -20928,6 +21008,8 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   DECL_TI_TEMPLATE (fndecl) = tmpl;
   DECL_TI_ARGS (fndecl) = targ_ptr;
 
+  set_instantiating_module (fndecl);
+
   /* Now we know the specialization, compute access previously
      deferred.  Do no access control for inheriting constructors,
      as we already checked access for the inherited constructor.  */
@@ -28002,6 +28082,8 @@ finish_concept_definition (cp_expr id, tree init)
   DECL_CONTEXT (decl) = current_scope ();
   DECL_INITIAL (decl) = init;
 
+  set_originating_module (decl, false);
+
   /* Push the enclosing template.  */
   return push_template_decl (decl);
 }
diff --git c/gcc/cp/ptree.c w/gcc/cp/ptree.c
index a28b722f571..a216ca59e99 100644
--- c/gcc/cp/ptree.c
+++ w/gcc/cp/ptree.c
@@ -59,6 +59,42 @@ cxx_print_decl (FILE *file, tree node, int indent)
 
   bool need_indent = true;
 
+  if (TREE_CODE (node) == FUNCTION_DECL
+      || TREE_CODE (node) == VAR_DECL
+      || TREE_CODE (node) == TYPE_DECL
+      || TREE_CODE (node) == TEMPLATE_DECL
+      || TREE_CODE (node) == CONCEPT_DECL
+      || TREE_CODE (node) == NAMESPACE_DECL)
+    {
+      unsigned m = 0;
+      if (DECL_LANG_SPECIFIC (node) && DECL_MODULE_IMPORT_P (node))
+	m = get_importing_module (node, true);
+
+      if (const char *name = m == ~0u ? "" : module_name (m, true))
+	{
+	  if (need_indent)
+	    indent_to (file, indent + 3);
+	  fprintf (file, " module %d:%s", m, name);
+	  need_indent = false;
+	}
+
+      if (DECL_LANG_SPECIFIC (node) && DECL_MODULE_PURVIEW_P (node))
+	{
+	  if (need_indent)
+	    indent_to (file, indent + 3);
+	  fprintf (file, " purview");
+	  need_indent = false;
+	}
+    }
+
+  if (DECL_MODULE_EXPORT_P (node))
+    {
+      if (need_indent)
+	indent_to (file, indent + 3);
+      fprintf (file, " exported");
+      need_indent = false;
+    }
+
   if (DECL_EXTERNAL (node) && DECL_NOT_REALLY_EXTERN (node))
     {
       if (need_indent)
@@ -250,6 +286,44 @@ cxx_print_xnode (FILE *file, tree node, int indent)
       print_node (file, "function", OVL_FUNCTION (node), indent + 4);
       print_node (file, "next", OVL_CHAIN (node), indent + 4);
       break;
+    case MODULE_VECTOR:
+      {
+	unsigned len = MODULE_VECTOR_NUM_CLUSTERS (node);
+	print_node (file, "name", MODULE_VECTOR_NAME (node), indent + 4);
+	fprintf (file, " clusters %u, alloc %u", len,
+		 MODULE_VECTOR_ALLOC_CLUSTERS (node));
+	for (unsigned ix = 0; ix != len; ix++)
+	  {
+	    module_cluster *cluster = &MODULE_VECTOR_CLUSTER (node, ix);
+	    char pfx[20];
+	    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	      if (cluster->indices[jx].span)
+		{
+		  int len = sprintf (pfx, "module:%u",
+				     cluster->indices[jx].base);
+		  if (cluster->indices[jx].span > 1)
+		    len
+		      += sprintf (&pfx[len], "(+%u)", cluster->indices[jx].span);
+		  len += sprintf (&pfx[len], " cluster:%u/%u", ix, jx);
+		  mc_slot &slot = cluster->slots[jx];
+		  if (slot.is_lazy ())
+		    {
+		      indent_to (file, indent + 4);
+		      unsigned lazy = slot.get_lazy ();
+		      fprintf (file, "%s snum:%u flags:%d",
+			       pfx, lazy >> 2, lazy & 3);
+		    }
+		  else if (slot)
+		    print_node (file, pfx, slot, indent + 4);
+		  else
+		    {
+		      indent_to (file, indent + 4);
+		      fprintf (file, "%s NULL", pfx);
+		    }
+		}
+	  }
+      }
+      break;
     case TEMPLATE_PARM_INDEX:
       print_node (file, "decl", TEMPLATE_PARM_DECL (node), indent+4);
       indent_to (file, indent + 3);
diff --git c/gcc/cp/semantics.c w/gcc/cp/semantics.c
index 352ebe03436..a0888bc6c29 100644
--- c/gcc/cp/semantics.c
+++ w/gcc/cp/semantics.c
@@ -3225,6 +3225,19 @@ begin_class_definition (tree t)
       t = make_class_type (TREE_CODE (t));
       pushtag (TYPE_IDENTIFIER (t), t);
     }
+
+  if (flag_modules)
+    {
+      if (!module_may_redeclare (TYPE_NAME (t)))
+	{
+	  error ("cannot declare %qD in a different module", TYPE_NAME (t));
+	  inform (DECL_SOURCE_LOCATION (TYPE_NAME (t)), "declared here");
+	  return error_mark_node;
+	}
+      set_instantiating_module (TYPE_NAME (t));
+      set_defining_module (TYPE_NAME (t));
+    }
+
   maybe_process_partial_specialization (t);
   pushclass (t);
   TYPE_BEING_DEFINED (t) = 1;
@@ -4503,7 +4516,8 @@ expand_or_defer_fn_1 (tree fn)
 	 it out, even though we haven't.  */
       TREE_ASM_WRITTEN (fn) = 1;
       /* If this is a constexpr function, keep DECL_SAVED_TREE.  */
-      if (!DECL_DECLARED_CONSTEXPR_P (fn))
+      if (!DECL_DECLARED_CONSTEXPR_P (fn)
+	  && !(modules_p () && DECL_DECLARED_INLINE_P (fn)))
 	DECL_SAVED_TREE (fn) = NULL_TREE;
       return false;
     }
diff --git c/gcc/gdbinit.in w/gcc/gdbinit.in
index e951c19db63..eb999d2a808 100644
--- c/gcc/gdbinit.in
+++ w/gcc/gdbinit.in
@@ -333,6 +333,10 @@ b fancy_abort
 # Put a breakpoint on internal_error to help with debugging ICEs.
 b internal_error
 
+# Break on module streaming error
+b bytes_in::set_overrun if !overrun
+b elf::set_error if !err
+
 set complaints 0
 # Don't let abort actually run, as it will make
 # stdio stop working and therefore the `pr' command above as well.
diff --git c/gcc/tree-core.h w/gcc/tree-core.h
index c9280a8d3b1..73aa0c3f399 100644
--- c/gcc/tree-core.h
+++ w/gcc/tree-core.h
@@ -769,6 +769,10 @@ enum tree_index {
   TI_SAT_UDA_TYPE,
   TI_SAT_UTA_TYPE,
 
+  TI_MODULE_HWM,
+  /* Nodes below here change during compilation, and should therefore
+     not be in the C++ module's global tree table.  */
+
   TI_OPTIMIZATION_DEFAULT,
   TI_OPTIMIZATION_CURRENT,
   TI_TARGET_OPTION_DEFAULT,
diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index 8e398863cf6..81be6457951 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -528,6 +538,9 @@ struct cpp_options
        one.  */
     bool phony_targets;
 
+    /* Generate dependency info for modules.  */
+    bool modules;
+
     /* If true, no dependency is generated on the main file.  */
     bool ignore_main_file;
 
diff --git c/gcc/cp/rtti.c w/gcc/cp/rtti.c
index 887aae31bf6..0be9eff54ad 100644
--- c/gcc/cp/rtti.c
+++ w/gcc/cp/rtti.c
@@ -144,16 +143,20 @@ static bool typeinfo_in_lib_p (tree);
 
 static int doing_runtime = 0;
 \f
-static void
+static unsigned
 push_abi_namespace (void)
 {
   push_nested_namespace (abi_node);
   push_visibility ("default", 2);
+  unsigned flags = module_kind;
+  module_kind = 0;
+  return flags;
 }
 
 static void
-pop_abi_namespace (void)
+pop_abi_namespace (unsigned flags)
 {
+  module_kind = flags;
   pop_visibility (2);
   pop_nested_namespace (abi_node);
 }
@@ -766,7 +769,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	  dcast_fn = dynamic_cast_node;
 	  if (!dcast_fn)
 	    {
-	      push_abi_namespace ();
+	      unsigned flags = push_abi_namespace ();
 	      tree tinfo_ptr = xref_tag (class_type,
 					 get_identifier ("__class_type_info"));
 	      tinfo_ptr = cp_build_qualified_type (tinfo_ptr, TYPE_QUAL_CONST);
@@ -781,7 +784,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 			       NULL_TREE));
 	      dcast_fn = (build_library_fn_ptr
 			  (fn_name, fn_type, ECF_LEAF | ECF_PURE | ECF_NOTHROW));
-	      pop_abi_namespace ();
+	      pop_abi_namespace (flags);
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
@@ -955,11 +958,11 @@ tinfo_base_init (tinfo_s *ti, tree target)
   vtable_ptr = ti->vtable;
   if (!vtable_ptr)
     {
-      push_abi_namespace ();
+      int flags = push_abi_namespace ();
       tree real_type = xref_tag (class_type, ti->name);
       tree real_decl = TYPE_NAME (real_type);
       DECL_SOURCE_LOCATION (real_decl) = BUILTINS_LOCATION;
-      pop_abi_namespace ();
+      pop_abi_namespace (flags);
 
       if (!COMPLETE_TYPE_P (real_type))
 	{
@@ -1479,6 +1482,7 @@ get_tinfo_desc (unsigned ix)
   finish_builtin_struct (pseudo_type, pseudo_name, fields, NULL_TREE);
   CLASSTYPE_AS_BASE (pseudo_type) = pseudo_type;
   DECL_CONTEXT (TYPE_NAME (pseudo_type)) = FROB_CONTEXT (global_namespace);
+  DECL_TINFO_P (TYPE_NAME (pseudo_type)) = true;
   xref_basetypes (pseudo_type, /*bases=*/NULL_TREE);
 
   res->type = cp_build_qualified_type (pseudo_type, TYPE_QUAL_CONST);
diff --git c/gcc/cp/tree.c w/gcc/cp/tree.c
index 3087c4ab52c..d125fa5e793 100644
--- c/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -2218,6 +2226,23 @@ build_ref_qualified_type (tree type, cp_ref_qualifier rqual)
   return build_cp_fntype_variant (type, rqual, raises, late);
 }
 
+tree
+make_module_vec (tree name, unsigned clusters MEM_STAT_DECL)
+{
+  /* Stored in an unsigned short, but we're limited to the number of
+     modules anyway.  */
+  gcc_checking_assert (clusters <= (unsigned short)(~0));
+  size_t length = (clusters * sizeof (module_cluster)
+		   + sizeof (tree_module_vec) - sizeof (module_cluster));
+  tree vec = ggc_alloc_cleared_tree_node_stat (length PASS_MEM_STAT);
+  TREE_SET_CODE (vec, MODULE_VECTOR);
+  MODULE_VECTOR_NAME (vec) = name;
+  MODULE_VECTOR_ALLOC_CLUSTERS (vec) = clusters;
+  MODULE_VECTOR_NUM_CLUSTERS (vec) = 0;
+
+  return vec;
+}
+
 /* Make a raw overload node containing FN.  */
 
 tree
@@ -2237,10 +2262,11 @@ ovl_make (tree fn, tree next)
   return result;
 }
 
-/* Add FN to the (potentially NULL) overload set OVL.  USING_OR_HIDDEN
-   is > 0, if FN is via a using declaration.  USING_OR_HIDDEN is < 0,
-   if FN is hidden.  (A decl cannot be both using and hidden.)  We
-   keep the hidden decls first, but remaining ones are unordered.  */
+/* Add FN to the (potentially NULL) overload set OVL.  USING_OR_HIDDEN is >
+   zero if this is a using-decl.  It is > 1 if we're exporting the
+   using decl.  USING_OR_HIDDEN is < 0, if FN is hidden.  (A decl
+   cannot be both using and hidden.)  We keep the hidden decls first,
+   but remaining ones are unordered.  */
 
 tree
 ovl_insert (tree fn, tree maybe_ovl, int using_or_hidden)
@@ -2264,7 +2290,11 @@ ovl_insert (tree fn, tree maybe_ovl, int using_or_hidden)
       if (using_or_hidden < 0)
 	OVL_HIDDEN_P (maybe_ovl) = true;
       if (using_or_hidden > 0)
-	OVL_DEDUP_P (maybe_ovl) = OVL_USING_P (maybe_ovl) = true;
+	{
+	  OVL_DEDUP_P (maybe_ovl) = OVL_USING_P (maybe_ovl) = true;
+	  if (using_or_hidden > 1)
+	    OVL_EXPORT_P (maybe_ovl) = true;
+	}
     }
   else
     maybe_ovl = fn;
diff --git c/gcc/cp/class.c w/gcc/cp/class.c
index c03737294eb..f4e44a5feba 100644
--- c/gcc/cp/class.c
+++ w/gcc/cp/class.c
@@ -4901,7 +4901,7 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
 /* Build the clones of FN, return the number of clones built.  These
    will be inserted onto DECL_CHAIN of FN.  */
 
-static void
+void
 build_cdtor_clones (tree fn, bool needs_vtt_p, bool base_omits_inherited_p,
 		    bool update_methods)
 {
@@ -6740,6 +6740,8 @@ layout_class_type (tree t, tree *virtuals_p)
       TYPE_CONTEXT (base_t) = t;
       DECL_CONTEXT (base_d) = t;
 
+      set_instantiating_module (base_d);
+
       /* If the ABI version is not at least two, and the last
 	 field was a bit-field, RLI may not be on a byte
 	 boundary.  In particular, rli_size_unit_so_far might
@@ -8719,6 +8721,7 @@ build_self_reference (void)
   DECL_ARTIFICIAL (decl) = 1;
   SET_DECL_SELF_REFERENCE_P (decl);
   set_underlying_type (decl);
+  set_instantiating_module (decl);  
 
   if (processing_template_decl)
     decl = push_template_decl (decl);


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

* [23/32] libcody
       [not found]                                         ` <9e6ec23c-6b36-de70-7630-55562583696f@acm.org>
@ 2020-11-03 21:17                                           ` Nathan Sidwell
       [not found]                                           ` <c839f3f8-9ce6-0c28-b981-d81f236a3a34@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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



libcody is an implementation of the module mapper protocol described in 
wg21.links/p1184 Its intent is an interface between compilers and build 
systems to resolve mappings between module names, source files and CMIs. 
  Possibly discovered dynamically.

libcody is included by value, so that builders of GCC do not need to 
fetch it separately.  I'd expect occasional resyncing much the same way 
as the sanitizer libraries are resynced.

libcody is an Apache-licensed github project, written by me.  I have 
asked the SC about incorporating it into GCC but have yet to receive a 
response.

If a response isn't obtained in a timely manner, I can relicense this 
instance under the GPL and contribute it to GCC.  That will resolve the 
immediate issue, but get a downstream headache when others add, for 
instance, clang module map functionality to it (we intend adding libcody 
to clang).  There are two other minor contributors to libcody -- Iain 
Sandoe and John Ravi, I can either get their permission for relicensing, 
or remove their contributions.

(John Ravi used libcody for his GSOC project showing a PoC joining LTO 
up to Make.)

If it is not acceptable to add libcody as a new top-level directory, I 
could, in addition to relicensing, place its sources directly in the cp 
directory, making it very tightly coupled to the compiler.  This would 
introduce unfortunate technical debt though.


-- 
Nathan Sidwell

[-- Attachment #2: 23-libcody.diff --]
[-- Type: text/x-patch, Size: 537010 bytes --]

diff --git c/Makefile.def w/Makefile.def
index 36fd26b0367..3bb3a811dde 100644
--- c/Makefile.def
+++ w/Makefile.def
@@ -81,6 +81,15 @@ host_modules= { module= itcl; };
 host_modules= { module= ld; bootstrap=true; };
 host_modules= { module= libbacktrace; bootstrap=true; };
 host_modules= { module= libcpp; bootstrap=true; };
+// As with libiconv, don't install any of libcody
+host_modules= { module= libcody; bootstrap=true;
+		no_install= true;
+		missing= pdf;
+		missing= html;
+		missing= info;
+		missing= install-pdf;
+		missing= install-html;
+		missing= install-info; };
 host_modules= { module= libdecnumber; bootstrap=true; };
 host_modules= { module= libgui; };
 host_modules= { module= libiberty; bootstrap=true;
@@ -347,6 +356,7 @@ dependencies = { module=all-gcc; on=all-build-libcpp; };
 dependencies = { module=all-gcc; on=all-zlib; };
 dependencies = { module=all-gcc; on=all-libbacktrace; hard=true; };
 dependencies = { module=all-gcc; on=all-libcpp; hard=true; };
+dependencies = { module=all-gcc; on=all-libcody; hard=true; };
 dependencies = { module=all-gcc; on=all-libdecnumber; hard=true; };
 dependencies = { module=all-gcc; on=all-libiberty; };
 dependencies = { module=all-gcc; on=all-fixincludes; };
diff --git c/Makefile.in w/Makefile.in
index 36e369df6e7..d9d2d1d8e6a 100644
--- c/Makefile.in
+++ w/Makefile.in
@@ -1017,6 +1017,7 @@ configure-host:  \
     maybe-configure-ld \
     maybe-configure-libbacktrace \
     maybe-configure-libcpp \
+    maybe-configure-libcody \
     maybe-configure-libdecnumber \
     maybe-configure-libgui \
     maybe-configure-libiberty \
@@ -1163,6 +1164,9 @@ all-host: maybe-all-libbacktrace
 @if libcpp-no-bootstrap
 all-host: maybe-all-libcpp
 @endif libcpp-no-bootstrap
+@if libcody-no-bootstrap
+all-host: maybe-all-libcody
+@endif libcody-no-bootstrap
 @if libdecnumber-no-bootstrap
 all-host: maybe-all-libdecnumber
 @endif libdecnumber-no-bootstrap
@@ -1281,6 +1285,7 @@ info-host: maybe-info-itcl
 info-host: maybe-info-ld
 info-host: maybe-info-libbacktrace
 info-host: maybe-info-libcpp
+info-host: maybe-info-libcody
 info-host: maybe-info-libdecnumber
 info-host: maybe-info-libgui
 info-host: maybe-info-libiberty
@@ -1370,6 +1375,7 @@ dvi-host: maybe-dvi-itcl
 dvi-host: maybe-dvi-ld
 dvi-host: maybe-dvi-libbacktrace
 dvi-host: maybe-dvi-libcpp
+dvi-host: maybe-dvi-libcody
 dvi-host: maybe-dvi-libdecnumber
 dvi-host: maybe-dvi-libgui
 dvi-host: maybe-dvi-libiberty
@@ -1459,6 +1465,7 @@ pdf-host: maybe-pdf-itcl
 pdf-host: maybe-pdf-ld
 pdf-host: maybe-pdf-libbacktrace
 pdf-host: maybe-pdf-libcpp
+pdf-host: maybe-pdf-libcody
 pdf-host: maybe-pdf-libdecnumber
 pdf-host: maybe-pdf-libgui
 pdf-host: maybe-pdf-libiberty
@@ -1548,6 +1555,7 @@ html-host: maybe-html-itcl
 html-host: maybe-html-ld
 html-host: maybe-html-libbacktrace
 html-host: maybe-html-libcpp
+html-host: maybe-html-libcody
 html-host: maybe-html-libdecnumber
 html-host: maybe-html-libgui
 html-host: maybe-html-libiberty
@@ -1637,6 +1645,7 @@ TAGS-host: maybe-TAGS-itcl
 TAGS-host: maybe-TAGS-ld
 TAGS-host: maybe-TAGS-libbacktrace
 TAGS-host: maybe-TAGS-libcpp
+TAGS-host: maybe-TAGS-libcody
 TAGS-host: maybe-TAGS-libdecnumber
 TAGS-host: maybe-TAGS-libgui
 TAGS-host: maybe-TAGS-libiberty
@@ -1726,6 +1735,7 @@ install-info-host: maybe-install-info-itcl
 install-info-host: maybe-install-info-ld
 install-info-host: maybe-install-info-libbacktrace
 install-info-host: maybe-install-info-libcpp
+install-info-host: maybe-install-info-libcody
 install-info-host: maybe-install-info-libdecnumber
 install-info-host: maybe-install-info-libgui
 install-info-host: maybe-install-info-libiberty
@@ -1815,6 +1825,7 @@ install-pdf-host: maybe-install-pdf-itcl
 install-pdf-host: maybe-install-pdf-ld
 install-pdf-host: maybe-install-pdf-libbacktrace
 install-pdf-host: maybe-install-pdf-libcpp
+install-pdf-host: maybe-install-pdf-libcody
 install-pdf-host: maybe-install-pdf-libdecnumber
 install-pdf-host: maybe-install-pdf-libgui
 install-pdf-host: maybe-install-pdf-libiberty
@@ -1904,6 +1915,7 @@ install-html-host: maybe-install-html-itcl
 install-html-host: maybe-install-html-ld
 install-html-host: maybe-install-html-libbacktrace
 install-html-host: maybe-install-html-libcpp
+install-html-host: maybe-install-html-libcody
 install-html-host: maybe-install-html-libdecnumber
 install-html-host: maybe-install-html-libgui
 install-html-host: maybe-install-html-libiberty
@@ -1993,6 +2005,7 @@ installcheck-host: maybe-installcheck-itcl
 installcheck-host: maybe-installcheck-ld
 installcheck-host: maybe-installcheck-libbacktrace
 installcheck-host: maybe-installcheck-libcpp
+installcheck-host: maybe-installcheck-libcody
 installcheck-host: maybe-installcheck-libdecnumber
 installcheck-host: maybe-installcheck-libgui
 installcheck-host: maybe-installcheck-libiberty
@@ -2082,6 +2095,7 @@ mostlyclean-host: maybe-mostlyclean-itcl
 mostlyclean-host: maybe-mostlyclean-ld
 mostlyclean-host: maybe-mostlyclean-libbacktrace
 mostlyclean-host: maybe-mostlyclean-libcpp
+mostlyclean-host: maybe-mostlyclean-libcody
 mostlyclean-host: maybe-mostlyclean-libdecnumber
 mostlyclean-host: maybe-mostlyclean-libgui
 mostlyclean-host: maybe-mostlyclean-libiberty
@@ -2171,6 +2185,7 @@ clean-host: maybe-clean-itcl
 clean-host: maybe-clean-ld
 clean-host: maybe-clean-libbacktrace
 clean-host: maybe-clean-libcpp
+clean-host: maybe-clean-libcody
 clean-host: maybe-clean-libdecnumber
 clean-host: maybe-clean-libgui
 clean-host: maybe-clean-libiberty
@@ -2260,6 +2275,7 @@ distclean-host: maybe-distclean-itcl
 distclean-host: maybe-distclean-ld
 distclean-host: maybe-distclean-libbacktrace
 distclean-host: maybe-distclean-libcpp
+distclean-host: maybe-distclean-libcody
 distclean-host: maybe-distclean-libdecnumber
 distclean-host: maybe-distclean-libgui
 distclean-host: maybe-distclean-libiberty
@@ -2349,6 +2365,7 @@ maintainer-clean-host: maybe-maintainer-clean-itcl
 maintainer-clean-host: maybe-maintainer-clean-ld
 maintainer-clean-host: maybe-maintainer-clean-libbacktrace
 maintainer-clean-host: maybe-maintainer-clean-libcpp
+maintainer-clean-host: maybe-maintainer-clean-libcody
 maintainer-clean-host: maybe-maintainer-clean-libdecnumber
 maintainer-clean-host: maybe-maintainer-clean-libgui
 maintainer-clean-host: maybe-maintainer-clean-libiberty
@@ -2494,6 +2511,7 @@ check-host:  \
     maybe-check-ld \
     maybe-check-libbacktrace \
     maybe-check-libcpp \
+    maybe-check-libcody \
     maybe-check-libdecnumber \
     maybe-check-libgui \
     maybe-check-libiberty \
@@ -2630,6 +2648,7 @@ install-host-nogcc:  \
     maybe-install-ld \
     maybe-install-libbacktrace \
     maybe-install-libcpp \
+    maybe-install-libcody \
     maybe-install-libdecnumber \
     maybe-install-libgui \
     maybe-install-libiberty \
@@ -2683,6 +2702,7 @@ install-host:  \
     maybe-install-ld \
     maybe-install-libbacktrace \
     maybe-install-libcpp \
+    maybe-install-libcody \
     maybe-install-libdecnumber \
     maybe-install-libgui \
     maybe-install-libiberty \
@@ -2792,6 +2812,7 @@ install-strip-host:  \
     maybe-install-strip-ld \
     maybe-install-strip-libbacktrace \
     maybe-install-strip-libcpp \
+    maybe-install-strip-libcody \
     maybe-install-strip-libdecnumber \
     maybe-install-strip-libgui \
     maybe-install-strip-libiberty \
@@ -25050,6 +25071,1015 @@ maintainer-clean-libcpp:
 
 
 
+.PHONY: configure-libcody maybe-configure-libcody
+maybe-configure-libcody:
+@if gcc-bootstrap
+configure-libcody: stage_current
+@endif gcc-bootstrap
+@if libcody
+maybe-configure-libcody: configure-libcody
+configure-libcody: 
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	$(HOST_EXPORTS)  \
+	echo Configuring in $(HOST_SUBDIR)/libcody; \
+	cd "$(HOST_SUBDIR)/libcody" || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) \
+	  $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias}  \
+	  || exit 1
+@endif libcody
+
+
+
+.PHONY: configure-stage1-libcody maybe-configure-stage1-libcody
+maybe-configure-stage1-libcody:
+@if libcody-bootstrap
+maybe-configure-stage1-libcody: configure-stage1-libcody
+configure-stage1-libcody:
+	@[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE1_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	CFLAGS="$(STAGE1_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGE1_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(LIBCFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage 1 in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	   \
+	  $(STAGE1_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stage2-libcody maybe-configure-stage2-libcody
+maybe-configure-stage2-libcody:
+@if libcody-bootstrap
+maybe-configure-stage2-libcody: configure-stage2-libcody
+configure-stage2-libcody:
+	@[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE2_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGE2_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGE2_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGE2_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage 2 in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGE2_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stage3-libcody maybe-configure-stage3-libcody
+maybe-configure-stage3-libcody:
+@if libcody-bootstrap
+maybe-configure-stage3-libcody: configure-stage3-libcody
+configure-stage3-libcody:
+	@[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE3_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGE3_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGE3_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGE3_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage 3 in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGE3_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stage4-libcody maybe-configure-stage4-libcody
+maybe-configure-stage4-libcody:
+@if libcody-bootstrap
+maybe-configure-stage4-libcody: configure-stage4-libcody
+configure-stage4-libcody:
+	@[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE4_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGE4_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGE4_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGE4_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage 4 in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGE4_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stageprofile-libcody maybe-configure-stageprofile-libcody
+maybe-configure-stageprofile-libcody:
+@if libcody-bootstrap
+maybe-configure-stageprofile-libcody: configure-stageprofile-libcody
+configure-stageprofile-libcody:
+	@[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEprofile_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGEprofile_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGEprofile_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGEprofile_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage profile in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGEprofile_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stagetrain-libcody maybe-configure-stagetrain-libcody
+maybe-configure-stagetrain-libcody:
+@if libcody-bootstrap
+maybe-configure-stagetrain-libcody: configure-stagetrain-libcody
+configure-stagetrain-libcody:
+	@[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEtrain_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGEtrain_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGEtrain_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGEtrain_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage train in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGEtrain_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stagefeedback-libcody maybe-configure-stagefeedback-libcody
+maybe-configure-stagefeedback-libcody:
+@if libcody-bootstrap
+maybe-configure-stagefeedback-libcody: configure-stagefeedback-libcody
+configure-stagefeedback-libcody:
+	@[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGEfeedback_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGEfeedback_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGEfeedback_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage feedback in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGEfeedback_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stageautoprofile-libcody maybe-configure-stageautoprofile-libcody
+maybe-configure-stageautoprofile-libcody:
+@if libcody-bootstrap
+maybe-configure-stageautoprofile-libcody: configure-stageautoprofile-libcody
+configure-stageautoprofile-libcody:
+	@[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGEautoprofile_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGEautoprofile_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGEautoprofile_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage autoprofile in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGEautoprofile_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+.PHONY: configure-stageautofeedback-libcody maybe-configure-stageautofeedback-libcody
+maybe-configure-stageautofeedback-libcody:
+@if libcody-bootstrap
+maybe-configure-stageautofeedback-libcody: configure-stageautofeedback-libcody
+configure-stageautofeedback-libcody:
+	@[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+	@$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+	test ! -f $(HOST_SUBDIR)/libcody/Makefile || exit 0; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS) \
+	CFLAGS="$(STAGEautofeedback_CFLAGS)"; export CFLAGS; \
+	CXXFLAGS="$(STAGEautofeedback_CXXFLAGS)"; export CXXFLAGS; \
+	LIBCFLAGS="$(STAGEautofeedback_CFLAGS)"; export LIBCFLAGS;  \
+	echo Configuring stage autofeedback in $(HOST_SUBDIR)/libcody; \
+	$(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/libcody; \
+	cd $(HOST_SUBDIR)/libcody || exit 1; \
+	case $(srcdir) in \
+	  /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+	  *) topdir=`echo $(HOST_SUBDIR)/libcody/ | \
+		sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+	esac; \
+	module_srcdir=libcody; \
+	$(SHELL) $$s/$$module_srcdir/configure \
+	  --srcdir=$${topdir}/$$module_srcdir \
+	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
+	  --target=${target_alias} \
+	  --with-build-libsubdir=$(HOST_SUBDIR) \
+	  $(STAGEautofeedback_CONFIGURE_FLAGS)
+@endif libcody-bootstrap
+
+
+
+
+
+.PHONY: all-libcody maybe-all-libcody
+maybe-all-libcody:
+@if gcc-bootstrap
+all-libcody: stage_current
+@endif gcc-bootstrap
+@if libcody
+TARGET-libcody=all
+maybe-all-libcody: all-libcody
+all-libcody: configure-libcody
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS)  \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) $(EXTRA_HOST_FLAGS) $(STAGE1_FLAGS_TO_PASS)  \
+		$(TARGET-libcody))
+@endif libcody
+
+
+
+.PHONY: all-stage1-libcody maybe-all-stage1-libcody
+.PHONY: clean-stage1-libcody maybe-clean-stage1-libcody
+maybe-all-stage1-libcody:
+maybe-clean-stage1-libcody:
+@if libcody-bootstrap
+maybe-all-stage1-libcody: all-stage1-libcody
+all-stage1: all-stage1-libcody
+TARGET-stage1-libcody = $(TARGET-libcody)
+all-stage1-libcody: configure-stage1-libcody
+	@[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE1_TFLAGS)"; \
+	$(HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGE1_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGE1_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGE1_CXXFLAGS)" \
+		LIBCFLAGS="$(LIBCFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS)  \
+		$(STAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGE1_TFLAGS)"  \
+		$(TARGET-stage1-libcody)
+
+maybe-clean-stage1-libcody: clean-stage1-libcody
+clean-stage1: clean-stage1-libcody
+clean-stage1-libcody:
+	@if [ $(current_stage) = stage1 ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stage1-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stage1-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS)  \
+	$(STAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stage2-libcody maybe-all-stage2-libcody
+.PHONY: clean-stage2-libcody maybe-clean-stage2-libcody
+maybe-all-stage2-libcody:
+maybe-clean-stage2-libcody:
+@if libcody-bootstrap
+maybe-all-stage2-libcody: all-stage2-libcody
+all-stage2: all-stage2-libcody
+TARGET-stage2-libcody = $(TARGET-libcody)
+all-stage2-libcody: configure-stage2-libcody
+	@[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE2_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGE2_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGE2_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGE2_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGE2_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGE2_TFLAGS)"  \
+		$(TARGET-stage2-libcody)
+
+maybe-clean-stage2-libcody: clean-stage2-libcody
+clean-stage2: clean-stage2-libcody
+clean-stage2-libcody:
+	@if [ $(current_stage) = stage2 ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stage2-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stage2-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stage3-libcody maybe-all-stage3-libcody
+.PHONY: clean-stage3-libcody maybe-clean-stage3-libcody
+maybe-all-stage3-libcody:
+maybe-clean-stage3-libcody:
+@if libcody-bootstrap
+maybe-all-stage3-libcody: all-stage3-libcody
+all-stage3: all-stage3-libcody
+TARGET-stage3-libcody = $(TARGET-libcody)
+all-stage3-libcody: configure-stage3-libcody
+	@[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE3_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGE3_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGE3_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGE3_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGE3_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGE3_TFLAGS)"  \
+		$(TARGET-stage3-libcody)
+
+maybe-clean-stage3-libcody: clean-stage3-libcody
+clean-stage3: clean-stage3-libcody
+clean-stage3-libcody:
+	@if [ $(current_stage) = stage3 ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stage3-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stage3-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stage4-libcody maybe-all-stage4-libcody
+.PHONY: clean-stage4-libcody maybe-clean-stage4-libcody
+maybe-all-stage4-libcody:
+maybe-clean-stage4-libcody:
+@if libcody-bootstrap
+maybe-all-stage4-libcody: all-stage4-libcody
+all-stage4: all-stage4-libcody
+TARGET-stage4-libcody = $(TARGET-libcody)
+all-stage4-libcody: configure-stage4-libcody
+	@[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGE4_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGE4_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGE4_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGE4_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGE4_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGE4_TFLAGS)"  \
+		$(TARGET-stage4-libcody)
+
+maybe-clean-stage4-libcody: clean-stage4-libcody
+clean-stage4: clean-stage4-libcody
+clean-stage4-libcody:
+	@if [ $(current_stage) = stage4 ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stage4-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stage4-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stageprofile-libcody maybe-all-stageprofile-libcody
+.PHONY: clean-stageprofile-libcody maybe-clean-stageprofile-libcody
+maybe-all-stageprofile-libcody:
+maybe-clean-stageprofile-libcody:
+@if libcody-bootstrap
+maybe-all-stageprofile-libcody: all-stageprofile-libcody
+all-stageprofile: all-stageprofile-libcody
+TARGET-stageprofile-libcody = $(TARGET-libcody)
+all-stageprofile-libcody: configure-stageprofile-libcody
+	@[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEprofile_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGEprofile_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGEprofile_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGEprofile_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGEprofile_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGEprofile_TFLAGS)"  \
+		$(TARGET-stageprofile-libcody)
+
+maybe-clean-stageprofile-libcody: clean-stageprofile-libcody
+clean-stageprofile: clean-stageprofile-libcody
+clean-stageprofile-libcody:
+	@if [ $(current_stage) = stageprofile ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stageprofile-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stageprofile-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stagetrain-libcody maybe-all-stagetrain-libcody
+.PHONY: clean-stagetrain-libcody maybe-clean-stagetrain-libcody
+maybe-all-stagetrain-libcody:
+maybe-clean-stagetrain-libcody:
+@if libcody-bootstrap
+maybe-all-stagetrain-libcody: all-stagetrain-libcody
+all-stagetrain: all-stagetrain-libcody
+TARGET-stagetrain-libcody = $(TARGET-libcody)
+all-stagetrain-libcody: configure-stagetrain-libcody
+	@[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEtrain_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGEtrain_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGEtrain_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGEtrain_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGEtrain_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGEtrain_TFLAGS)"  \
+		$(TARGET-stagetrain-libcody)
+
+maybe-clean-stagetrain-libcody: clean-stagetrain-libcody
+clean-stagetrain: clean-stagetrain-libcody
+clean-stagetrain-libcody:
+	@if [ $(current_stage) = stagetrain ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stagetrain-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stagetrain-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stagefeedback-libcody maybe-all-stagefeedback-libcody
+.PHONY: clean-stagefeedback-libcody maybe-clean-stagefeedback-libcody
+maybe-all-stagefeedback-libcody:
+maybe-clean-stagefeedback-libcody:
+@if libcody-bootstrap
+maybe-all-stagefeedback-libcody: all-stagefeedback-libcody
+all-stagefeedback: all-stagefeedback-libcody
+TARGET-stagefeedback-libcody = $(TARGET-libcody)
+all-stagefeedback-libcody: configure-stagefeedback-libcody
+	@[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGEfeedback_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGEfeedback_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGEfeedback_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGEfeedback_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGEfeedback_TFLAGS)"  \
+		$(TARGET-stagefeedback-libcody)
+
+maybe-clean-stagefeedback-libcody: clean-stagefeedback-libcody
+clean-stagefeedback: clean-stagefeedback-libcody
+clean-stagefeedback-libcody:
+	@if [ $(current_stage) = stagefeedback ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stagefeedback-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stagefeedback-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stageautoprofile-libcody maybe-all-stageautoprofile-libcody
+.PHONY: clean-stageautoprofile-libcody maybe-clean-stageautoprofile-libcody
+maybe-all-stageautoprofile-libcody:
+maybe-clean-stageautoprofile-libcody:
+@if libcody-bootstrap
+maybe-all-stageautoprofile-libcody: all-stageautoprofile-libcody
+all-stageautoprofile: all-stageautoprofile-libcody
+TARGET-stageautoprofile-libcody = $(TARGET-libcody)
+all-stageautoprofile-libcody: configure-stageautoprofile-libcody
+	@[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	$$s/gcc/config/i386/$(AUTO_PROFILE) \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGEautoprofile_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGEautoprofile_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGEautoprofile_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGEautoprofile_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGEautoprofile_TFLAGS)"  \
+		$(TARGET-stageautoprofile-libcody)
+
+maybe-clean-stageautoprofile-libcody: clean-stageautoprofile-libcody
+clean-stageautoprofile: clean-stageautoprofile-libcody
+clean-stageautoprofile-libcody:
+	@if [ $(current_stage) = stageautoprofile ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stageautoprofile-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stageautoprofile-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+.PHONY: all-stageautofeedback-libcody maybe-all-stageautofeedback-libcody
+.PHONY: clean-stageautofeedback-libcody maybe-clean-stageautofeedback-libcody
+maybe-all-stageautofeedback-libcody:
+maybe-clean-stageautofeedback-libcody:
+@if libcody-bootstrap
+maybe-all-stageautofeedback-libcody: all-stageautofeedback-libcody
+all-stageautofeedback: all-stageautofeedback-libcody
+TARGET-stageautofeedback-libcody = $(TARGET-libcody)
+all-stageautofeedback-libcody: configure-stageautofeedback-libcody
+	@[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+	$(HOST_EXPORTS) \
+	$(POSTSTAGE1_HOST_EXPORTS)  \
+	cd $(HOST_SUBDIR)/libcody && \
+	 \
+	$(MAKE) $(BASE_FLAGS_TO_PASS) \
+		CFLAGS="$(STAGEautofeedback_CFLAGS)" \
+		GENERATOR_CFLAGS="$(STAGEautofeedback_GENERATOR_CFLAGS)" \
+		CXXFLAGS="$(STAGEautofeedback_CXXFLAGS)" \
+		LIBCFLAGS="$(STAGEautofeedback_CFLAGS)" \
+		CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+		CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+		LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+		$(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  \
+		TFLAGS="$(STAGEautofeedback_TFLAGS)" PERF_DATA=perf.data \
+		$(TARGET-stageautofeedback-libcody)
+
+maybe-clean-stageautofeedback-libcody: clean-stageautofeedback-libcody
+clean-stageautofeedback: clean-stageautofeedback-libcody
+clean-stageautofeedback-libcody:
+	@if [ $(current_stage) = stageautofeedback ]; then \
+	  [ -f $(HOST_SUBDIR)/libcody/Makefile ] || exit 0; \
+	else \
+	  [ -f $(HOST_SUBDIR)/stageautofeedback-libcody/Makefile ] || exit 0; \
+	  $(MAKE) stageautofeedback-start; \
+	fi; \
+	cd $(HOST_SUBDIR)/libcody && \
+	$(MAKE) $(EXTRA_HOST_FLAGS) $(POSTSTAGE1_FLAGS_TO_PASS)  clean
+@endif libcody-bootstrap
+
+
+
+
+
+.PHONY: check-libcody maybe-check-libcody
+maybe-check-libcody:
+@if libcody
+maybe-check-libcody: check-libcody
+
+check-libcody:
+	@: $(MAKE); $(unstage)
+	@r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) $(EXTRA_HOST_EXPORTS) \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(FLAGS_TO_PASS)  $(EXTRA_BOOTSTRAP_FLAGS) check)
+
+@endif libcody
+
+.PHONY: install-libcody maybe-install-libcody
+maybe-install-libcody:
+@if libcody
+maybe-install-libcody: install-libcody
+
+install-libcody:
+
+@endif libcody
+
+.PHONY: install-strip-libcody maybe-install-strip-libcody
+maybe-install-strip-libcody:
+@if libcody
+maybe-install-strip-libcody: install-strip-libcody
+
+install-strip-libcody:
+
+@endif libcody
+
+# Other targets (info, dvi, pdf, etc.)
+
+.PHONY: maybe-info-libcody info-libcody
+maybe-info-libcody:
+@if libcody
+maybe-info-libcody: info-libcody
+
+# libcody doesn't support info.
+info-libcody:
+
+@endif libcody
+
+.PHONY: maybe-dvi-libcody dvi-libcody
+maybe-dvi-libcody:
+@if libcody
+maybe-dvi-libcody: dvi-libcody
+
+dvi-libcody: \
+    configure-libcody 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing dvi in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          dvi) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-pdf-libcody pdf-libcody
+maybe-pdf-libcody:
+@if libcody
+maybe-pdf-libcody: pdf-libcody
+
+# libcody doesn't support pdf.
+pdf-libcody:
+
+@endif libcody
+
+.PHONY: maybe-html-libcody html-libcody
+maybe-html-libcody:
+@if libcody
+maybe-html-libcody: html-libcody
+
+# libcody doesn't support html.
+html-libcody:
+
+@endif libcody
+
+.PHONY: maybe-TAGS-libcody TAGS-libcody
+maybe-TAGS-libcody:
+@if libcody
+maybe-TAGS-libcody: TAGS-libcody
+
+TAGS-libcody: \
+    configure-libcody 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing TAGS in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          TAGS) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-install-info-libcody install-info-libcody
+maybe-install-info-libcody:
+@if libcody
+maybe-install-info-libcody: install-info-libcody
+
+# libcody doesn't support install-info.
+install-info-libcody:
+
+@endif libcody
+
+.PHONY: maybe-install-pdf-libcody install-pdf-libcody
+maybe-install-pdf-libcody:
+@if libcody
+maybe-install-pdf-libcody: install-pdf-libcody
+
+# libcody doesn't support install-pdf.
+install-pdf-libcody:
+
+@endif libcody
+
+.PHONY: maybe-install-html-libcody install-html-libcody
+maybe-install-html-libcody:
+@if libcody
+maybe-install-html-libcody: install-html-libcody
+
+# libcody doesn't support install-html.
+install-html-libcody:
+
+@endif libcody
+
+.PHONY: maybe-installcheck-libcody installcheck-libcody
+maybe-installcheck-libcody:
+@if libcody
+maybe-installcheck-libcody: installcheck-libcody
+
+installcheck-libcody: \
+    configure-libcody 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing installcheck in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          installcheck) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-mostlyclean-libcody mostlyclean-libcody
+maybe-mostlyclean-libcody:
+@if libcody
+maybe-mostlyclean-libcody: mostlyclean-libcody
+
+mostlyclean-libcody: 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing mostlyclean in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          mostlyclean) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-clean-libcody clean-libcody
+maybe-clean-libcody:
+@if libcody
+maybe-clean-libcody: clean-libcody
+
+clean-libcody: 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing clean in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          clean) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-distclean-libcody distclean-libcody
+maybe-distclean-libcody:
+@if libcody
+maybe-distclean-libcody: distclean-libcody
+
+distclean-libcody: 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing distclean in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          distclean) \
+	  || exit 1
+
+@endif libcody
+
+.PHONY: maybe-maintainer-clean-libcody maintainer-clean-libcody
+maybe-maintainer-clean-libcody:
+@if libcody
+maybe-maintainer-clean-libcody: maintainer-clean-libcody
+
+maintainer-clean-libcody: 
+	@[ -f ./libcody/Makefile ] || exit 0; \
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	for flag in $(EXTRA_HOST_FLAGS) ; do \
+	  eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \
+	done; \
+	echo "Doing maintainer-clean in libcody"; \
+	(cd $(HOST_SUBDIR)/libcody && \
+	  $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \
+	          "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \
+	          "RANLIB=$${RANLIB}" \
+	          "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" "WINDMC=$${WINDMC}" \
+	          maintainer-clean) \
+	  || exit 1
+
+@endif libcody
+
+
+
 .PHONY: configure-libdecnumber maybe-configure-libdecnumber
 maybe-configure-libdecnumber:
 @if gcc-bootstrap
@@ -55705,6 +56735,11 @@ stage1-start::
 	  mkdir stage1-libcpp; \
 	mv stage1-libcpp libcpp
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stage1-libcody ] || \
+	  mkdir stage1-libcody; \
+	mv stage1-libcody libcody
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stage1-libdecnumber ] || \
 	  mkdir stage1-libdecnumber; \
@@ -55825,6 +56860,11 @@ stage1-end::
 	  cd $(HOST_SUBDIR); mv libcpp stage1-libcpp; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stage1-libcody; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stage1-libdecnumber; \
@@ -56004,6 +57044,12 @@ stage2-start::
 	mv stage2-libcpp libcpp; \
 	mv stage1-libcpp prev-libcpp || test -f stage1-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stage2-libcody ] || \
+	  mkdir stage2-libcody; \
+	mv stage2-libcody libcody; \
+	mv stage1-libcody prev-libcody || test -f stage1-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stage2-libdecnumber ] || \
 	  mkdir stage2-libdecnumber; \
@@ -56148,6 +57194,12 @@ stage2-end::
 	  mv prev-libcpp stage1-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stage2-libcody; \
+	  mv prev-libcody stage1-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stage2-libdecnumber; \
@@ -56358,6 +57410,12 @@ stage3-start::
 	mv stage3-libcpp libcpp; \
 	mv stage2-libcpp prev-libcpp || test -f stage2-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stage3-libcody ] || \
+	  mkdir stage3-libcody; \
+	mv stage3-libcody libcody; \
+	mv stage2-libcody prev-libcody || test -f stage2-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stage3-libdecnumber ] || \
 	  mkdir stage3-libdecnumber; \
@@ -56502,6 +57560,12 @@ stage3-end::
 	  mv prev-libcpp stage2-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stage3-libcody; \
+	  mv prev-libcody stage2-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stage3-libdecnumber; \
@@ -56768,6 +57832,12 @@ stage4-start::
 	mv stage4-libcpp libcpp; \
 	mv stage3-libcpp prev-libcpp || test -f stage3-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stage4-libcody ] || \
+	  mkdir stage4-libcody; \
+	mv stage4-libcody libcody; \
+	mv stage3-libcody prev-libcody || test -f stage3-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stage4-libdecnumber ] || \
 	  mkdir stage4-libdecnumber; \
@@ -56912,6 +57982,12 @@ stage4-end::
 	  mv prev-libcpp stage3-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stage4-libcody; \
+	  mv prev-libcody stage3-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stage4-libdecnumber; \
@@ -57166,6 +58242,12 @@ stageprofile-start::
 	mv stageprofile-libcpp libcpp; \
 	mv stage1-libcpp prev-libcpp || test -f stage1-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stageprofile-libcody ] || \
+	  mkdir stageprofile-libcody; \
+	mv stageprofile-libcody libcody; \
+	mv stage1-libcody prev-libcody || test -f stage1-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stageprofile-libdecnumber ] || \
 	  mkdir stageprofile-libdecnumber; \
@@ -57310,6 +58392,12 @@ stageprofile-end::
 	  mv prev-libcpp stage1-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stageprofile-libcody; \
+	  mv prev-libcody stage1-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stageprofile-libdecnumber; \
@@ -57497,6 +58585,12 @@ stagetrain-start::
 	mv stagetrain-libcpp libcpp; \
 	mv stageprofile-libcpp prev-libcpp || test -f stageprofile-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stagetrain-libcody ] || \
+	  mkdir stagetrain-libcody; \
+	mv stagetrain-libcody libcody; \
+	mv stageprofile-libcody prev-libcody || test -f stageprofile-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stagetrain-libdecnumber ] || \
 	  mkdir stagetrain-libdecnumber; \
@@ -57641,6 +58735,12 @@ stagetrain-end::
 	  mv prev-libcpp stageprofile-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stagetrain-libcody; \
+	  mv prev-libcody stageprofile-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stagetrain-libdecnumber; \
@@ -57828,6 +58928,12 @@ stagefeedback-start::
 	mv stagefeedback-libcpp libcpp; \
 	mv stagetrain-libcpp prev-libcpp || test -f stagetrain-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stagefeedback-libcody ] || \
+	  mkdir stagefeedback-libcody; \
+	mv stagefeedback-libcody libcody; \
+	mv stagetrain-libcody prev-libcody || test -f stagetrain-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stagefeedback-libdecnumber ] || \
 	  mkdir stagefeedback-libdecnumber; \
@@ -57972,6 +59078,12 @@ stagefeedback-end::
 	  mv prev-libcpp stagetrain-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stagefeedback-libcody; \
+	  mv prev-libcody stagetrain-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stagefeedback-libdecnumber; \
@@ -58182,6 +59294,12 @@ stageautoprofile-start::
 	mv stageautoprofile-libcpp libcpp; \
 	mv stage1-libcpp prev-libcpp || test -f stage1-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stageautoprofile-libcody ] || \
+	  mkdir stageautoprofile-libcody; \
+	mv stageautoprofile-libcody libcody; \
+	mv stage1-libcody prev-libcody || test -f stage1-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stageautoprofile-libdecnumber ] || \
 	  mkdir stageautoprofile-libdecnumber; \
@@ -58326,6 +59444,12 @@ stageautoprofile-end::
 	  mv prev-libcpp stage1-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stageautoprofile-libcody; \
+	  mv prev-libcody stage1-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stageautoprofile-libdecnumber; \
@@ -58513,6 +59637,12 @@ stageautofeedback-start::
 	mv stageautofeedback-libcpp libcpp; \
 	mv stageautoprofile-libcpp prev-libcpp || test -f stageautoprofile-lean 
 @endif libcpp
+@if libcody
+	@cd $(HOST_SUBDIR); [ -d stageautofeedback-libcody ] || \
+	  mkdir stageautofeedback-libcody; \
+	mv stageautofeedback-libcody libcody; \
+	mv stageautoprofile-libcody prev-libcody || test -f stageautoprofile-lean 
+@endif libcody
 @if libdecnumber
 	@cd $(HOST_SUBDIR); [ -d stageautofeedback-libdecnumber ] || \
 	  mkdir stageautofeedback-libdecnumber; \
@@ -58657,6 +59787,12 @@ stageautofeedback-end::
 	  mv prev-libcpp stageautoprofile-libcpp; : ; \
 	fi
 @endif libcpp
+@if libcody
+	@if test -d $(HOST_SUBDIR)/libcody; then \
+	  cd $(HOST_SUBDIR); mv libcody stageautofeedback-libcody; \
+	  mv prev-libcody stageautoprofile-libcody; : ; \
+	fi
+@endif libcody
 @if libdecnumber
 	@if test -d $(HOST_SUBDIR)/libdecnumber; then \
 	  cd $(HOST_SUBDIR); mv libdecnumber stageautofeedback-libdecnumber; \
@@ -59185,6 +60321,16 @@ all-stagetrain-gcc: all-stagetrain-libcpp
 all-stagefeedback-gcc: all-stagefeedback-libcpp
 all-stageautoprofile-gcc: all-stageautoprofile-libcpp
 all-stageautofeedback-gcc: all-stageautofeedback-libcpp
+all-gcc: all-libcody
+all-stage1-gcc: all-stage1-libcody
+all-stage2-gcc: all-stage2-libcody
+all-stage3-gcc: all-stage3-libcody
+all-stage4-gcc: all-stage4-libcody
+all-stageprofile-gcc: all-stageprofile-libcody
+all-stagetrain-gcc: all-stagetrain-libcody
+all-stagefeedback-gcc: all-stagefeedback-libcody
+all-stageautoprofile-gcc: all-stageautoprofile-libcody
+all-stageautofeedback-gcc: all-stageautofeedback-libcody
 all-gcc: all-libdecnumber
 all-stage1-gcc: all-stage1-libdecnumber
 all-stage2-gcc: all-stage2-libdecnumber
diff --git c/configure w/configure
index a2ea1a329b6..158db088bd2 100755
--- c/configure
+++ w/configure
@@ -2787,7 +2787,7 @@ build_tools="build-texinfo build-flex build-bison build-m4 build-fixincludes"
 
 # these libraries are used by various programs built for the host environment
 #f
-host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktrace libcpp libdecnumber gmp mpfr mpc isl libelf libiconv libctf"
+host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktrace libcpp libcody libdecnumber gmp mpfr mpc isl libelf libiconv libctf"
 
 # these tools are built for the host environment
 # Note, the powerpc-eabi build depends on sim occurring before gdb in order to
diff --git c/configure.ac w/configure.ac
index 44fa75f3a32..e0d106e432b 100644
--- c/configure.ac
+++ w/configure.ac
@@ -132,7 +132,7 @@ build_tools="build-texinfo build-flex build-bison build-m4 build-fixincludes"
 
 # these libraries are used by various programs built for the host environment
 #f
-host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktrace libcpp libdecnumber gmp mpfr mpc isl libelf libiconv libctf"
+host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktrace libcpp libcody libdecnumber gmp mpfr mpc isl libelf libiconv libctf"
 
 # these tools are built for the host environment
 # Note, the powerpc-eabi build depends on sim occurring before gdb in order to
diff --git c/gcc/Makefile.in w/gcc/Makefile.in
index 7b94497b6f2..cf4c6f603ea 100644
--- c/gcc/Makefile.in
+++ w/gcc/Makefile.in
@@ -412,6 +412,9 @@ enable_as_accelerator = @enable_as_accelerator@
 CPPLIB = ../libcpp/libcpp.a
 CPPINC = -I$(srcdir)/../libcpp/include
 
+CODYLIB = ../libcody/libcody.a
+CODYINC = -I$(srcdir)/../libcody
+
 # Where to find decNumber
 enable_decimal_float = @enable_decimal_float@
 DECNUM = $(srcdir)/../libdecnumber
@@ -982,6 +985,7 @@ SYSTEM_H = system.h hwint.h $(srcdir)/../include/libiberty.h \
 PREDICT_H = predict.h predict.def
 CPPLIB_H = $(srcdir)/../libcpp/include/line-map.h \
 	$(srcdir)/../libcpp/include/cpplib.h
+CODYLIB_H = $(srcdir)/../libcody/cody.hh
 INPUT_H = $(srcdir)/../libcpp/include/line-map.h input.h
 OPTS_H = $(INPUT_H) $(VEC_H) opts.h $(OBSTACK_H)
 SYMTAB_H = $(srcdir)/../libcpp/include/symtab.h $(OBSTACK_H)
@@ -1102,7 +1106,7 @@ BUILD_ERRORS = build/errors.o
 # libintl.h will be found in ../intl if we are using the included libintl.
 INCLUDES = -I. -I$(@D) -I$(srcdir) -I$(srcdir)/$(@D) \
 	   -I$(srcdir)/../include @INCINTL@ \
-	   $(CPPINC) $(GMPINC) $(DECNUMINC) $(BACKTRACEINC) \
+	   $(CPPINC) $(CODYINC) $(GMPINC) $(DECNUMINC) $(BACKTRACEINC) \
 	   $(ISLINC)
 
 COMPILE.base = $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) -o $@
@@ -1711,7 +1715,7 @@ endif
 ALL_HOST_OBJS = $(ALL_HOST_FRONTEND_OBJS) $(ALL_HOST_BACKEND_OBJS)
 
 BACKEND = libbackend.a main.o libcommon-target.a libcommon.a \
-	$(CPPLIB) $(LIBDECNUMBER)
+	$(CPPLIB) $(CODYLIB) $(LIBDECNUMBER)
 
 # This is defined to "yes" if Tree checking is enabled, which roughly means
 # front-end checking.
diff --git c/libcody/CMakeLists.txt w/libcody/CMakeLists.txt
new file mode 100644
index 00000000000..72e59f955ea
--- /dev/null
+++ w/libcody/CMakeLists.txt
@@ -0,0 +1,121 @@
+# Top Level CMake file for libcody.
+
+cmake_minimum_required(VERSION 3.4.3)
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  message(STATUS "No build type selected, default to MinSizeRel")
+  set(CMAKE_BUILD_TYPE MinSizeRel)
+  set(LIBCODY_ENABLE_ASSERTIONS 1)
+endif()
+string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
+
+set(cmake_3_2_USES_TERMINAL USES_TERMINAL)
+
+if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE)
+  message(FATAL_ERROR "In-source builds are not allowed. ")
+endif()
+
+# message(STATUS "SRC ${CMAKE_SOURCE_DIR} CSRC : ${CMAKE_CURRENT_SOURCE_DIR} ")
+
+# Add path for custom modules
+set(CMAKE_MODULE_PATH
+    ${CMAKE_MODULE_PATH}
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake"
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
+
+# If we are building stand-alone, set up the names and versions.
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR
+   OR LIBCODY_STANDALONE_BUILD)
+
+  project(libcody CXX)
+
+  set(PACKAGE_NAME codylib)
+  set(LIBCODY_VERSION_MAJOR 0)
+  set(LIBCODY_VERSION_MINOR 0)
+  set(LIBCODY_VERSION_PATCH 1)
+  set(LIBCODY_VERSION_SUFFIX git)
+  set(LIBCODY_VERSION "${LIBCODY_VERSION_MAJOR}.${LIBCODY_VERSION_MINOR}.${LIBCODY_VERSION_PATCH}")
+  set(PACKAGE_VERSION "${LIBCODY_VERSION}-${LIBCODY_VERSION_SUFFIX}")
+  set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+  set(PACKAGE_URL "https://github.com/urnathan/libcody")
+  set(PACKAGE_BUGREPORT "https://github.com/urnathan/libcody/issues")
+
+  set (GIT_REV "git" "-C" "${CMAKE_CURRENT_SOURCE_DIR}" "rev-parse" "--short=12" "HEAD")
+  execute_process(
+      COMMAND ${GIT_REV}
+      RESULT_VARIABLE HAD_ERROR
+      OUTPUT_VARIABLE CODY_REVISION
+    )
+  if (NOT HAD_ERROR)
+    string(REGEX REPLACE "\n$" "" CODY_REVISION "${CODY_REVISION}")
+    set (GIT_CHANGES "git" "-C" "${CMAKE_CURRENT_SOURCE_DIR}" "diff-index" "--quiet" "HEAD" "--")
+    execute_process(
+      COMMAND ${GIT_CHANGES}
+      RESULT_VARIABLE MOD_ERROR
+      OUTPUT_VARIABLE MOD_OUTPUT
+    )
+    if (MOD_ERROR)
+      set (CODY_REVISION "${CODY_REVISION}-modified")
+    endif ()
+  else()
+    set(CODY_REVISION, "unknown")
+  endif ()
+  set(LIBCODY_STANDALONE YES)
+else()
+  set(LIBCODY_STANDALONE NO)
+endif()
+
+# We are using C++11
+set (CMAKE_CXX_STANDARD 11)
+
+message(STATUS "git revision ${CODY_REVISION} ")
+option(CODY_CHECKING "Enable checking" ON)
+# Address github issue #10
+option(CODY_WITHEXCEPTIONS "Enable exceptions" OFF)
+
+if (LIBCODY_STANDALONE)
+  include(CTest)
+endif()
+
+include(libcody-config-ix)
+
+add_definitions(
+ -DPACKAGE_URL="${PACKAGE_URL}"
+ -DBUGURL="${PACKAGE_BUGREPORT}"
+ -DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ -DPACKAGE_NAME="${PACKAGE_NAME}"
+ -DPACKAGE_STRING="${PACKAGE_STRING}"
+ -DPACKAGE_VERSION="${LIBCODY_VERSION}"
+ -DREVISION="${CODY_REVISION}"
+ )
+if (CODY_CHECKING)
+  add_definitions(-DNMS_CHECKING=1)
+else()
+  add_definitions(-DNMS_CHECKING=0)
+endif()
+
+set(LIBCODY_SOURCES
+  buffer.cc
+  client.cc
+  fatal.cc
+  netclient.cc
+  netserver.cc
+  resolver.cc
+  packet.cc
+  server.cc)
+
+if(LIBCODY_STANDALONE)
+  add_library(cody STATIC ${LIBCODY_SOURCES})
+else()
+  message(STATUS "Configured for in-tree build of libcody as LLVMcody")
+  add_llvm_component_library(LLVMcody ${LIBCODY_SOURCES})
+endif()
+
+if (LIBCODY_STANDALONE)
+
+  set_target_properties(cody PROPERTIES PUBLIC_HEADER "cody.hh")
+  install(TARGETS cody 
+        LIBRARY DESTINATION lib
+        PUBLIC_HEADER DESTINATION include
+  )
+endif()
diff --git c/libcody/CODING.md w/libcody/CODING.md
new file mode 100644
index 00000000000..1ff0a9da763
--- /dev/null
+++ w/libcody/CODING.md
@@ -0,0 +1,115 @@
+# Coding standard
+
+I guess I should document this, it might not be obvious.
+
+libcody is implemented in C++11.  Because it's used in compiler
+development, we can't use the latest and greatest.
+
+The formatting is close to GNU, but with a few differences.
+
+## Extensions to C++11
+
+It uses __VA_OPT__ when available, falling back on GNU's variadic
+macro `,#` extension.  This is in the `Assert` macro, so one can have
+multi-argument template instantiations there.  Not that libcody does
+that, but this is code I used elsewhere.
+
+## GNU
+
+The underlying formatting is GNU style.  Here are a few notes about
+things that commonly catches programmers unfamiliar with it is:
+
+* Spaces between binary operators.  Particularly in a function call,
+  between the name and the open paren:
+
+  ```c++
+  Fn (a + b, ary[4], *ptr);
+  ```
+
+  In general GNU style uses a lot more whitespace than Clang-style.
+  We're not trying to cram as much code as possible onto a page!
+
+* Scope braces are always on a line of their own, indented by 2
+  spaces, if they're a sub-statement of an `if`, `for` or whatever:
+
+  ```c++
+  if (bob)
+    {
+      Frob ();
+      Quux ();
+    }
+  ```
+
+  Conditions and loops containing a single statement should not use `{}`.
+  FWIW this was my personal indentation scheme, before I even met GNU code!
+
+* The same is true for a function definition body, except the
+  indentation is zero:
+
+  ```c++
+  int Foo ()
+    noexcept // indented
+  {
+    return 0;
+  }
+  ```
+
+* Initialization bracing is not like scope bracing.  There tends to be
+  more flexibility.
+
+* Break lines at 80 chars, this should be /before/ the operator, not after:
+
+  ```c++
+  a = (b
+       + c);
+  ptr
+  ->MemberFn (stuff);
+  Func
+  (arg);
+  ```
+
+  Thus you can tell what lines are continued from the previous by
+  looking at their start.  Use parens to control indentation.
+
+  If you find yourself wanting to break a line at `.`, don't.
+  Refactor your code to avoid needing that.
+
+* Template instantiations and C++ casts should have no space before the `<`:
+
+  ```c++
+  std::vector<int> k;
+  static_cast<T> (arg); // space before the ( though
+  ```
+
+* Pointer and reference types need a space before the `*` or `&`, if
+  the preceding token is ascii text (a cpp-identifier):
+
+  ```
+  int *ptr;
+  int **ptr_ptr;
+  int *&pref = ptr;
+  ```
+
+  See below a difference in qualifier placement.
+
+* Code should compile without warnings.
+
+## Not GNU
+
+### Names
+
+Unlike GNU code, variants of Camel Case are used.  use `PascalCase`
+for function, type and global variable names.  Use `dromedaryCase` for
+member variables.  Block-scope vars can be `dromedaryCase` or
+`snake_case`, your choice.
+
+### Type qualifiers
+
+Type qualifiers go after the thing they qualify.  You have to do this
+for pointers anyway, and read them inside-out, because, C Just being
+consistent:
+
+```c++
+int const foo = 5; // constant int
+int *const pfoo = nullptr;  // constant pointer to int
+```
diff --git c/libcody/CONTRIB.md w/libcody/CONTRIB.md
new file mode 100644
index 00000000000..234ed069d41
--- /dev/null
+++ w/libcody/CONTRIB.md
@@ -0,0 +1,10 @@
+# A probably incomplete list of contributors
+
+Thanks for the interest in this library!
+
+* Iain Sandoe
+  Darwin testing and porting
+
+* Martin Liska
+  Code cleanups
+
diff --git c/libcody/LICENSE w/libcody/LICENSE
new file mode 100644
index 00000000000..261eeb9e9f8
--- /dev/null
+++ w/libcody/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git c/libcody/Makefile.in w/libcody/Makefile.in
new file mode 100644
index 00000000000..aa0c5c98825
--- /dev/null
+++ w/libcody/Makefile.in
@@ -0,0 +1,186 @@
+# Nathan's generic Makefile		-*- mode:Makefile -*-
+# Copyright (C) 2019-2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+ifeq (0,$(MAKELEVEL)$(SERIAL))
+ifneq (,@toolbin@)
+$(info Prepending @toolbin@ to PATH)
+PATH := @toolbin@:$(PATH)
+export PATH
+endif
+
+# Figure out if we should set parallelism
+ifeq (,$(filter clean%,$(MAKECMDGOALS)))
+PARALLELISM := @NUM_CPUS@
+endif
+endif
+
+ifeq (00,$(MAKELEVEL)$(if $(PARALLELISM),0,1))
+# Although Make 4.3 documentation suggests I can set parallelism just
+# by appending to MAKEFLAGS, it doesn't seem to work.  It's also not
+# possible to figure out the current Make invocation's parallelism,
+# the -j option doesn't appear in MAKEFLAGS and is magically inserted
+# when that is expanded in a rule.  I can't figure how to get a rule
+# expansion into a variable to test.  Fortunately, Make propagates an
+# incoming -j option rather than the one you attempted to append
+$(info Parallelizing $(PARALLELISM) ways)
+MAKEFLAGS += -j$(PARALLELISM)
+ifneq (,$(MAKECMDGOALS))
+$(MAKECMDGOALS): recurse
+endif
+recurse:
+	$(MAKE) -r$(MAKEFLAGS) $(MAKECMDGOALS)
+.PHONY: recurse
+else
+
+srcdir := @srcdir@
+prefix := @prefix@
+exec_prefix := @exec_prefix@
+bindir := @bindir@
+libdir := @libdir@
+includedir := @includedir@
+SUBDIRS := @SUBDIRS@
+# autoconf doesn't seem to like setting SHELL
+SHELL := $(shell which zsh 2>/dev/null >/dev/null && echo zsh \
+  || (echo "No ZSH, maybe flakey" >&2 && echo sh))
+
+# We have to place the -I paths last, so that building will see -I paths to us
+CXX := $(filter-out -I%,@CXX@)
+AR := @AR@
+INSTALL := $(srcdir)/build-aux/install-sh
+
+# C++ compiler options
+CXXFLAGS := @CXXFLAGS@
+CXXINC := $(filter -I%,@CXX@)
+CXXOPTS := $(CXXFLAGS)
+ifeq ($(notdir $(firstword $(CXX))),g++)
+# It's GCC, or pretending to be it -- so it better smell like it!
+# Code generation
+CXXOPTS += -fno-enforce-eh-specs
+CXXOPTS += -fno-stack-protector -fno-threadsafe-statics
+ifneq (@EXCEPTIONS@,yes)
+CXXOPTS += -fno-exceptions -fno-rtti
+endif
+ifeq ($(filter -fdebug-prefix-map=%,$(CXXOPTS)),)
+CXXOPTS += -fdebug-prefix-map=${srcdir}/=
+endif
+# Warning options
+CXXOPTS += -W -Wall -Woverloaded-virtual -Wshadow
+CXXOPTS += -Wno-invalid-offsetof -Wno-unused-variable
+CXXOPTS += -Wno-missing-field-initializers
+# Diagnostic options, look at controlling terminal so that piping
+# through more works
+MLEN := $(shell stty size </dev/tty 2>/dev/null | cut -d' ' -f2)
+ifneq (,$(MLEN))
+CXXOPTS += -fmessage-length=$(MLEN)
+endif
+CXXOPTS += -fdiagnostics-color=always -fno-diagnostics-show-option
+else
+ifeq ($(notdir $(firstword $(CXX))),clang++)
+CXXOPTS += -fno-stack-protector -fno-threadsafe-statics
+ifneq (@EXCEPTIONS@,yes)
+CXXOPTS += -fno-exceptions -fno-rtti
+endif
+# Warning options
+CXXOPTS += -W -Wall -Woverloaded-virtual -Wshadow
+CXXOPTS += -Wno-invalid-offsetof -Wno-unused-variable
+CXXOPTS += -Wno-missing-field-initializers
+else
+# Add different compiler's options here
+endif
+endif
+
+# Config
+CXXOPTS += $(filter-out -DHAVE_CONFIG_H,@DEFS@) -include config.h
+
+# Linker options
+LDFLAGS := -L. @LDFLAGS@
+LIBS := @LIBS@
+
+# Per-source & per-directory compile flags (warning: recursive)
+SRC_CXXFLAGS = $(CXXFLAGS$(patsubst $(srcdir)%,%,$1)) \
+	$(if $(filter-out $(srcdir)/,$1),\
+	  $(call $0,$(dir $(patsubst %/,%,$1))))
+
+ifneq ($(MAINTAINER),)
+override MAINTAINER += $1
+endif
+ifeq (@MAINTAINER@,yes)
+MAINTAINER = $2
+else
+MAINTAINER = \# --enable-maintainer-mode to rebuild $1, or make MAINTAINER=touch
+endif
+
+vpath %.in $(srcdir)
+vpath %.cc $(srcdir)
+
+.SUFFIXES: .o .cc
+
+%.o: %.cc
+	@mkdir -p $(dir $@)
+	$(CXX) $(strip $(CXXOPTS) $(call SRC_CXXFLAGS,$<) $(CXXINC)) \
+	  -MMD -MP -MF ${@:.o=.d} -c -o $@ $<
+
+all:: Makefile $(addprefix all.,$(SUBDIRS))
+
+check:: Makefile $(addprefix check.,$(SUBDIRS))
+
+clean:: Makefile $(addprefix clean.,$(SUBDIRS))
+
+revision.stamp: $(addprefix $(srcdir)/,. $(SUBDIRS))
+	@revision=$$(git -C $(srcdir) rev-parse HEAD 2>/dev/null) ;\
+	if test -n "$$revision" ;\
+	then revision=git-$$revision ;\
+	  if git -C $(srcdir) status --porcelain 2>/dev/null | grep -vq '^  ' ;\
+	  then revision+=M ;\
+	  fi ;\
+	else revision=unknown ;\
+	fi ;\
+	echo $$revision > $@
+
+revision: revision.stamp
+	@cmp -s $< $@ || cp -f $< $@
+
+clean::
+	rm -f revision.stamp revision
+
+distclean:: clean
+	rm -f config.log config.status
+	rm -rf $(SUBDIRS)
+
+$(srcdir)/configure: $(srcdir)/configure.ac \
+	  $(patsubst %,$(srcdir)/%/config.m4,. $(SUBDIRS))
+	$(call MAINTAINER,$@,cd $(@D) && autoconf -W all,error)
+
+$(srcdir)/config.h.in: $(srcdir)/configure.ac \
+	  $(patsubst %,$(srcdir)/%/config.m4,. $(SUBDIRS))
+	$(call MAINTAINER,$@,cd $(@D) && autoheader -f -W all,error)
+
+config.h: config.status config.h.in
+	./$< --header=$@
+	touch $@
+
+ifeq ($(filter %clean,$(MAKECMDGOALS)),)
+@CONFIG_FILES@: %: config.status %.in
+	./$< --file=$@
+	touch $@
+endif
+
+config.status: $(srcdir)/configure $(srcdir)/config.h.in
+	if test -x $@; then ./$@ -recheck; else $< @configure_args@; fi
+
+distclean:: clean
+	rm -f config.h
+
+maintainer-clean:: distclean
+	rm -f $(srcdir)/config.h.in
+
+clean::
+	rm -f $(shell find $(srcdir) -name '*~')
+
+.PHONY: all check clean distclean maintainer-clean
+
+-include $(addsuffix /Makesub,. $(SUBDIRS))
+-include $(addsuffix /tests/Makesub,. $(SUBDIRS))
+
+endif
diff --git c/libcody/Makesub.in w/libcody/Makesub.in
new file mode 100644
index 00000000000..0bfe6f6c1b3
--- /dev/null
+++ w/libcody/Makesub.in
@@ -0,0 +1,48 @@
+# CODYlib		-*- mode:Makefile -*-
+# Copyright (C) 2019-2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+DOXYGEN := @DOXYGEN@
+CXXFLAGS/ := -I$(srcdir)
+LIBCODY.O := buffer.o client.o fatal.o netclient.o netserver.o \
+	resolver.o packet.o server.o
+
+all:: .gdbinit
+
+.gdbinit: gdbinit
+	rm -f $@ ; ln -s $< $@
+
+clean::
+	rm -f gdbinit .gdbinit
+
+all:: libcody.a
+
+libcody.a: $(LIBCODY.O)
+	$(AR) -cr $@ $^
+
+clean::
+	rm -f $(LIBCODY.O) $(LIBCODY.O:.o=.d)
+	rm -f libcody.a
+
+CXXFLAGS/fatal.cc = -DREVISION='"$(shell cat revision)"' -DSRCDIR='"$(srcdir)"'
+
+fatal.o: Makefile revision
+
+doc:: dox.cfg
+ifneq ($(DOXYGEN),:)
+	cd $(<D); $(DOXYGEN) $(<F) >&dox.log
+else
+	@echo "doxygen not present, documentation not built"
+endif
+
+clean::
+	rm -rf dox dox.log
+
+install::
+	$(INSTALL) -d $(libdir) $(includedir)
+	$(INSTALL) libcody.a $(libdir)
+	$(INSTALL) $(srcdir)/cody.hh $(includedir)
+
+ifeq ($(filter clean%,$(MAKECMDGOALS)),)
+-include $(LIBCODY.O:.o=.d)
+endif
diff --git c/libcody/README.md w/libcody/README.md
new file mode 100644
index 00000000000..940d37c320c
--- /dev/null
+++ w/libcody/README.md
@@ -0,0 +1,481 @@
+# libCODY: COmpiler DYnamism<sup><a href="#1">1</a></sup>
+
+Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+
+libCODY is an implementation of a communication protocol between
+compilers and build systems.
+
+**WARNING:**  This is preliminary software.
+
+In addition to supporting C++modules, this may also support LTO
+requirements and could also feed deal with generated #include files
+and feed the compiler with prepruned include paths and whatnot.  (The
+system calls involved in include searches can be quite expensive on
+some build infrastuctures.)
+
+* Client and Server objects
+* Direct connection for in-process use
+* Testing with Joust (that means nothing to you, doesn't it!)
+
+
+## Problem Being Solved
+
+The origin is in C++20 modules:
+```
+import foo;
+```
+
+At that import, the compiler needs<sup><a href="#2">2</a></sup> to
+load up the compiled serialization of module `foo`.  Where is that
+file?  Does it even exist?  Unless the build system already knows the
+dependency graph, this might be a completely unknown module.  Now, the
+build system knows how to build things, but it might not have complete
+information about the dependencies.  The ultimate source of
+dependencies is the source code being compiled, and specifying the
+same thing in multiple places is a recipe for build skew.
+
+Hence, a protocol by which a compiler can query a build system.  This
+was originally described in <a
+href="https://wg21.link/p1184r1">p1184r1:A Module Mapper</a>.  Along
+with a proof-of-concept hack in GNUmake, described in <a
+href="https://wg21.link/p1602">p1602:Make Me A Module</a>. The current
+implementation has evolved and an update to p1184 will be forthcoming.
+
+## Packet Encoding
+
+The protocol is turn-based.  The compiler sends a block of one or more
+requests to the builder, then waits for a block of responses to all of
+those requests.  If the builder needs to compile something to satisfy
+a request, there may be some time before the response.  A builder may
+service multiple compilers concurrently, and it'll need some buffering
+scheme to deal with that.
+
+When multiple requests are in a block, the responses are also in a
+block, and in corresponding order.
+
+Every request has a response.
+
+Requests and responses are user-readable text.  It is not intended as
+a transmission medium to send large binary objects (such as compiled
+modules).  It is presumed the builder and the compiler share a file
+system, for that kind of thing.<sup><a href="#3">3</a></sup>
+
+Messages characters are encoded in UTF8.
+
+Messages are a sequenc of octets ending with a NEWLINE (0xa).  The lines
+consist of a sequence of words, separated by WHITESPACE (0x20 or 0x9).
+Words themselves do not contain WHITESPACE.  Lines consisting solely
+of WHITESPACE (or empty) are ignored.
+
+To encode a block of multiple messages, non-final messages end with a
+single word of SEMICOLON (0x3b), immediately before the NEWLINE.  Thus
+a serial connection can determine whether a block is complete without
+decoding the messages.
+
+Words containing characters in the set [-+_/%.A-Za-z0-9] need not be
+quoted.  Words containing characters outside that set should be
+quoted.  A zero-length word may be achieved with `''`
+
+Quoted words begin and end with APOSTROPHE (x27). Within the quoted
+word, BACKSLASH (x5c) is used as an escape mechanism, with the
+following meanings:
+
+* \\n - NEWLINE (0xa)
+* \\t - TAB (0x9)
+* \\' - APOSTROPHE (')
+* \\\\ - BACKSLASH (\\)
+
+Characters in the range [0x00, 0x20) and 0x7f are encoded with one or
+two lowercase hex characters.  Octets in the range [0x80,0xff) are
+UTF8 encodings of unicode characters outside the traditional ASCII set
+and passed as such.
+
+Decoding should be more relaxed.  Unquoted words containing characters
+in the range [0x20,0xff] other than BACKSLASH or APOSTROPHE should be
+accepted.  In a quoted sequence, `\` followed by one or two lower case
+hex characters decode to that octet.  Further, words can be
+constructed from a mixture of abutted quoted and unquoted sequences.
+For instance `FOO' 'bar` would decode to the word `FOO bar`.
+
+Notice that the block continuation marker of `;` is not a valid
+encoding of the word `;`, which would be `';'`.
+
+It is recommended that words are separated by single SPACE characters.
+
+## Messages
+
+The message descriptions use `$metavariable` examples.
+
+The request messages are specific to a particlar action.  The response
+messages are more generic, describing their value types, but not their
+meaning.  Message consumers need to know the response to decode them.
+Notice the `Packet::GetRequest()` method records in response packates
+what the request being responded to was.  Do not confuse this with the
+`Packet::GetCode ()` method.
+
+### Responses
+
+The simplest response is a single:
+
+`OK`
+
+This indicates the request was successful.
+
+
+An error response is:
+
+`ERROR $message`
+
+The message is a human-readable string.  It indicates failure of the request.
+
+Pathnames are encoded with:
+
+`PATHNAME $pathname`
+
+Boolean responses use:
+
+`BOOL `(`TRUE`|`FALSE`)
+
+### Handshake Request
+
+The first message is a handshake:
+
+`HELLO $version $compiler $ident`
+
+The `$version` is a numeric value, currently `1`.  `$compiler` identifies
+the compiler &mdash; builders may need to keep compiled modules from
+different compilers separate.  `$ident` is an identifer the builder
+might use to identify the compilation it is communicting with.
+
+Responses are:
+
+`HELLO $version $builder`
+
+A successful handshake.  The communication is now connected and other
+messages may be exchanged.  An ERROR response indicates an unsuccesful
+handshake.  The communication remains unconnected.
+
+There is nothing restricting a handshake to its own message block.  Of
+course, if the handshake fails, subsequent non-handshake messages in
+the block will fail (producing error responses).
+
+### C++ Module Requests
+
+A set of requests are specific to C++ modules
+
+#### Repository
+
+All relative CMI file names are relative to a repository.  (There are
+usually no abosolute CMI files).  The respository may be determined
+with:
+
+`MODULE-REPO`
+
+A PATHNAME response is expected.  The `$pathname` may be an empty
+word, which is equivalent to `.`.
+
+#### Exporting
+
+A compilation of a module interface, partition or header unit can
+inform the builder with:
+
+`MODULE-EXPORT $module`
+
+This will result in a PATHNAME response naming the Compiled Module Interface
+pathname to write.
+
+The `MODULE-EXPORT` request does not indicate the module has been
+successfully compiled.  At most one `MODULE-EXPORT` is to be made, and
+as the connection is for a single compilation, the builder may infer
+dependency relationships between the module being generated and import
+requests made.
+
+Named module names and header unit names are distinguished by making
+the latter unambiguously look like file names.  Firstly, they must be
+fully resolved according to the compiler's usual include path.  If
+that results in an absolute name file name (beginning with `/`, or
+certain other OS-specific sequences), all is well.  Otherwise a
+relative file name must be prefixed by `./` to be distinguished from a
+similarly named named module.  This prefixing must occur, even if the
+header-unit's name contains characters that cannot appear in a named
+module's name.
+
+It is expected that absolute header-unit names convert to relative CMI
+names, to keep all CMIs within the CMI repository.  This means that
+steps must be taken to distinguish the CMIs for `/here` from `./here`,
+and this can be achieved by replacing the leading `./` directory with
+`,/`, which is visually similar but does not have the self-reference
+semantics of dot.  Likewise, header-unit names containing `..`
+directories, can be remapped to `,,`.  (When symlinks are involved
+`bob/dob/..` might not be `bob`, of course.)  C++ header-unit
+semantics are such that there is no need to resolve multiple ways of
+spelling a particular header-unit to a unique CMI file.
+
+Successful compilation of an interface is indicated with a subsequent:
+
+`MODULE-COMPILED $module`
+
+FIXME: do we need the module here?
+
+request.  This indicates the CMI file has been written to disk, so
+that any other compilations waiting on it may proceed.  Depending on
+compiler implementation, the CMI may be written before the compilation
+completes.  A single OK response is expected.
+
+Compilation failure can be inferred by lack of a `MODULE-COMPILED`
+request.  It is presumed the builder can determine this, as it is also
+responsible for launching and reaping the compiler invocations
+themselves.
+
+#### Importing
+
+Importation, inculding that of header-units, uses:
+
+`MODULE-IMPORT $module`
+
+A PATHNAME response names the CMI file to be read.  Should the builder
+have to invoke a compilation to produce the CMI, the response should
+be delayed until that occurs.  If such a compilation fails, an error
+response should be provided to the requestor &mdash; which will then
+presumably fail in some manner.
+
+#### Include Translation
+
+Include translation can be determined with:
+
+`INCLUDE-TRANSLATE $header`
+
+The header name, `$header`, is the fully resolved header name, in the
+above-mentioned unambigous filename form.  The response will either be
+a BOOL response indicating translation (TRUE) or textual inclusion
+(FALSE).  Alternatively a PATHNAME response can directly name the CMI,
+and implies translation, this possibly elides a subsequent
+`MODULE-IMPORT` request.
+
+### GCC LTO Messages
+
+These set of requests are used for GCC LTO jobserver integration with GNU Make
+
+#### Invoke Command
+
+A command can be invoked with the follow message:
+
+`INVOKE $args`
+
+A successful invocation provides an OK response.  A failed
+invocation's produces an ERROR response.
+
+FIXME: Note for generalization this command needs to indicate which
+files may need transfering to and from a remote build system.
+
+## Building libCody
+
+Libcody is written in C++11.  (It's a intended for compilers, so
+there'd be a boostrapping problem if it used the latest and greatest.)
+
+### Using configure and make.
+
+It supports the usual `configure`, `make`, `make check` & `make install`
+sequence.  It does not support building in the source directory --
+that just didn't drop out, and it's not how I build things (because,
+again, for compilers).  Excitingly it uses my own `joust` test
+harness, so you'll need to build and install that somewhere, if you
+want the comfort of testing.
+
+The following configure options are available, in addition to the usual set:
+
+* `--enable-checking` Compile with assert-like checking.  Defaults to on.
+
+* `--with-tooldir=DIR` Prepend `DIR` to `PATH` when building (`DIR`
+  need not already include the trailing `/bin`, and the right things
+  happen).  Use this if you need to point to non-standard tools that
+  you usually don't have in your path.  This path is also used when
+  the configure script searches for programs.
+
+* `--with-toolinc=DIR`, `--with-toollib=DIR`, include path and library
+  path variants of `--with-tooldir`.  If these are siblings of the
+  tool bin directory, they'll be found automatically.
+
+* `--with-compiler=NAME` Specify a particular compiler to use.
+  Usually what configure finds is sufficiently usable.
+
+* `--with-bugurl=URL` Override the bugreporting URL.  Do this if
+  you're providing libcody as part of a package that /you/ are
+  supporting.
+
+* `--enable-maintainer-mode` Specify that rules to rebuild things like
+  `configure` (with `autoconf`) should be enabled.  When not enabled,
+  you'll get a message if these appear out of date, but that can
+  happen naturally after an update or clone as `git`, in common with
+  other VCs, doesn't preserve the relative ordering of file
+  modifications.  You can use `make MAINTAINER=touch` to shut make up,
+  if this occurs (or manually execute the `autoconf` and related
+  commands).
+
+When building, you can override the default optimization flags with
+`CXXFLAGS=$flags`.  I often build a debuggable library with `make
+CXXFLAGS=-g3`.
+
+The `Makefile` will also parallelize according to the number of CPUs,
+unless you specify explicitly with a `-j` option.  This is a little
+clunky, as it's not possible to figure out inside the makefile whether
+the user provided `-j`.  (Or at least I've not figured out how.)
+
+### Using cmake and make
+
+#### In the clang/LLVM project
+
+The primary motivation for a cmake implementation is to allow building
+libcody "in tree" in clang/LLVM.  In that case, a checkout of libcody
+can be placed (or symbolically linked) into clang/tools.  This will
+configure and build the library along with other LLVM dependencies.
+
+*NOTE* This is not treated as an installable entity (it is present only
+for use by the project).
+
+*NOTE* The testing targets would not be appropriate in this configuration;
+it is expected that lit-based testing of the required functionality will be
+done by the code using the library.
+
+#### Stand-alone
+
+For use on platforms that don't support configure & make effectively, it
+is possible to use the cmake & make process in stand-alone mode (similar
+to the configure & make process above).
+
+An example use.
+```
+cmake -DCMAKE_INSTALL_PREFIX=/path/to/installation -DCMAKE_CXX_COMPILER=clang++ /path/to/libcody/source
+make
+make install
+```
+Supported flags (additions to the usual cmake ones).
+
+* `-DCODY_CHECKING=ON,OFF`: Compile with assert-like checking. (defaults ON)
+
+* `-DCODY_WITHEXCEPTIONS=ON,OFF`: Compile with C++ exceptions and RTTI enabled.
+(defaults OFF, to be compatible with GCC and LLVM).
+
+*TODO*: At present there is no support for `ctest` integration (this should be
+feasible, provided that `joust` is installed and can be discovered by `cmake`).
+
+## API
+
+The library defines entities in the `::Cody` namespace.
+
+There are 4 user-visible classes:
+
+* `Packet`: Responses to requests are `Packets`.  These have a code,
+  indicating the response kind, and a payload.
+
+* `Client`: The compiler-end of a connection.  Requests may be made
+  and responses are returned.
+
+* `Server`: The builder-end of a connection.  Requests may be waited
+  for, and responses made.  Builders that serve multiple concurrent
+  connections and spawn compilations to resolve dependencies may need
+  to derive from this class to provide response queuing.
+
+* `Resolver`: The processing engine of the builder side.  User code is
+  expected to derive from this class and provide virtual function
+  overriders to affect the semantics of the resolver.
+
+In addition there are a number of helpers to setup connections.
+
+Logically the Client and the Server communicate via a sequential
+channel.  The channel may be provided by:
+
+* two pipes, with different file descriptors for reading and writing
+  at each end.
+
+* a socket, which will use the same file descriptor for reading and
+  writing.  the socket can be created in a number of ways, including
+  Unix domain and IPv6 TCP, for which helpers are provided.
+
+* a direct, in-process, connection, using buffer swapping.
+
+The communication channel is presumed reliable.
+
+Refer to the (currently very sparse) doxygen-generated documentation
+for details of the API.
+
+## Examples
+
+To create an in-process resolver, use the following boilerplate:
+
+```
+class MyResolver : Cody::Resolver { ... stuff here ... };
+
+Cody::Client *MakeClient (char const *maybe_ident)
+{
+  auto *r = new MyResolver (...);
+  auto *s = new Cody::Server (r);
+  auto *c = new Cody::Client (s);
+
+  auto t = c->ConnectRequest ("ME", maybe_ident);
+  if (t.GetCode () == Cody::Client::TC_CONNECT)
+    ;// Yay!
+  else if (t.GetCode () == Cody::Client::TC_ERROR)
+    report_error (t.GetString ());
+
+  return c;
+}
+
+```
+
+For a remotely connecting client:
+```
+Cody::Client *MakeClient ()
+{
+  char const *err = nullptr;
+  int fd = OpenInet6 (char const **err, name, port);
+  if (fd < 0)
+    { ... error... return nullptr;}
+
+  auto *c = new Cody::Client (fd);
+
+  auto t = c->ConnectRequest ("ME", maybe_ident);
+  if (t.GetCode () == Cody::Client::TC_CONNECT)
+    ;// Yay!
+  else if (t.GetCode () == Cody::Client::TC_ERROR)
+    report_error (t.GetString ());
+
+  return c;
+}
+```
+
+# Future Directions
+
+* Current Directory.  There is no mechanism to check the builder and
+  the compiler have the same working directory.  Perhaps that should
+  be addressed.
+
+* Include path canonization and/or header file lookup.  This can be
+  expensive, particularly with many `-I` options, due to the system
+  calls.  Perhaps using a common resource would be cheaper?
+
+* Generated header file lookup/construction.  This is essentially the
+  same problem as importing a module, and build systems are crap at
+  dealing with this.
+
+* Link-time compilations.  Another place the compiler would like to
+  ask the build system to do things.
+
+* C++20 API entrypoints &mdash; std:string_view would be nice
+
+* Exception-safety audit.  Exceptions are not used, but memory
+  exhaustion could happen.  And perhaps user's resolver code employs
+  exceptions?
+
+<a name="1">1</a>: Or a small town in Wyoming
+
+<a name="2">2</a>: This describes one common implementation technique.
+The std itself doesn't require such serializations, but the ability to
+create them is kind of the point.  Also, 'compiler' is used where we
+mean any consumer of a module, and 'build system' where we mean any
+producer of a module.
+
+<a name="3">3</a>: Even when the builder is managing a distributed set
+of compilations, the builder must have a mechanism to get source files
+to, and object files from, the compilations.  That scheme can also
+transfer the CMI files.
diff --git c/libcody/buffer.cc w/libcody/buffer.cc
new file mode 100644
index 00000000000..52df3176c9a
--- /dev/null
+++ w/libcody/buffer.cc
@@ -0,0 +1,387 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+// C++
+#include <algorithm>
+// C
+#include <cstring>
+// OS
+#include <unistd.h>
+#include <cerrno>
+
+// MessageBuffer code
+
+// Lines consist of words and end with a NEWLINE (0xa) char
+// Whitespace characters are TAB (0x9) and SPACE (0x20)
+// Words consist of non-whitespace chars separated by whitespace.
+// Multiple lines in one transaction are indicated by ending non-final
+// lines with a SEMICOLON (0x3b) word, immediately before the NEWLINE
+// Continuations with ; preceding it
+// Words matching regexp [-+_/%.a-zA-Z0-9]+ need no quoting.
+// Quoting with '...'
+// Anything outside of [-+_/%.a-zA-Z0-9] needs quoting
+// Anything outside of <= <space> or DEL or \' or \\ needs escaping.
+// Escapes are \\, \', \n, \t, \_, everything else as \<hex><hex>?
+// Spaces separate words, UTF8 encoding for non-ascii chars
+
+namespace Cody {
+namespace Detail {
+
+static const char CONTINUE = S2C(u8";");
+
+void MessageBuffer::BeginLine ()
+{
+  if (!buffer.empty ())
+    {
+      // Terminate the previous line with a continuation
+      buffer.reserve (buffer.size () + 3);
+      buffer.push_back (S2C(u8" "));
+      buffer.push_back (CONTINUE);
+      buffer.push_back (S2C(u8"\n"));
+    }
+  lastBol = buffer.size ();
+}
+
+// QUOTE means 'maybe quote', we search it for quote-needing chars
+
+void MessageBuffer::Append (char const *str, bool quote, size_t len)
+{
+  if (len == ~size_t (0))
+    len = strlen (str);
+
+  if (!len && !quote)
+    return;
+
+  // We want to quote characters outside of [-+_A-Za-z0-9/%.], anything
+  // that could remotely be shell-active.  UTF8 encoding for non-ascii.
+  if (quote && len)
+    {
+      quote = false;
+      // Scan looking for quote-needing characters.  We could just
+      // append until we find one, but that's probably confusing
+      for (size_t ix = len; ix--;)
+	{
+	  unsigned char c = (unsigned char)str[ix];
+	  if (!((c >= S2C(u8"a") && c <= S2C(u8"z"))
+		|| (c >= S2C(u8"A") && c <= S2C(u8"Z"))
+		|| (c >= S2C(u8"0") && c <= S2C(u8"9"))
+		|| c == S2C(u8"-") || c == S2C(u8"+") || c == S2C(u8"_")
+		|| c == S2C(u8"/") || c == S2C(u8"%") || c == S2C(u8".")))
+	    {
+	      quote = true;
+	      break;
+	    }
+	}
+    }
+
+  // Maximal length of appended string
+  buffer.reserve (buffer.size () + len * (quote ? 3 : 1) + 2);
+
+  if (quote)
+    buffer.push_back (S2C(u8"'"));
+
+  for (auto *end = str + len; str != end;)
+    {
+      auto *e = end;
+
+      if (quote)
+	// Look for next escape-needing char.  More relaxed than
+	// the earlier needs-quoting check.
+	for (e = str; e != end; ++e)
+	  {
+	    unsigned char c = (unsigned char)*e;
+	    if (c < S2C(u8" ") || c == 0x7f
+		|| c == S2C(u8"\\") || c == S2C(u8"'"))
+	      break;
+	  }
+      buffer.insert (buffer.end (), str, e);
+      str = e;
+
+      if (str == end)
+	break;
+
+      buffer.push_back (S2C(u8"\\"));
+      switch (unsigned char c = (unsigned char)*str++)
+	{
+	case S2C(u8"\t"):
+	  c = S2C(u8"t");
+	  goto append;
+
+	case S2C(u8"\n"):
+	  c = S2C(u8"n");
+	  goto append;
+
+	case S2C(u8"'"):
+	case S2C(u8"\\"):
+	append:
+	  buffer.push_back (c);
+	  break;
+
+	default:
+	  // Full-on escape.  Use 2 lower-case hex chars
+	  for (unsigned shift = 8; shift;)
+	    {
+	      shift -= 4;
+
+	      char nibble = (c >> shift) & 0xf;
+	      nibble += S2C(u8"0");
+	      if (nibble > S2C(u8"9"))
+		nibble += S2C(u8"a") - (S2C(u8"9") + 1);
+	      buffer.push_back (nibble);
+	    }
+	}
+    }
+
+  if (quote)
+    buffer.push_back (S2C(u8"'"));
+}
+
+void MessageBuffer::Append (char c)
+{
+  buffer.push_back (c);
+}
+
+void MessageBuffer::AppendInteger (unsigned u)
+{
+  std::string v (std::to_string (u));
+  AppendWord (v);
+}
+
+int MessageBuffer::Write (int fd) noexcept
+{
+  size_t limit = buffer.size () - lastBol;
+  ssize_t count = write (fd, &buffer.data ()[lastBol], limit);
+
+  int err = 0;
+  if (count < 0)
+    err = errno;
+  else
+    {
+      lastBol += count;
+      if (size_t (count) != limit)
+	err = EAGAIN;
+    }
+
+  if (err != EAGAIN && err != EINTR)
+    {
+      // Reset for next message
+      buffer.clear ();
+      lastBol = 0;
+    }
+
+  return err;
+}
+
+int MessageBuffer::Read (int fd) noexcept
+{
+  constexpr size_t blockSize = 200;
+
+  size_t lwm = buffer.size ();
+  size_t hwm = buffer.capacity ();
+  if (hwm - lwm < blockSize / 2)
+    hwm += blockSize;
+  buffer.resize (hwm);
+
+  auto iter = buffer.begin () + lwm;
+  ssize_t count = read (fd, &*iter, hwm - lwm);
+  buffer.resize (lwm + (count >= 0 ? count : 0));
+
+  if (count < 0)
+    return errno;
+
+  if (!count)
+    // End of file
+    return -1;
+
+  bool more = true;
+  for (;;)
+    {
+      auto newline = std::find (iter, buffer.end (), S2C(u8"\n"));
+      if (newline == buffer.end ())
+	break;
+      more = newline != buffer.begin () && newline[-1] == CONTINUE;
+      iter = newline + 1;
+	
+      if (iter == buffer.end ())
+	break;
+
+      if (!more)
+	{
+	  // There is no continuation, but there are chars after the
+	  // newline.  Truncate the buffer and return an error
+	  buffer.resize (iter - buffer.begin ());
+	  return EINVAL;
+	}
+    }
+
+  return more ? EAGAIN : 0;
+}
+
+int MessageBuffer::Lex (std::vector<std::string> &result)
+{
+  result.clear ();
+
+  int err = ENOENT;
+  if (IsAtEnd ())
+    return ENOENT;
+
+  Assert (buffer.back () == S2C(u8"\n"));
+
+  auto iter = buffer.begin () + lastBol;
+
+  for (std::string *word = nullptr;;)
+    {
+      char c = *iter;
+
+      ++iter;
+      if (c == S2C(u8" ") || c == S2C(u8"\t"))
+	{
+	  word = nullptr;
+	  continue;
+	}
+
+      if (c == S2C(u8"\n"))
+	break;
+
+      if (c == CONTINUE)
+	{
+	  // Line continuation
+	  if (word || *iter != S2C(u8"\n"))
+	    goto malformed;
+	  ++iter;
+	  break;
+	}
+
+      if (c <= S2C(u8" ") || c >= 0x7f)
+	goto malformed;
+
+      if (!word)
+	{
+	  result.emplace_back ();
+	  word = &result.back ();
+	}
+
+      if (c == S2C(u8"'"))
+	{
+	  // Quoted word
+	  for (;;)
+	    {
+	      c = *iter;
+
+	      if (c == S2C(u8"\n"))
+		{
+		malformed:;
+		  result.clear ();
+		  iter = std::find (iter, buffer.end (), S2C(u8"\n"));
+		  auto back = iter;
+		  if (back[-1] == CONTINUE  && back[-2] == S2C(u8" "))
+		    // Smells like a line continuation
+		    back -= 2;
+		  result.emplace_back (&buffer[lastBol],
+				       back - buffer.begin () - lastBol);
+		  ++iter;
+		  lastBol = iter - buffer.begin ();
+		  return EINVAL;
+		}
+
+	      if (c < S2C(u8" ") || c >= 0x7f)
+		goto malformed;
+
+	      ++iter;
+	      if (c == S2C(u8"'"))
+		break;
+
+	      if (c == S2C(u8"\\"))
+		// escape
+		switch (c = *iter)
+		  {
+		    case S2C(u8"\\"):
+		    case S2C(u8"'"):
+		      ++iter;
+		      break;
+
+		    case S2C(u8"n"):
+		      c = S2C(u8"\n");
+		      ++iter;
+		      break;
+
+		    case S2C(u8"_"):
+		      // We used to escape SPACE as \_, so accept that
+		      c = S2C(u8" ");
+		      ++iter;
+		      break;
+
+		    case S2C(u8"t"):
+		      c = S2C(u8"\t");
+		      ++iter;
+		      break;
+
+		    default:
+		      {
+			unsigned v = 0;
+			for (unsigned nibble = 0; nibble != 2; nibble++)
+			  {
+			    c = *iter;
+			    if (c < S2C(u8"0"))
+			      {
+				if (!nibble)
+				  goto malformed;
+				break;
+			      }
+			    else if (c <= S2C(u8"9"))
+			      c -= S2C(u8"0");
+			    else if (c < S2C(u8"a"))
+			      {
+				if (!nibble)
+				  goto malformed;
+				break;
+			      }
+			    else if (c <= S2C(u8"f"))
+			      c -= S2C(u8"a") - 10;
+			    else
+			      {
+				if (!nibble)
+				  goto malformed;
+				break;
+			      }
+			    ++iter;
+			    v = (v << 4) | c;
+			  }
+			c = v;
+		      }
+		  }
+	      word->push_back (c);
+	    }
+	}
+      else
+	// Unquoted character
+	word->push_back (c);
+    }
+  lastBol = iter - buffer.begin ();
+  if (result.empty ())
+    return ENOENT;
+
+  return 0;
+}
+
+void MessageBuffer::LexedLine (std::string &str)
+{
+  if (lastBol)
+    {
+      size_t pos = lastBol - 1;
+      for (; pos; pos--)
+	if (buffer[pos-1] == S2C(u8"\n"))
+	  break;
+
+      size_t end = lastBol - 1;
+      if (buffer[end-1] == CONTINUE && buffer[end-2] == S2C(u8" "))
+	// Strip line continuation
+	end -= 2;
+      str.append (&buffer[pos], end - pos);
+    }
+}
+} // Detail
+} // Cody
diff --git c/libcody/build-aux/config.guess w/libcody/build-aux/config.guess
new file mode 100755
index 00000000000..256083a70d3
--- /dev/null
+++ w/libcody/build-aux/config.guess
@@ -0,0 +1,1476 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2018 Free Software Foundation, Inc.
+
+timestamp='2018-03-08'
+
+# This file 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2018 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > "$dummy.c" ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	LIBC=gnu
+
+	eval "$set_cc_for_build"
+	cat <<-EOF > "$dummy.c"
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#else
+	LIBC=gnu
+	#endif
+	EOF
+	eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+	# If ldd exists, use it to detect musl libc.
+	if command -v ldd >/dev/null && \
+		ldd --version 2>&1 | grep -q ^musl
+	then
+	    LIBC=musl
+	fi
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    "/sbin/$sysctl" 2>/dev/null || \
+	    "/usr/sbin/$sysctl" 2>/dev/null || \
+	    echo unknown)`
+	case "$UNAME_MACHINE_ARCH" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine="${arch}${endian}"-unknown
+		;;
+	    *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently (or will in the future) and ABI.
+	case "$UNAME_MACHINE_ARCH" in
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval "$set_cc_for_build"
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# Determine ABI tags.
+	case "$UNAME_MACHINE_ARCH" in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "$UNAME_VERSION" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "$machine-${os}${release}${abi}"
+	exit ;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+	exit ;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+	exit ;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+	exit ;;
+    *:MidnightBSD:*:*)
+	echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+	exit ;;
+    *:ekkoBSD:*:*)
+	echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+	exit ;;
+    *:SolidBSD:*:*)
+	echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+	exit ;;
+    macppc:MirBSD:*:*)
+	echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+	exit ;;
+    *:MirBSD:*:*)
+	echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+	exit ;;
+    *:Sortix:*:*)
+	echo "$UNAME_MACHINE"-unknown-sortix
+	exit ;;
+    *:Redox:*:*)
+	echo "$UNAME_MACHINE"-unknown-redox
+	exit ;;
+    mips:OSF1:*.*)
+        echo mips-dec-osf1
+        exit ;;
+    alpha:OSF1:*:*)
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE=alpha ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE=alphaev5 ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE=alphaev56 ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE=alphapca56 ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE=alphapca57 ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE=alphaev6 ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE=alphaev67 ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE=alphaev69 ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE=alphaev7 ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE=alphaev79 ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	exitcode=$?
+	trap '' 0
+	exit $exitcode ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo "$UNAME_MACHINE"-unknown-amigaos
+	exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo "$UNAME_MACHINE"-unknown-morphos
+	exit ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit ;;
+    *:z/VM:*:*)
+	echo s390-ibm-zvmoe
+	exit ;;
+    *:OS400:*:*)
+	echo powerpc-ibm-os400
+	exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix"$UNAME_RELEASE"
+	exit ;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	echo arm-unknown-riscos
+	exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit ;;
+    DRS?6000:unix:4.0:6*)
+	echo sparc-icl-nx6
+	exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7; exit ;;
+	esac ;;
+    s390x:SunOS:*:*)
+	echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+	exit ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+	exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+	exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	echo i386-pc-auroraux"$UNAME_RELEASE"
+	exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	eval "$set_cc_for_build"
+	SUN_ARCH=i386
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH=x86_64
+	    fi
+	fi
+	echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+	exit ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+	exit ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+	exit ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos"$UNAME_RELEASE"
+	exit ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos"$UNAME_RELEASE"
+		;;
+	    sun4)
+		echo sparc-sun-sunos"$UNAME_RELEASE"
+		;;
+	esac
+	exit ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos"$UNAME_RELEASE"
+	exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint"$UNAME_RELEASE"
+	exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint"$UNAME_RELEASE"
+	exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	echo m68k-atari-mint"$UNAME_RELEASE"
+	exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	echo m68k-milan-mint"$UNAME_RELEASE"
+	exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	echo m68k-hades-mint"$UNAME_RELEASE"
+	exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	echo m68k-unknown-mint"$UNAME_RELEASE"
+	exit ;;
+    m68k:machten:*:*)
+	echo m68k-apple-machten"$UNAME_RELEASE"
+	exit ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten"$UNAME_RELEASE"
+	exit ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix"$UNAME_RELEASE"
+	exit ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix"$UNAME_RELEASE"
+	exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix"$UNAME_RELEASE"
+	exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval "$set_cc_for_build"
+	sed 's/^	//' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+	  dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	echo mips-mips-riscos"$UNAME_RELEASE"
+	exit ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit ;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+	then
+	    if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+	       [ "$TARGET_BINARY_INTERFACE"x = x ]
+	    then
+		echo m88k-dg-dgux"$UNAME_RELEASE"
+	    else
+		echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+	    fi
+	else
+	    echo i586-dg-dgux"$UNAME_RELEASE"
+	fi
+	exit ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+	exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+	fi
+	echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+	exit ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval "$set_cc_for_build"
+		sed 's/^		//' << EOF > "$dummy.c"
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+		then
+			echo "$SYSTEM_NAME"
+		else
+			echo rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit ;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/lslpp ] ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+	else
+		IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+	fi
+	echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+	exit ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+	echo romp-ibm-bsd4.4
+	exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd"$UNAME_RELEASE"   # 4.3 with uname added to
+	exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+	case "$UNAME_MACHINE" in
+	    9000/31?)            HP_ARCH=m68000 ;;
+	    9000/[34]??)         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case "$sc_cpu_version" in
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case "$sc_kernel_bits" in
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if [ "$HP_ARCH" = "" ]; then
+		    eval "$set_cc_for_build"
+		    sed 's/^		//' << EOF > "$dummy.c"
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ "$HP_ARCH" = hppa2.0w ]
+	then
+	    eval "$set_cc_for_build"
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH=hppa2.0w
+	    else
+		HP_ARCH=hppa64
+	    fi
+	fi
+	echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+	exit ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux"$HPUX_REV"
+	exit ;;
+    3050*:HI-UX:*:*)
+	eval "$set_cc_for_build"
+	sed 's/^	//' << EOF > "$dummy.c"
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	echo unknown-hitachi-hiuxwe2
+	exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+	echo hppa1.1-hp-bsd
+	exit ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+	echo hppa1.1-hp-osf
+	exit ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo "$UNAME_MACHINE"-unknown-osf1mk
+	else
+	    echo "$UNAME_MACHINE"-unknown-osf1
+	fi
+	exit ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+	exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+	exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+	exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+	exit ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    *:UNICOS/mp:*:*)
+	echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+	exit ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi"$UNAME_RELEASE"
+	exit ;;
+    *:BSD/OS:*:*)
+	echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+	exit ;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case "$UNAME_PROCESSOR" in
+	    amd64)
+		UNAME_PROCESSOR=x86_64 ;;
+	    i386)
+		UNAME_PROCESSOR=i586 ;;
+	esac
+	echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+	exit ;;
+    i*:CYGWIN*:*)
+	echo "$UNAME_MACHINE"-pc-cygwin
+	exit ;;
+    *:MINGW64*:*)
+	echo "$UNAME_MACHINE"-pc-mingw64
+	exit ;;
+    *:MINGW*:*)
+	echo "$UNAME_MACHINE"-pc-mingw32
+	exit ;;
+    *:MSYS*:*)
+	echo "$UNAME_MACHINE"-pc-msys
+	exit ;;
+    i*:PW*:*)
+	echo "$UNAME_MACHINE"-pc-pw32
+	exit ;;
+    *:Interix*:*)
+	case "$UNAME_MACHINE" in
+	    x86)
+		echo i586-pc-interix"$UNAME_RELEASE"
+		exit ;;
+	    authenticamd | genuineintel | EM64T)
+		echo x86_64-unknown-interix"$UNAME_RELEASE"
+		exit ;;
+	    IA64)
+		echo ia64-unknown-interix"$UNAME_RELEASE"
+		exit ;;
+	esac ;;
+    i*:UWIN*:*)
+	echo "$UNAME_MACHINE"-pc-uwin
+	exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	echo x86_64-unknown-cygwin
+	exit ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+	exit ;;
+    *:GNU:*:*)
+	# the GNU system
+	echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+	exit ;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+	exit ;;
+    i*86:Minix:*:*)
+	echo "$UNAME_MACHINE"-pc-minix
+	exit ;;
+    aarch64:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    arc:Linux:*:* | arceb:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    arm*:Linux:*:*)
+	eval "$set_cc_for_build"
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+	    else
+		echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+	    fi
+	fi
+	exit ;;
+    avr32*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    cris:Linux:*:*)
+	echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+	exit ;;
+    crisv32:Linux:*:*)
+	echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+	exit ;;
+    e2k:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    frv:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    hexagon:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    i*86:Linux:*:*)
+	echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+	exit ;;
+    ia64:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    k1om:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    m32r*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    m68*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	eval "$set_cc_for_build"
+	sed 's/^	//' << EOF > "$dummy.c"
+	#undef CPU
+	#undef ${UNAME_MACHINE}
+	#undef ${UNAME_MACHINE}el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=${UNAME_MACHINE}el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=${UNAME_MACHINE}
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`"
+	test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; }
+	;;
+    mips64el:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-"$LIBC"
+	exit ;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    padre:Linux:*:*)
+	echo sparc-unknown-linux-"$LIBC"
+	exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-"$LIBC"
+	exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+	  PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+	  *)    echo hppa-unknown-linux-"$LIBC" ;;
+	esac
+	exit ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-"$LIBC"
+	exit ;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-"$LIBC"
+	exit ;;
+    ppc64le:Linux:*:*)
+	echo powerpc64le-unknown-linux-"$LIBC"
+	exit ;;
+    ppcle:Linux:*:*)
+	echo powerpcle-unknown-linux-"$LIBC"
+	exit ;;
+    riscv32:Linux:*:* | riscv64:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+	exit ;;
+    sh64*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    sh*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    tile*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    vax:Linux:*:*)
+	echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+	exit ;;
+    x86_64:Linux:*:*)
+	echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+	exit ;;
+    xtensa*:Linux:*:*)
+	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+	exit ;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+	exit ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo "$UNAME_MACHINE"-pc-os2-emx
+	exit ;;
+    i*86:XTS-300:*:STOP)
+	echo "$UNAME_MACHINE"-unknown-stop
+	exit ;;
+    i*86:atheos:*:*)
+	echo "$UNAME_MACHINE"-unknown-atheos
+	exit ;;
+    i*86:syllable:*:*)
+	echo "$UNAME_MACHINE"-pc-syllable
+	exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	echo i386-unknown-lynxos"$UNAME_RELEASE"
+	exit ;;
+    i*86:*DOS:*:*)
+	echo "$UNAME_MACHINE"-pc-msdosdjgpp
+	exit ;;
+    i*86:*:4.*:*)
+	UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+	else
+		echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+	fi
+	exit ;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}"
+	exit ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+	else
+		echo "$UNAME_MACHINE"-pc-sysv32
+	fi
+	exit ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configure will decide that
+	# this is a cross-build.
+	echo i586-pc-msdosdjgpp
+	exit ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv"$UNAME_RELEASE"  # Unknown i860-SVR4
+	fi
+	exit ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit ;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos"$UNAME_RELEASE"
+	exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos"$UNAME_RELEASE"
+	exit ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+	exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+	exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv"$UNAME_RELEASE"
+	exit ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo "$UNAME_MACHINE"-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit ;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	echo i586-unisys-sysv4
+	exit ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit ;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo "$UNAME_MACHINE"-stratus-vos
+	exit ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux"$UNAME_RELEASE"
+	exit ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+		echo mips-nec-sysv"$UNAME_RELEASE"
+	else
+		echo mips-unknown-sysv"$UNAME_RELEASE"
+	fi
+	exit ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit ;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	echo i586-pc-haiku
+	exit ;;
+    x86_64:Haiku:*:*)
+	echo x86_64-unknown-haiku
+	exit ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-7:SUPER-UX:*:*)
+	echo sx7-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-8:SUPER-UX:*:*)
+	echo sx8-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-8R:SUPER-UX:*:*)
+	echo sx8r-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    SX-ACE:SUPER-UX:*:*)
+	echo sxace-nec-superux"$UNAME_RELEASE"
+	exit ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+	exit ;;
+    *:Rhapsody:*:*)
+	echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+	exit ;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+	eval "$set_cc_for_build"
+	if test "$UNAME_PROCESSOR" = unknown ; then
+	    UNAME_PROCESSOR=powerpc
+	fi
+	if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
+	    if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		       (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		       grep IS_64BIT_ARCH >/dev/null
+		then
+		    case $UNAME_PROCESSOR in
+			i386) UNAME_PROCESSOR=x86_64 ;;
+			powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		    esac
+		fi
+		# On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+		if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+		       (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		       grep IS_PPC >/dev/null
+		then
+		    UNAME_PROCESSOR=powerpc
+		fi
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # Avoid executing cc on OS X 10.9, as it ships with a stub
+	    # that puts up a graphical alert prompting to install
+	    # developer tools.  Any system running Mac OS X 10.7 or
+	    # later (Darwin 11 and later) is required to have a 64-bit
+	    # processor. This is not true of the ARM version of Darwin
+	    # that Apple uses in portable devices.
+	    UNAME_PROCESSOR=x86_64
+	fi
+	echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+	exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = x86; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+	exit ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit ;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+	echo neo-tandem-nsk"$UNAME_RELEASE"
+	exit ;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	echo nse-tandem-nsk"$UNAME_RELEASE"
+	exit ;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk"$UNAME_RELEASE"
+	exit ;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+	echo nsv-tandem-nsk"$UNAME_RELEASE"
+	exit ;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+	echo nsx-tandem-nsk"$UNAME_RELEASE"
+	exit ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit ;;
+    DS/*:UNIX_System_V:*:*)
+	echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+	exit ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = 386; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo "$UNAME_MACHINE"-unknown-plan9
+	exit ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit ;;
+    SEI:*:*:SEIUX)
+	echo mips-sei-seiux"$UNAME_RELEASE"
+	exit ;;
+    *:DragonFly:*:*)
+	echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+	exit ;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case "$UNAME_MACHINE" in
+	    A*) echo alpha-dec-vms ; exit ;;
+	    I*) echo ia64-dec-vms ; exit ;;
+	    V*) echo vax-dec-vms ; exit ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	echo i386-pc-xenix
+	exit ;;
+    i*86:skyos:*:*)
+	echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+	exit ;;
+    i*86:rdos:*:*)
+	echo "$UNAME_MACHINE"-pc-rdos
+	exit ;;
+    i*86:AROS:*:*)
+	echo "$UNAME_MACHINE"-pc-aros
+	exit ;;
+    x86_64:VMkernel:*:*)
+	echo "$UNAME_MACHINE"-unknown-esx
+	exit ;;
+    amd64:Isilon\ OneFS:*:*)
+	echo x86_64-unknown-onefs
+	exit ;;
+esac
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+    mips:Linux | mips64:Linux)
+	# If we got here on MIPS GNU/Linux, output extra information.
+	cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+	;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+and
+  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git c/libcody/build-aux/config.sub w/libcody/build-aux/config.sub
new file mode 100755
index 00000000000..20f7cf29a9e
--- /dev/null
+++ w/libcody/build-aux/config.sub
@@ -0,0 +1,1833 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2018 Free Software Foundation, Inc.
+
+timestamp='2018-05-05'
+
+# This file 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2018 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Spilt fields of configuration type
+IFS="-" read -r field1 field2 field3 field4 <<EOF
+$1
+EOF
+
+# Separate into logical components for further validation
+case $1 in
+	*-*-*-*)
+		basic_machine=$field1-$field2
+		os=-$field3-$field4
+		;;
+	*-*-*)
+		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+		# parts
+		maybe_os=$field2-$field3
+		case $maybe_os in
+			nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
+			| linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+			| storm-chaos* | os2-emx* | rtmk-nova*)
+				basic_machine=$field1
+				os=-$maybe_os
+				;;
+			android-linux)
+				basic_machine=$field1-unknown
+				os=-linux-android
+				;;
+			*)
+				basic_machine=$field1-$field2
+				os=-$field3
+				;;
+		esac
+		;;
+	*-*)
+		basic_machine=$field1
+		os=-$field2
+		;;
+	*)
+		basic_machine=$1
+		os=
+		;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis | -knuth | -cray | -microblaze*)
+		os=
+		basic_machine=$1
+		;;
+	-bluegene*)
+		os=-cnk
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+	-chorusrdb)
+		os=-chorusrdb
+		basic_machine=$1
+		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco6)
+		os=-sco5v6
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*178)
+		os=-lynxos178
+		;;
+	-lynx*5)
+		os=-lynxos5
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| aarch64 | aarch64_be \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+	| am33_2.0 \
+	| arc | arceb \
+	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \
+	| avr | avr32 \
+	| ba \
+	| be32 | be64 \
+	| bfin \
+	| c4x | c8051 | clipper | csky \
+	| d10v | d30v | dlx | dsp16xx \
+	| e2k | epiphany \
+	| fido | fr30 | frv | ft32 \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| hexagon \
+	| i370 | i860 | i960 | ia16 | ia64 \
+	| ip2k | iq2000 \
+	| k1om \
+	| le32 | le64 \
+	| lm32 \
+	| m32c | m32r | m32rle | m68000 | m68k | m88k \
+	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+	| mips | mipsbe | mipseb | mipsel | mipsle \
+	| mips16 \
+	| mips64 | mips64el \
+	| mips64octeon | mips64octeonel \
+	| mips64orion | mips64orionel \
+	| mips64r5900 | mips64r5900el \
+	| mips64vr | mips64vrel \
+	| mips64vr4100 | mips64vr4100el \
+	| mips64vr4300 | mips64vr4300el \
+	| mips64vr5000 | mips64vr5000el \
+	| mips64vr5900 | mips64vr5900el \
+	| mipsisa32 | mipsisa32el \
+	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
+	| mipsisa64 | mipsisa64el \
+	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
+	| mipsisa64sb1 | mipsisa64sb1el \
+	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipsr5900 | mipsr5900el \
+	| mipstx39 | mipstx39el \
+	| mn10200 | mn10300 \
+	| moxie \
+	| mt \
+	| msp430 \
+	| nds32 | nds32le | nds32be \
+	| nfp \
+	| nios | nios2 | nios2eb | nios2el \
+	| ns16k | ns32k \
+	| open8 | or1k | or1knd | or32 \
+	| pdp10 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle \
+	| pru \
+	| pyramid \
+	| riscv32 | riscv64 \
+	| rl78 | rx \
+	| score \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh64 | sh64le \
+	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+	| spu \
+	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+	| ubicom32 \
+	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| visium \
+	| wasm32 \
+	| x86 | xc16x | xstormy16 | xtensa \
+	| z8k | z80)
+		basic_machine=$basic_machine-unknown
+		;;
+	c54x)
+		basic_machine=tic54x-unknown
+		;;
+	c55x)
+		basic_machine=tic55x-unknown
+		;;
+	c6x)
+		basic_machine=tic6x-unknown
+		;;
+	leon|leon[3-9])
+		basic_machine=sparc-$basic_machine
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
+		;;
+	m9s12z | m68hcs12z | hcs12z | s12z)
+		basic_machine=s12z-unknown
+		os=-none
+		;;
+	ms1)
+		basic_machine=mt-unknown
+		;;
+
+	strongarm | thumb | xscale)
+		basic_machine=arm-unknown
+		;;
+	xgate)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	xscaleeb)
+		basic_machine=armeb-unknown
+		;;
+
+	xscaleel)
+		basic_machine=armel-unknown
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| aarch64-* | aarch64_be-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
+	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+	| avr-* | avr32-* \
+	| ba-* \
+	| be32-* | be64-* \
+	| bfin-* | bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* \
+	| c8051-* | clipper-* | craynv-* | csky-* | cydra-* \
+	| d10v-* | d30v-* | dlx-* \
+	| e2k-* | elxsi-* \
+	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| hexagon-* \
+	| i*86-* | i860-* | i960-* | ia16-* | ia64-* \
+	| ip2k-* | iq2000-* \
+	| k1om-* \
+	| le32-* | le64-* \
+	| lm32-* \
+	| m32c-* | m32r-* | m32rle-* \
+	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+	| microblaze-* | microblazeel-* \
+	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+	| mips16-* \
+	| mips64-* | mips64el-* \
+	| mips64octeon-* | mips64octeonel-* \
+	| mips64orion-* | mips64orionel-* \
+	| mips64r5900-* | mips64r5900el-* \
+	| mips64vr-* | mips64vrel-* \
+	| mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* \
+	| mips64vr5000-* | mips64vr5000el-* \
+	| mips64vr5900-* | mips64vr5900el-* \
+	| mipsisa32-* | mipsisa32el-* \
+	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
+	| mipsisa64-* | mipsisa64el-* \
+	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
+	| mipsisa64sb1-* | mipsisa64sb1el-* \
+	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipsr5900-* | mipsr5900el-* \
+	| mipstx39-* | mipstx39el-* \
+	| mmix-* \
+	| mt-* \
+	| msp430-* \
+	| nds32-* | nds32le-* | nds32be-* \
+	| nfp-* \
+	| nios-* | nios2-* | nios2eb-* | nios2el-* \
+	| none-* | np1-* | ns16k-* | ns32k-* \
+	| open8-* \
+	| or1k*-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+	| pru-* \
+	| pyramid-* \
+	| riscv32-* | riscv64-* \
+	| rl78-* | romp-* | rs6000-* | rx-* \
+	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+	| sparclite-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
+	| tahoe-* \
+	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tile*-* \
+	| tron-* \
+	| ubicom32-* \
+	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+	| vax-* \
+	| visium-* \
+	| wasm32-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xc16x-* | xps100-* \
+	| xstormy16-* | xtensa*-* \
+	| ymp-* \
+	| z8k-* | z80-*)
+		;;
+	# Recognize the basic CPU types without company name, with glob match.
+	xtensa*)
+		basic_machine=$basic_machine-unknown
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-pc
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	abacus)
+		basic_machine=abacus-unknown
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amd64)
+		basic_machine=x86_64-pc
+		;;
+	amd64-*)
+		basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aros)
+		basic_machine=i386-pc
+		os=-aros
+		;;
+	asmjs)
+		basic_machine=asmjs-unknown
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	blackfin)
+		basic_machine=bfin-unknown
+		os=-linux
+		;;
+	blackfin-*)
+		basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	bluegene*)
+		basic_machine=powerpc-ibm
+		os=-cnk
+		;;
+	c54x-*)
+		basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	c55x-*)
+		basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	c6x-*)
+		basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	c90)
+		basic_machine=c90-cray
+		os=-unicos
+		;;
+	cegcc)
+		basic_machine=arm-unknown
+		os=-cegcc
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | j90)
+		basic_machine=j90-cray
+		os=-unicos
+		;;
+	craynv)
+		basic_machine=craynv-cray
+		os=-unicosmp
+		;;
+	cr16 | cr16-*)
+		basic_machine=cr16-unknown
+		os=-elf
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	crisv32 | crisv32-* | etraxfs*)
+		basic_machine=crisv32-axis
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	crx)
+		basic_machine=crx-unknown
+		os=-elf
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	decsystem10* | dec10*)
+		basic_machine=pdp10-dec
+		os=-tops10
+		;;
+	decsystem20* | dec20*)
+		basic_machine=pdp10-dec
+		os=-tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dicos)
+		basic_machine=i686-pc
+		os=-dicos
+		;;
+	djgpp)
+		basic_machine=i586-pc
+		os=-msdosdjgpp
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2*)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	e500v[12])
+		basic_machine=powerpc-unknown
+		os=$os"spe"
+		;;
+	e500v[12]-*)
+		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		os=$os"spe"
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+	i*86v32)
+		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	leon-*|leon[3-9]-*)
+		basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
+		;;
+	m68knommu)
+		basic_machine=m68k-unknown
+		os=-linux
+		;;
+	m68knommu-*)
+		basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	microblaze*)
+		basic_machine=microblaze-xilinx
+		;;
+	mingw64)
+		basic_machine=x86_64-pc
+		os=-mingw64
+		;;
+	mingw32)
+		basic_machine=i686-pc
+		os=-mingw32
+		;;
+	mingw32ce)
+		basic_machine=arm-unknown
+		os=-mingw32ce
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mips3*-*)
+		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	morphos)
+		basic_machine=powerpc-unknown
+		os=-morphos
+		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	ms1-*)
+		basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
+		;;
+	msys)
+		basic_machine=i686-pc
+		os=-msys
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	nacl)
+		basic_machine=le32-unknown
+		os=-nacl
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next)
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	neo-tandem)
+		basic_machine=neo-tandem
+		;;
+	nse-tandem)
+		basic_machine=nse-tandem
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	nsv-tandem)
+		basic_machine=nsv-tandem
+		;;
+	nsx-tandem)
+		basic_machine=nsx-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	openrisc | openrisc-*)
+		basic_machine=or32-unknown
+		;;
+	os400)
+		basic_machine=powerpc-ibm
+		os=-os400
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	parisc)
+		basic_machine=hppa-unknown
+		os=-linux
+		;;
+	parisc-*)
+		basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+	pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pc98)
+		basic_machine=i386-pc
+		;;
+	pc98-*)
+		basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	pentium | p5 | k5 | k6 | nexgen | viac3)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon | athlon_*)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2 | pentiumiii | pentium3)
+		basic_machine=i686-pc
+		;;
+	pentium4)
+		basic_machine=i786-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	pentium4-*)
+		basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc | ppcbe)	basic_machine=powerpc-unknown
+		;;
+	ppc-* | ppcbe-*)
+		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle)
+		basic_machine=powerpcle-unknown
+		;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+		;;
+	ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little)
+		basic_machine=powerpc64le-unknown
+		;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rdos | rdos64)
+		basic_machine=x86_64-pc
+		os=-rdos
+		;;
+	rdos32)
+		basic_machine=i386-pc
+		os=-rdos
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	s390 | s390-*)
+		basic_machine=s390-ibm
+		;;
+	s390x | s390x-*)
+		basic_machine=s390x-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sb1)
+		basic_machine=mipsisa64sb1-unknown
+		;;
+	sb1el)
+		basic_machine=mipsisa64sb1el-unknown
+		;;
+	sde)
+		basic_machine=mipsisa32-sde
+		os=-elf
+		;;
+	sei)
+		basic_machine=mips-sei
+		os=-seiux
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh5el)
+		basic_machine=sh5le-unknown
+		;;
+	simso-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	strongarm-* | thumb-*)
+		basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=alphaev5-cray
+		os=-unicos
+		;;
+	t90)
+		basic_machine=t90-cray
+		os=-unicos
+		;;
+	tile*)
+		basic_machine=$basic_machine-unknown
+		os=-linux-gnu
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	toad1)
+		basic_machine=pdp10-xkl
+		os=-tops20
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	tpf)
+		basic_machine=s390x-ibm
+		os=-tpf
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+		basic_machine=f301-fujitsu
+		;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	x64)
+		basic_machine=x86_64-pc
+		;;
+	xbox)
+		basic_machine=i686-pc
+		os=-mingw32
+		;;
+	xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	xscale-* | xscalee[bl]-*)
+		basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
+		;;
+	ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	mmix)
+		basic_machine=mmix-knuth
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+		basic_machine=sh-unknown
+		;;
+	cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x$os != x ]
+then
+case $os in
+	# First match some system type aliases that might get confused
+	# with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-auroraux)
+		os=-auroraux
+		;;
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# es1800 is here to avoid being matched by es* (a different OS)
+	-es1800*)
+		os=-ose
+		;;
+	# Now accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST end in a * to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+	      | -sym* | -kopensolaris* | -plan9* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* | -aros* | -cloudabi* | -sortix* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
+	      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
+	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \
+	      | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
+	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
+	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+	      | -morphos* | -superux* | -rtmk* | -windiss* \
+	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
+	      | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
+	      | -midnightbsd*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto-qnx*)
+		;;
+	-nto*)
+		os=`echo $os | sed -e 's|nto|nto-qnx|'`
+		;;
+	-sim | -xray | -os68k* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo "$os" | sed -e 's|mac|macos|'`
+		;;
+	-linux-dietlibc)
+		os=-linux-dietlibc
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-os400*)
+		os=-os400
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-atheos*)
+		os=-atheos
+		;;
+	-syllable*)
+		os=-syllable
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-nova*)
+		os=-rtmk-nova
+		;;
+	-ns2)
+		os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-tpf*)
+		os=-tpf
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+		os=-mint
+		;;
+	-zvmoe)
+		os=-zvmoe
+		;;
+	-dicos*)
+		os=-dicos
+		;;
+	-pikeos*)
+		# Until real need of OS specific support for
+		# particular features comes up, bare metal
+		# configurations are quite functional.
+		case $basic_machine in
+		    arm*)
+			os=-eabi
+			;;
+		    *)
+			os=-elf
+			;;
+		esac
+		;;
+	-nacl*)
+		;;
+	-ios)
+		;;
+	-none)
+		;;
+	-*-eabi)
+		case $basic_machine in
+		    arm*)
+			;;
+		esac
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	score-*)
+		os=-elf
+		;;
+	spu-*)
+		os=-elf
+		;;
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	c4x-* | tic4x-*)
+		os=-coff
+		;;
+	c8051-*)
+		os=-elf
+		;;
+	hexagon-*)
+		os=-elf
+		;;
+	tic54x-*)
+		os=-coff
+		;;
+	tic55x-*)
+		os=-coff
+		;;
+	tic6x-*)
+		os=-coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=-tops20
+		;;
+	pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mep-*)
+		os=-elf
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	or32-*)
+		os=-coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	pru-*)
+		os=-elf
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-knuth)
+		os=-mmixware
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next)
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-gould)
+		os=-sysv
+		;;
+	*-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+	*-sgi)
+		os=-irix
+		;;
+	*-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-cnk*|-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-os400*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-tpf*)
+				vendor=ibm
+				;;
+			-vxsim* | -vxworks* | -windiss*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo "$basic_machine$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git c/libcody/build-aux/install-sh w/libcody/build-aux/install-sh
new file mode 100755
index 00000000000..8175c640fe6
--- /dev/null
+++ w/libcody/build-aux/install-sh
@@ -0,0 +1,518 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2018-03-11.20; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+	*/) dst=$dst$dstbase;;
+	*)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            # Note that $RANDOM variable is not portable (e.g. dash);  Use it
+            # here however when possible just to lower collision chance.
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+            trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            # Because "mkdir -p" follows existing symlinks and we likely work
+            # directly in world-writeable /tmp, make sure that the '$tmpdir'
+            # directory is successfully created first before we actually test
+            # 'mkdir -p' feature.
+            if (umask $mkdir_umask &&
+                $mkdirprog $mkdir_mode "$tmpdir" &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   test_tmpdir="$tmpdir/a"
+                   ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git c/libcody/client.cc w/libcody/client.cc
new file mode 100644
index 00000000000..56c13a4118c
--- /dev/null
+++ w/libcody/client.cc
@@ -0,0 +1,331 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+// C
+#include <cerrno>
+#include <cstring>
+
+// Client code
+
+namespace Cody {
+
+// These do not need to be members
+static Packet ConnectResponse (std::vector<std::string> &words);
+static Packet PathnameResponse (std::vector<std::string> &words);
+static Packet OKResponse (std::vector<std::string> &words);
+static Packet IncludeTranslateResponse (std::vector<std::string> &words);
+
+// Must be consistently ordered with the RequestCode enum
+static Packet (*const responseTable[Detail::RC_HWM])
+  (std::vector<std::string> &) =
+  {
+    &ConnectResponse,
+    &PathnameResponse,
+    &PathnameResponse,
+    &PathnameResponse,
+    &OKResponse,
+    &IncludeTranslateResponse,
+    &OKResponse,
+  };
+
+Client::Client ()
+{
+  fd.from = fd.to = -1;
+}
+
+Client::Client (Client &&src)
+  : write (std::move (src.write)),
+    read (std::move (src.read)),
+    corked (std::move (src.corked)),
+    is_direct (src.is_direct),
+    is_connected (src.is_connected)
+{
+  if (is_direct)
+    server = src.server;
+  else
+    {
+      fd.from = src.fd.from;
+      fd.to = src.fd.to;
+    }
+}
+
+Client::~Client ()
+{
+}
+
+Client &Client::operator= (Client &&src)
+{
+  write = std::move (src.write);
+  read = std::move (src.read);
+  corked = std::move (src.corked);
+  is_direct = src.is_direct;
+  is_connected = src.is_connected;
+  if (is_direct)
+    server = src.server;
+  else
+    {
+      fd.from = src.fd.from;
+      fd.to = src.fd.to;
+    }
+
+  return *this;
+}
+
+int Client::CommunicateWithServer ()
+{
+  write.PrepareToWrite ();
+  read.PrepareToRead ();
+  if (IsDirect ())
+    server->DirectProcess (write, read);
+  else
+    {
+      // Write the write buffer
+      while (int e = write.Write (fd.to))
+	if (e != EAGAIN && e != EINTR)
+	  return e;
+      // Read the read buffer
+      while (int e = read.Read (fd.from))
+	if (e != EAGAIN && e != EINTR)
+	  return e;
+    }
+
+  return 0;
+}
+
+static Packet CommunicationError (int err)
+{
+  std::string e {u8"communication error:"};
+  e.append (strerror (err));
+
+  return Packet (Client::PC_ERROR, std::move (e));
+}
+
+Packet Client::ProcessResponse (std::vector<std::string> &words,
+			       unsigned code, bool isLast)
+{
+  if (int e = read.Lex (words))
+    {
+      if (e == EINVAL)
+	{
+	  std::string msg (u8"malformed string '");
+	  msg.append (words[0]);
+	  msg.append (u8"'");
+	  return Packet (Client::PC_ERROR, std::move (msg));
+	}
+      else
+	return Packet (Client::PC_ERROR, u8"missing response");
+    }
+
+  Assert (!words.empty ());
+  if (words[0] == u8"ERROR")
+    return Packet (Client::PC_ERROR,
+		  std::move (words.size () == 2 ? words[1]
+			     : u8"malformed error response"));
+
+  if (isLast && !read.IsAtEnd ())
+    return Packet (Client::PC_ERROR,
+		   std::string (u8"unexpected extra response"));
+
+  Assert (code < Detail::RC_HWM);
+  Packet result (responseTable[code] (words));
+  result.SetRequest (code);
+  if (result.GetCode () == Client::PC_ERROR && result.GetString ().empty ())
+    {
+      std::string msg {u8"malformed response '"};
+
+      read.LexedLine (msg);
+      msg.append (u8"'");
+      result.GetString () = std::move (msg);
+    }
+  else if (result.GetCode () == Client::PC_CONNECT)
+    is_connected = true;
+
+  return result;
+}
+
+Packet Client::MaybeRequest (unsigned code)
+{
+  if (IsCorked ())
+    {
+      corked.push_back (code);
+      return Packet (PC_CORKED);
+    }
+
+  if (int err = CommunicateWithServer ())
+    return CommunicationError (err);
+
+  std::vector<std::string> words;
+  return ProcessResponse(words, code, true);
+}
+
+void Client::Cork ()
+{
+  if (corked.empty ())
+    corked.push_back (-1);
+}
+
+std::vector<Packet> Client::Uncork ()
+{
+  std::vector<Packet> result;
+
+  if (corked.size () > 1)
+    {
+      if (int err = CommunicateWithServer ())
+	result.emplace_back (CommunicationError (err));
+      else
+	{
+	  std::vector<std::string> words;
+	  for (auto iter = corked.begin () + 1; iter != corked.end ();)
+	    {
+	      char code = *iter;
+	      ++iter;
+	      result.emplace_back (ProcessResponse (words, code,
+						    iter == corked.end ()));
+	    }
+	}
+    }
+
+  corked.clear ();
+
+  return result;
+}
+
+// Now the individual message handlers
+
+// HELLO $vernum $agent $ident
+Packet Client::Connect (char const *agent, char const *ident,
+			  size_t alen, size_t ilen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"HELLO");
+  write.AppendInteger (Version);
+  write.AppendWord (agent, true, alen);
+  write.AppendWord (ident, true, ilen);
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_CONNECT);
+}
+
+// HELLO VERSION AGENT
+Packet ConnectResponse (std::vector<std::string> &words)
+{
+  if (words[0] == u8"HELLO" && words.size () == 3)
+    {
+      char *eptr;
+      unsigned long version = strtoul (words[1].c_str (), &eptr, 10);
+      if (*eptr || version < Version)
+	return Packet (Client::PC_ERROR, u8"incompatible version");
+      else
+	return Packet (Client::PC_CONNECT, version);
+    }
+
+  return Packet (Client::PC_ERROR, u8"");
+}
+
+// MODULE-REPO
+Packet Client::ModuleRepo ()
+{
+  write.BeginLine ();
+  write.AppendWord (u8"MODULE-REPO");
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_MODULE_REPO);
+}
+
+// PATHNAME $dir | ERROR
+Packet PathnameResponse (std::vector<std::string> &words)
+{
+  if (words[0] == u8"PATHNAME" && words.size () == 2)
+    return Packet (Client::PC_PATHNAME, std::move (words[1]));
+
+  return Packet (Client::PC_ERROR, u8"");
+}
+
+// INVOKE $args
+Packet Client::InvokeSubProcess (char const *const *argv, size_t argc)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"INVOKE");
+
+  for(size_t i = 0; i < argc; i++) 
+    write.AppendWord (argv[i]);
+
+  write.EndLine ();
+  return MaybeRequest (Detail::RC_INVOKE);
+}
+
+// OK or ERROR
+Packet OKResponse (std::vector<std::string> &words)
+{
+  if (words[0] == u8"OK")
+    return Packet (Client::PC_OK);
+  else
+    return Packet (Client::PC_ERROR,
+		   words.size () == 2 ? std::move (words[1]) : "");
+}
+
+// MODULE-EXPORT $modulename
+Packet Client::ModuleExport (char const *module, size_t mlen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"MODULE-EXPORT");
+  write.AppendWord (module, true, mlen);
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_MODULE_EXPORT);
+}
+
+// MODULE-IMPORT $modulename
+Packet Client::ModuleImport (char const *module, size_t mlen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"MODULE-IMPORT");
+  write.AppendWord (module, true, mlen);
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_MODULE_IMPORT);
+}
+
+// MODULE-COMPILED $modulename
+Packet Client::ModuleCompiled (char const *module, size_t mlen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"MODULE-COMPILED");
+  write.AppendWord (module, true, mlen);
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_MODULE_COMPILED);
+}
+
+Packet Client::IncludeTranslate (char const *include, size_t ilen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"INCLUDE-TRANSLATE");
+  write.AppendWord (include, true, ilen);
+  write.EndLine ();
+
+  return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE);
+}
+
+// BOOL $truthiness
+// PATHNAME $cmifile
+Packet IncludeTranslateResponse (std::vector<std::string> &words)
+{
+  if (words[0] == u8"BOOL" && words.size () == 2)
+    {
+      if (words[1] == u8"FALSE")
+	return Packet (Client::PC_BOOL, 0);
+      else if (words[1] == u8"TRUE")
+	return Packet (Client::PC_BOOL, 1);
+      else
+	return Packet (Client::PC_ERROR, u8"");
+    }
+  else
+    return PathnameResponse (words);
+}
+
+}
+
diff --git c/libcody/cmake/libcody-config-ix.cmake w/libcody/cmake/libcody-config-ix.cmake
new file mode 100644
index 00000000000..0606e85e3b8
--- /dev/null
+++ w/libcody/cmake/libcody-config-ix.cmake
@@ -0,0 +1,43 @@
+# message(STATUS "*top config-ix* CMAKE_SYSTEM : ${CMAKE_SYSTEM}")
+
+include(CheckIncludeFile)
+include(CheckIncludeFileCXX)
+#include(CheckLibraryExists)
+#include(CheckSymbolExists)
+include(CheckFunctionExists)
+#include(CheckCXXSourceCompiles)
+#include(TestBigEndian)
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+
+# Flags
+
+check_cxx_compiler_flag(-stdlib=libc++    LIBCODY_CXX_HAS_STDLIB_FLAG)
+
+check_cxx_compiler_flag(-fno-enforce-eh-specs LIBCODY_HAS_NOENFORCE)
+check_cxx_compiler_flag(-fno-stack-protector LIBCODY_HAS_NOSTACKPROT)
+check_cxx_compiler_flag(-fno-threadsafe-statics LIBCODY_HAS_NOTHREADSAFESTATICS)
+
+check_cxx_compiler_flag(-Wno-gnu-zero-variadic-macro-arguments LIBCODY_CXX_W_GZVMA)
+
+# Address github issue #10
+if (NOT CODY_WITHEXCEPTIONS)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
+  if (LIBCODY_HAS_NOENFORCE)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-enforce-eh-specs")
+  endif()
+endif()
+
+if (LIBCODY_HAS_NOSTACKPROT)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector")
+endif()
+if (LIBCODY_HAS_NOTHREADSAFESTATICS)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
+endif()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Woverloaded-virtual -Wshadow")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -Wno-unused-variable")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers")
+if (LIBCODY_CXX_W_GZVMA)
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
+endif ()
diff --git c/libcody/cody.hh w/libcody/cody.hh
new file mode 100644
index 00000000000..330a8401bb9
--- /dev/null
+++ w/libcody/cody.hh
@@ -0,0 +1,769 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+#ifndef CODY_HH
+#define CODY_HH 1
+
+// Have a known-good list of networking systems
+#if defined (__unix__) || defined (__MACH__)
+#define CODY_NETWORKING 1
+#else
+#define CODY_NETWORKING 0
+#endif
+#if 0  // For testing
+#undef CODY_NETWORKING
+#define CODY_NETWORKING 0
+#endif
+
+// C++
+#include <memory>
+#include <string>
+#include <vector>
+// C
+#include <cstddef>
+// OS
+#include <errno.h>
+#include <sys/types.h>
+#if CODY_NETWORKING
+#include <sys/socket.h>
+#endif
+
+namespace Cody {
+
+// Set version to 1, as this is completely incompatible with 0.
+// Fortunately both versions 0 and 1 will recognize each other's HELLO
+// messages sufficiently to error out
+constexpr unsigned Version = 1;
+
+// FIXME: I guess we need a file-handle abstraction here
+// Is windows DWORDPTR still?, or should it be FILE *? (ew).
+
+namespace Detail  {
+
+// C++11 doesn't have utf8 character literals :(
+
+template<unsigned I>
+constexpr char S2C (char const (&s)[I])
+{
+  static_assert (I == 2, "only single octet strings may be converted");
+  return s[0];
+}
+
+/// Internal buffering class.  Used to concatenate outgoing messages
+/// and Lex incoming ones.
+class MessageBuffer
+{
+  std::vector<char> buffer;  ///< buffer holding the message
+  size_t lastBol = 0;  ///< location of the most recent Beginning Of
+		       ///< Line, or position we've readed when writing
+
+public:
+  MessageBuffer () = default;
+  ~MessageBuffer () = default;
+  MessageBuffer (MessageBuffer &&) = default;
+  MessageBuffer &operator= (MessageBuffer &&) = default;
+
+public:
+  ///
+  /// Finalize a buffer to be written.  No more lines can be added to
+  /// the buffer.  Use before a sequence of Write calls.
+  void PrepareToWrite ()
+  {
+    buffer.push_back (u8"\n"[0]);
+    lastBol = 0;
+  }
+  ///
+  /// Prepare a buffer for reading.  Use before a sequence of Read calls.
+  void PrepareToRead ()
+  {
+    buffer.clear ();
+    lastBol = 0;
+  }
+
+public:
+  /// Begin a message line.  Use before a sequence of Append and
+  /// related calls.
+  void BeginLine ();
+  /// End a message line.  Use after a sequence of Append and related calls.
+  void EndLine () {}
+
+public:
+  /// Append a string to the current line.  No whitespace is prepended
+  /// or appended.
+  ///
+  /// @param str the string to be written
+  /// @param maybe_quote indicate if there's a possibility the string
+  /// contains characters that need quoting.  Defaults to false.
+  /// It is always safe to set
+  /// this true, but that causes an additional scan of the string.
+  /// @param len The length of the string.  If not specified, strlen
+  /// is used to find the length.
+  void Append (char const *str, bool maybe_quote = false,
+	       size_t len = ~size_t (0));
+
+  ///
+  /// Add whitespace word separator.  Multiple adjacent whitespace is fine.
+  void Space ()
+  {
+    Append (Detail::S2C(u8" "));
+  }
+
+public:
+  /// Add a word as with Append, but prefixing whitespace to make a
+  /// separate word
+  void AppendWord (char const *str, bool maybe_quote = false,
+		   size_t len = ~size_t (0))
+  {
+    if (buffer.size () != lastBol)
+      Space ();
+    Append (str, maybe_quote, len);
+  }
+  /// Add a word as with AppendWord
+  /// @param str the string to append
+  /// @param maybe_quote string might need quoting, as for Append
+  void AppendWord (std::string const &str, bool maybe_quote = false)
+  {
+    AppendWord (str.data (), maybe_quote, str.size ());
+  }
+  ///
+  /// Add an integral value, prepending a space.
+  void AppendInteger (unsigned u);
+
+private:
+  /// Append a literal character.
+  /// @param c character to append
+  void Append (char c);
+
+public:
+  /// Lex the next input line into a vector of words.
+  /// @param words filled with a vector of lexed strings
+  /// @result 0 if no errors, an errno value on lexxing error such as
+  /// there being no next line (ENOENT), or malformed quoting (EINVAL)
+  int Lex (std::vector<std::string> &words);
+
+public:
+  /// Append the most-recently lexxed line to a string.  May be useful
+  /// in error messages.  The unparsed line is appended -- before any
+  /// unquoting.
+  /// If we had c++17 string_view, we'd simply return a view of the
+  /// line, and leave it to the caller to do any concatenation.
+  /// @param l string to-which the lexxed line is appended.
+  void LexedLine (std::string &l);
+
+public:
+  /// Detect if we have reached the end of the input buffer.
+  /// I.e. there are no more lines to Lex
+  /// @result True if at end
+  bool IsAtEnd () const
+  {
+    return lastBol == buffer.size ();
+  }
+
+public:
+  /// Read from end point into a read buffer, as with read(2).  This will
+  /// not block , unless FD is blocking, and there is nothing
+  /// immediately available.
+  /// @param fd file descriptor to read from.  This may be a regular
+  /// file, pipe or socket.
+  /// @result on error returns errno.  If end of file occurs, returns
+  /// -1.  At end of message returns 0.  If there is more needed
+  /// returns EAGAIN (or possibly EINTR).  If the message is
+  /// malformed, returns EINVAL.
+  int Read (int fd) noexcept;
+
+public:
+  /// Write to an end point from a write buffer, as with write(2).  As
+  /// with Read, this will not usually block.
+  /// @param fd file descriptor to write to.  This may be a regular
+  /// file, pipe or socket.
+  /// @result on error returns errno.
+  /// At end of message returns 0.  If there is more to write
+  /// returns EAGAIN (or possibly EINTR).
+  int Write (int fd) noexcept;
+};
+
+///
+/// Request codes.  Perhaps this should be exposed?  These are likely
+/// useful to servers that queue requests.
+enum RequestCode
+{
+  RC_CONNECT,
+  RC_MODULE_REPO,
+  RC_MODULE_EXPORT,
+  RC_MODULE_IMPORT,
+  RC_MODULE_COMPILED,
+  RC_INCLUDE_TRANSLATE,
+  RC_INVOKE,
+  RC_HWM
+};
+
+/// Internal file descriptor tuple.  It's used as an anonymous union member.
+struct FD
+{
+  int from;	///< Read from this FD
+  int to;	///< Write to this FD
+};
+
+}
+
+///
+/// Response data for a request.  Returned by Client's request calls,
+/// which return a single Packet.  When the connection is Corked, the
+/// Uncork call will return a vector of Packets.
+class Packet
+{
+public:
+  ///
+  /// Packet is a variant structure.  These are the possible content types.
+  enum Category { INTEGER, STRING, VECTOR};
+
+private:
+  // std:variant is a C++17 thing, so we're doing this ourselves.
+  union
+  {
+    size_t integer;	///< Integral value
+    std::string string; ///< String value
+    std::vector<std::string> vector;  ///< Vector of string value
+  };
+  Category cat : 2;  ///< Discriminatory
+
+private:
+  unsigned short code = 0;  ///< Packet type
+  unsigned short request = 0;
+
+public:
+  Packet (unsigned c, size_t i = 0)
+    : integer (i), cat (INTEGER), code (c)
+  {
+  }
+  Packet (unsigned c, std::string &&s)
+    : string (std::move (s)), cat (STRING), code (c)
+  {
+  }
+  Packet (unsigned c, std::string const &s)
+    : string (s), cat (STRING), code (c)
+  {
+  }
+  Packet (unsigned c, std::vector<std::string> &&v)
+    : vector (std::move (v)), cat (VECTOR), code (c)
+  {
+  }
+  // No non-move constructor from a vector.  You should not be doing
+  // that.
+
+  // Only move constructor and move assignment
+  Packet (Packet &&t)
+  {
+    Create (std::move (t));
+  }
+  Packet &operator= (Packet &&t)
+  {
+    Destroy ();
+    Create (std::move (t));
+
+    return *this;
+  }
+  ~Packet ()
+  {
+    Destroy ();
+  }
+
+private:
+  ///
+  /// Variant move creation from another packet
+  void Create (Packet &&t);
+  ///
+  /// Variant destruction
+  void Destroy ();
+
+public:
+  ///
+  /// Return the packet type
+  unsigned GetCode () const
+  {
+    return code;
+  }
+  ///
+  /// Return the packet type
+  unsigned GetRequest () const
+  {
+    return request;
+  }
+  void SetRequest (unsigned r)
+  {
+    request = r;
+  }
+  ///
+  /// Return the category of the packet's payload
+  Category GetCategory () const
+  {
+    return cat;
+  }
+
+public:
+  ///
+  /// Return an integral payload.  Undefined if the category is not INTEGER
+  size_t GetInteger () const
+  {
+    return integer;
+  }
+  ///
+  /// Return (a reference to) a string payload.  Undefined if the
+  /// category is not STRING
+  std::string const &GetString () const
+  {
+    return string;
+  }
+  std::string &GetString ()
+  {
+    return string;
+  }
+  ///
+  /// Return (a reference to) a constant vector of strings payload.
+  /// Undefined if the category is not VECTOR
+  std::vector<std::string> const &GetVector () const
+  {
+    return vector;
+  }
+  ///
+  /// Return (a reference to) a non-conatant vector of strings payload.
+  /// Undefined if the category is not VECTOR
+  std::vector<std::string> &GetVector ()
+  {
+    return vector;
+  }
+};
+
+class Server;
+
+///
+/// Client-side (compiler) object.
+class Client
+{
+public:
+  /// Response packet codes
+  enum PacketCode
+  {
+    PC_CORKED,		///< Messages are corked
+    PC_CONNECT,		///< Packet is integer version
+    PC_ERROR,		///< Packet is error string
+    PC_OK,
+    PC_BOOL,
+    PC_PATHNAME
+  };
+
+private:
+  Detail::MessageBuffer write; ///< Outgoing write buffer
+  Detail::MessageBuffer read;  ///< Incoming read buffer
+  std::string corked; ///< Queued request tags
+  union
+  {
+    Detail::FD fd;   ///< FDs connecting to server
+    Server *server;  ///< Directly connected server
+  };
+  bool is_direct = false;  ///< Discriminator
+  bool is_connected = false;  /// Connection handshake succesful
+
+private:
+  Client ();
+public:
+  /// Direct connection constructor.
+  /// @param s Server to directly connect
+  Client (Server *s)
+    : Client ()
+  {
+    is_direct = true;
+    server = s;
+  }
+  /// Communication connection constructor
+  /// @param from file descriptor to read from
+  /// @param to file descriptor to write to, defaults to from
+  Client (int from, int to = -1)
+    : Client ()
+  {
+    fd.from = from;
+    fd.to = to < 0 ? from : to;
+  }
+  ~Client ();
+  // We have to provide our own move variants, because of the variant member.
+  Client (Client &&);
+  Client &operator= (Client &&);
+
+public:
+  ///
+  /// Direct connection predicate
+  bool IsDirect () const
+  {
+    return is_direct;
+  }
+  ///
+  /// Successful handshake predicate
+  bool IsConnected () const
+  {
+    return is_connected;
+  }
+
+public:
+  ///
+  /// Get the read FD
+  /// @result the FD to read from, -1 if a direct connection
+  int GetFDRead () const
+  {
+    return is_direct ? -1 : fd.from;
+  }
+  ///
+  /// Get the write FD
+  /// @result the FD to write to, -1 if a direct connection
+  int GetFDWrite () const
+  {
+    return is_direct ? -1 : fd.to;
+  }
+  ///
+  /// Get the directly-connected server
+  /// @result the server, or nullptr if a communication connection
+  Server *GetServer () const
+  {
+    return is_direct ? server : nullptr;
+  }
+
+public:
+  ///
+  /// Perform connection handshake.  All othe requests will result in
+  /// errors, until handshake is succesful.
+  /// @param agent compiler identification
+  /// @param ident compilation identifiation (maybe nullptr)
+  /// @param alen length of agent string, if known
+  /// @param ilen length of ident string, if known
+  /// @result packet indicating success (or deferrment) of the connection.
+  Packet Connect (char const *agent, char const *ident,
+		 size_t alen = ~size_t (0), size_t ilen = ~size_t (0));
+  /// std::string wrapper for connection
+  /// @param agent compiler identification
+  /// @param ident compilation identification
+  Packet Connect (std::string const &agent, std::string const &ident)
+  {
+    return Connect (agent.c_str (), ident.c_str (),
+		    agent.size (), ident.size ());
+  }
+
+public:
+  /// Invoke a sub process
+  /// @param str command args
+  /// @result packet indicating success or error of command
+  Packet InvokeSubProcess (char const *const *argv, size_t argc);
+
+  Packet InvokeSubProcess (std::vector<const char *> &args)
+  {
+    return InvokeSubProcess (args.data (), args.size ());
+  }
+
+public:
+  /// Request compiler module repository
+  /// @result packet indicating repo
+  Packet ModuleRepo ();
+
+  /// Inform of compilation of a named module interface or partition,
+  /// or a header unit
+  /// @param str module or header-unit
+  /// @param len name length, if known
+  /// @result CMI name (or deferrment/error)
+  Packet ModuleExport (char const *str, size_t len = ~size_t (0));
+  Packet ModuleExport (std::string const &s)
+  {
+    return ModuleExport (s.c_str (), s.size ());
+  }
+
+  /// Importation of a module, partition or header-unit
+  /// @param str module or header-unit
+  /// @param len name length, if known
+  /// @result CMI name (or deferrment/error)
+  Packet ModuleImport (char const *str, size_t len = ~size_t (0));
+  Packet ModuleImport (std::string const &s)
+  {
+    return ModuleImport (s.c_str (), s.size ());
+  }
+
+  /// Successful compilation of a module interface, partition or
+  /// header-unit.  Must have been preceeded by a ModuleExport
+  /// request.
+  /// @param str module or header-unit
+  /// @param len name length, if known
+  /// @result  OK (or deferment/error)
+  Packet ModuleCompiled (char const *str, size_t len = ~size_t (0));
+  Packet ModuleCompiled (std::string const &s)
+  {
+    return ModuleCompiled (s.c_str (), s.size ());
+  }
+
+  /// Include translation query.
+  /// @param str header unit name
+  /// @param len name length, if known
+  /// @result  Packet indicating include translation boolean, or CMI
+  /// name (or deferment/error)
+  Packet IncludeTranslate (char const *str, size_t len = ~size_t (0));
+  Packet IncludeTranslate (std::string const &s)
+  {
+    return IncludeTranslate (s.c_str (), s.size ());
+  }
+
+public:
+  /// Cork the connection.  All requests are queued up.  Each request
+  /// call will return a PC_CORKED packet.
+  void Cork ();
+
+  /// Uncork the connection.  All queued requests are sent to the
+  /// server, and a block of responses waited for.
+  /// @result A vector of packets, containing the in-order responses to the
+  /// queued requests.
+  std::vector<Packet> Uncork ();
+  ///
+  /// Indicate corkedness of connection
+  bool IsCorked () const
+  {
+    return !corked.empty ();
+  }
+
+private:
+  Packet ProcessResponse (std::vector<std::string> &, unsigned code,
+			  bool isLast);
+  Packet MaybeRequest (unsigned code);
+  int CommunicateWithServer ();
+};
+
+/// This server-side class is used to resolve requests from one or
+/// more clients.  You are expected to derive from it and override the
+/// virtual functions it provides.  The connection resolver may return
+/// a different resolved object to service the remainder of the
+/// connection -- for instance depending on the compiler that is
+/// making the requests.
+class Resolver
+{
+public:
+  Resolver () = default;
+  virtual ~Resolver ();
+
+protected:
+  /// Mapping from a module or header-unit name to a CMI file name.
+  /// @param module module name
+  /// @result CMI name
+  virtual std::string GetCMIName (std::string const &module);
+
+  /// Return the CMI file suffix to use
+  /// @result CMI suffix, a statically allocated string
+  virtual char const *GetCMISuffix ();
+
+public:
+  /// When the requests of a directly-connected server are processed,
+  /// we may want to wait for the requests to complete (for instance a
+  /// set of subjobs).
+  /// @param s directly connected server.
+  virtual void WaitUntilReady (Server *s);
+
+public:
+  /// Provide an error response.
+  /// @param s the server to provide the response to.
+  /// @param msg the error message
+  virtual void ErrorResponse (Server *s, std::string &&msg);
+
+public:
+  /// Connection handshake.  Provide response to server and return new
+  /// (or current) resolver, or nullptr.
+  /// @param s server to provide response to
+  /// @param version the client's version number
+  /// @param agent the client agent (compiler identification)
+  /// @param ident the compilation identification (may be empty)
+  /// @result nullptr in the case of an error.  An error response will
+  /// be sent.  If handing off to another resolver, return that,
+  /// otherwise this
+  virtual Resolver *ConnectRequest (Server *s, unsigned version,
+				    std::string &agent, std::string &ident);
+
+public:
+  // return 0 on ok, ERRNO on failure, -1 on unspecific error
+  virtual int ModuleRepoRequest (Server *s);
+  virtual int ModuleExportRequest (Server *s, std::string &module);
+  virtual int ModuleImportRequest (Server *s, std::string &module);
+  virtual int ModuleCompiledRequest (Server *s, std::string &module);
+  virtual int IncludeTranslateRequest (Server *s, std::string &include);
+
+public:
+  virtual int InvokeSubProcessRequest (Server *s, std::vector<std::string> &args);
+};
+
+
+/// This server-side (build system) class handles a single connection
+/// to a client.  It has 3 states, READING:accumulating a message
+/// block froma client, WRITING:writing a message block to a client
+/// and PROCESSING:resolving requests.  If the server does not spawn
+/// jobs to build needed artifacts, the PROCESSING state will be brief.
+class Server
+{
+public:
+  enum Direction
+  {
+    READING,  // Server is waiting for completion of a (set of)
+	      // requests from client.  The next state will be PROCESSING.
+    WRITING,  // Server is writing a (set of) responses to client.
+	      // The next state will be READING.
+    PROCESSING  // Server is processing client request(s).  The next
+		// state will be WRITING.
+  };
+
+private:
+  Detail::MessageBuffer write;
+  Detail::MessageBuffer read;
+  Resolver *resolver;
+  Detail::FD fd;
+  bool is_connected = false;
+  Direction direction : 2;
+
+public:
+  Server (Resolver *r);
+  Server (Resolver *r, int from, int to = -1)
+    : Server (r)
+  {
+    fd.from = from;
+    fd.to = to >= 0 ? to : from;
+  }
+  ~Server ();
+  Server (Server &&);
+  Server &operator= (Server &&);
+
+public:
+  bool IsConnected () const
+  {
+    return is_connected;
+  }
+
+public:
+  void SetDirection (Direction d)
+  {
+    direction = d;
+  }
+
+public:
+  Direction GetDirection () const
+  {
+    return direction;
+  }
+  int GetFDRead () const
+  {
+    return fd.from;
+  }
+  int GetFDWrite () const
+  {
+    return fd.to;
+  }
+  Resolver *GetResolver () const
+  {
+    return resolver;
+  }
+
+public:
+  /// Process requests from a directly-connected client.  This is a
+  /// small wrapper around ProcessRequests, with some buffer swapping
+  /// for communication.  It is expected that such processessing is
+  /// immediate.
+  /// @param from message block from client
+  /// @param to message block to client
+  void DirectProcess (Detail::MessageBuffer &from, Detail::MessageBuffer &to);
+
+public:
+  /// Process the messages queued in the read buffer.  We enter the
+  /// PROCESSING state, and each message line causes various resolver
+  /// methods to be called.  Once processed, the server may need to
+  /// wait for all the requests to be ready, or it may be able to
+  /// immediately write responses back.
+  void ProcessRequests ();
+
+public:
+  /// Accumulate an error response.
+  /// @param error the error message to encode
+  /// @param elen length of error, if known
+  void ErrorResponse (char const *error, size_t elen = ~size_t (0));
+  void ErrorResponse (std::string const &error)
+  {
+    ErrorResponse (error.data (), error.size ());
+  }
+
+  /// Accumulate an OK response
+  void OKResponse ();
+
+  /// Accumulate a boolean response
+  void BoolResponse (bool);
+
+  /// Accumulate a pathname response
+  /// @param path (may be nullptr, or empty)
+  /// @param rlen length, if known
+  void PathnameResponse (char const *path, size_t plen = ~size_t (0));
+  void PathnameResponse (std::string const &path)
+  {
+    PathnameResponse (path.data (), path.size ());
+  }
+
+public:
+  /// Accumulate a (successful) connection response
+  /// @param agent the server-side agent
+  /// @param alen agent length, if known
+  void ConnectResponse (char const *agent, size_t alen = ~size_t (0));
+  void ConnectResponse (std::string const &agent)
+  {
+    ConnectResponse (agent.data (), agent.size ());
+  }
+
+public:
+  /// Write message block to client.  Semantics as for
+  /// MessageBuffer::Write.
+  /// @result errno or completion (0).
+  int Write ()
+  {
+    return write.Write (fd.to);
+  }
+  /// Initialize for writing a message block.  All responses to the
+  /// incomping message block must be complete  Enters WRITING state.
+  void PrepareToWrite ()
+  {
+    write.PrepareToWrite ();
+    direction = WRITING;
+  }
+
+public:
+  /// Read message block from client.  Semantics as for
+  /// MessageBuffer::Read.
+  /// @result errno, eof (-1) or completion (0)
+  int Read ()
+  {
+    return read.Read (fd.from);
+  }
+  /// Initialize for reading a message block.  Enters READING state.
+  void PrepareToRead ()
+  {
+    read.PrepareToRead ();
+    direction = READING;
+  }
+};
+
+// Helper network stuff
+
+#if CODY_NETWORKING
+// Socket with specific address
+int OpenSocket (char const **, sockaddr const *sock, socklen_t len);
+int ListenSocket (char const **, sockaddr const *sock, socklen_t len,
+		  unsigned backlog);
+
+// Local domain socket (eg AF_UNIX)
+int OpenLocal (char const **, char const *name);
+int ListenLocal (char const **, char const *name, unsigned backlog = 0);
+
+// ipv6 socket
+int OpenInet6 (char const **e, char const *name, int port);
+int ListenInet6 (char const **, char const *name, int port,
+		 unsigned backlog = 0);
+#endif
+
+// FIXME: Mapping file utilities?
+
+}
+
+#endif // CODY_HH
diff --git c/libcody/config.h.in w/libcody/config.h.in
new file mode 100644
index 00000000000..85425472a16
--- /dev/null
+++ w/libcody/config.h.in
@@ -0,0 +1,29 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Bug reporting location */
+#undef BUGURL
+
+/* Enable checking */
+#undef NMS_CHECKING
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+#undef _FORTIFY_SOURCE
+
+#define _GNU_SOURCE 1
diff --git c/libcody/config.m4 w/libcody/config.m4
new file mode 100644
index 00000000000..0603d3a6df4
--- /dev/null
+++ w/libcody/config.m4
@@ -0,0 +1,305 @@
+# Nathan's Common Config -*- mode:autoconf -*-
+# Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+AC_DEFUN([NMS_NOT_IN_SOURCE],
+[if test -e configure ; then
+AC_MSG_ERROR([Do not build in the source tree.  Reasons])
+fi])
+
+AC_DEFUN([NMS_TOOLS],
+[AC_MSG_CHECKING([tools])
+AC_ARG_WITH([tools],
+AS_HELP_STRING([--with-tools=DIR],[tool directory]),
+[if test "$withval" = "yes" ; then
+  AC_MSG_ERROR([tool location not specified])
+elif test "$withval" = "no" ; then
+  :
+elif ! test -d "${withval%/bin}" ; then
+  AC_MSG_ERROR([tools not present])
+else
+  tools=${withval%/bin}
+fi])
+AC_MSG_RESULT($tools)
+AC_MSG_CHECKING([tool binaries])
+AC_ARG_WITH([toolbin],
+AS_HELP_STRING([--with-toolbin=DIR],[tool bin directory]),
+[if test "$withval" = "yes" ; then
+  AC_MSG_ERROR([tool bin location not specified])
+elif test "$withval" = "no" ; then
+  :
+elif ! test "$withval" ; then
+  :
+elif ! test -d "${withval%/bin}/bin" ; then
+  AC_MSG_ERROR([tool bin not present])
+else
+  toolbin=${withval%/bin}/bin
+fi],
+[if test "$tools" && test -d $tools/bin ; then
+  toolbin=$tools/bin
+fi])
+AC_MSG_RESULT($toolbin)
+if test "$toolbin" ; then
+  PATH="$toolbin:$PATH"
+fi
+AC_SUBST(toolbin)])
+
+AC_DEFUN([NMS_TOOL_DIRS],
+[if test "$tools" && test -d "$tools/include" ; then
+  CXX+=" -I$tools/include"
+fi
+if test "$tools" && test -d "$tools/lib" ; then
+  toollib="$tools/lib"
+  if os=$(CXX -print-multi-os-directory 2>/dev/null) ; then
+    toollib+="/${os}"
+  fi
+  LDFLAGS+=" -L $toollib"
+  unset toollib
+fi])
+
+AC_DEFUN([NMS_NUM_CPUS],
+[AC_MSG_CHECKING([number of CPUs])
+case $build in
+     (*-*-darwin*) NUM_CPUS=$(sysctl -n hw.ncpu 2>/dev/null) ;;
+     (*) NUM_CPUS=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null) ;;
+esac
+test "$NUM_CPUS" = 0 && NUM_CPUS=
+AC_MSG_RESULT([${NUM_CPUS:-unknown}])
+test "$NUM_CPUS" = 1 && NUM_CPUS=
+AC_SUBST(NUM_CPUS)])
+
+AC_DEFUN([NMS_MAINTAINER_MODE],
+[AC_ARG_ENABLE([maintainer-mode],
+AS_HELP_STRING([--enable-maintainer-mode],
+[enable maintainer mode.  Add rules to rebuild configurey bits]),,
+[enable_maintainer_mode=no])
+case "$enable_maintainer_mode" in
+  ("yes") maintainer_mode=yes ;;
+  ("no") maintainer=no ;;
+  (*) AC_MSG_ERROR([unknown maintainer mode $enable_maintainer_mode]) ;;
+esac
+AC_MSG_CHECKING([maintainer-mode])
+AC_MSG_RESULT([$maintainer_mode])
+test "$maintainer_mode" = yes && MAINTAINER=yes
+AC_SUBST(MAINTAINER)])
+
+AC_DEFUN([NMS_CXX_COMPILER],
+[AC_ARG_WITH([compiler],
+AS_HELP_STRING([--with-compiler=NAME],[which compiler to use]),
+AC_MSG_CHECKING([C++ compiler])
+if test "$withval" = "yes" ; then
+  AC_MSG_ERROR([NAME not specified])
+elif test "$withval" = "no" ; then
+  AC_MSG_ERROR([Gonna need a C++ compiler!])
+else
+  CXX="${withval}"
+  AC_MSG_RESULT([$CXX])
+fi)])
+
+AC_DEFUN([NMS_CXX_11],
+[AC_MSG_CHECKING([whether $CXX is for C++11])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+[#if __cplusplus != 201103
+#error "C++11 is required"
+#endif
+]])],
+[AC_MSG_RESULT([yes])],
+[CXX_ORIG="$CXX"
+CXX+=" -std=c++11"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+[#if __cplusplus != 201103
+#error "C++11 is required"
+#endif
+]])],
+AC_MSG_RESULT([adding -std=c++11]),
+[CXX="$CXX_ORIG"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+[#if __cplusplus > 201103
+#error "C++11 is required"
+#endif
+]])],
+AC_MSG_RESULT([> C++11]),
+AC_MSG_RESULT([no])
+AC_MSG_ERROR([C++11 is required])]))
+unset CXX_ORIG])])
+
+AC_DEFUN([NMS_CXX_20],
+[AC_MSG_CHECKING([whether $CXX is for C++20])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+[#if __cplusplus <= 201703
+#error "C++20 is required"
+#endif
+]])],
+[AC_MSG_RESULT([yes])],
+[CXX+=" -std=c++20"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+[#if __cplusplus <= 201703
+#error "C++20 is required"
+#endif
+]])],
+AC_MSG_RESULT([adding -std=c++20]),
+AC_MSG_RESULT([no])
+AC_MSG_ERROR([C++20 is required])]))
+
+AC_MSG_CHECKING([whether C++20 support is sufficiently advanced])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <version>
+// There doesn't seem to be a feature macro for __VA_OPT__ :(
+#define VARIADIC(X,...) X __VA_OPT__((__VA_ARGS__))
+#define X(Y,Z) 1
+int ary[VARIADIC(X,Y,Z)];
+#if  __cpp_constinit < 201907
+#error "C++20 constinit required"
+cpp_constinit is __cpp_constinit
+#endif
+#if  __cpp_if_constexpr < 201606
+#error "C++20 constexpr required"
+cpp_constexpr is __cpp_if_constexpr
+#endif
+#if  __cpp_concepts < 201907
+#error "C++20 concepts required"
+cpp_concepts is __cpp_concepts
+#endif
+#if __cpp_structured_bindings < 201606
+#error "C++20 structured bindings required"
+cpp_structured_bindings is __cpp_structured_bindings
+#endif
+#if __cpp_lib_int_pow2 < 202002
+#error "std::has_single_bit required"
+cpp_lib_int_pow2 is __cpp_lib_int_pow2
+#endif
+]])],
+AC_MSG_RESULT([yes 🙂]),
+AC_MSG_RESULT([no 🙁])
+AC_MSG_ERROR([C++20 support is too immature]))])
+
+AC_DEFUN([NMS_ENABLE_EXCEPTIONS],
+[AC_ARG_ENABLE([exceptions],
+AS_HELP_STRING([--enable-exceptions],
+[enable exceptions & rtti]),,
+[enable_exceptions="no"])
+case "$enable_exceptions" in
+  ("yes") nms_exceptions=yes ;;
+  ("no") nms_exceptions=no ;;
+  (*) AC_MSG_ERROR([unknown exceptions $enable_exceptions]) ;;
+esac
+AC_MSG_CHECKING([exceptions])
+AC_MSG_RESULT([$nms_exceptions])
+if test "$nms_exceptions" != no ; then
+  EXCEPTIONS=yes
+fi
+AC_SUBST(EXCEPTIONS)])
+
+AC_DEFUN([NMS_LINK_OPT],
+[AC_MSG_CHECKING([adding $1 to linker])
+ORIG_LDFLAGS="$LDFLAGS"
+LDFLAGS+=" $1"
+AC_LINK_IFELSE([AC_LANG_PROGRAM([])],
+[AC_MSG_RESULT([ok])],
+[LDFLAGS="$ORIG_LDFLAGS"
+AC_MSG_RESULT([no])])
+unset ORIG_LDFLAGS])
+
+AC_DEFUN([NMS_BUGURL],
+[AC_MSG_CHECKING([bugurl])
+AC_ARG_WITH(bugurl,
+AS_HELP_STRING([--with-bugurl=URL],[where to report bugs]),
+if test "$withval" = "yes" ; then
+  AC_MSG_ERROR([URL not specified])
+elif test "$withval" = "no" ; then
+  BUGURL=""
+else
+  BUGURL="${withval}"
+fi,BUGURL="${PACKAGE_BUGREPORT}")
+AC_MSG_RESULT($BUGURL)
+AC_DEFINE_UNQUOTED(BUGURL,"$BUGURL",[Bug reporting location])])
+
+AC_DEFUN([NMS_DISTRIBUTION],
+[AC_ARG_ENABLE([distribution],
+AS_HELP_STRING([--enable-distribution],
+[enable distribution.  Inhibit components that prevent distribution]),,
+[enable_distribution="no"])
+case "$enable_distribution" in
+  ("yes") nms_distribution=yes ;;
+  ("no") nms_distribution=no ;;
+  (*) AC_MSG_ERROR([unknown distribution $enable_distribution]) ;;
+esac
+AC_MSG_CHECKING([distribution])
+AC_MSG_RESULT([$nms_distribution])])
+
+AC_DEFUN([NMS_ENABLE_CHECKING],
+[AC_ARG_ENABLE([checking],
+AS_HELP_STRING([--enable-checking],
+[enable run-time checking]),,
+[enable_checking="yes"])
+case "$enable_checking" in
+  (yes|all|yes,*) nms_checking=yes ;;
+  (no|none|release) nms_checking= ;;
+  (*) AC_MSG_ERROR([unknown check "$enable_checking"]) ;;
+esac
+AC_MSG_CHECKING([checking])
+AC_MSG_RESULT([${nms_checking:-no}])
+if test "$nms_checking" = yes ; then
+  AC_DEFINE_UNQUOTED([NMS_CHECKING], [0${nms_checking:+1}], [Enable checking])
+fi])
+
+AC_DEFUN([NMS_WITH_BINUTILS],
+[AC_MSG_CHECKING([binutils])
+AC_ARG_WITH(bfd,
+AS_HELP_STRING([--with-bfd=DIR], [location of libbfd]),
+if test "$withval" = "yes" ; then
+  AC_MSG_ERROR([DIR not specified])
+elif test "$withval" = "no" ; then
+  AC_MSG_RESULT(installed)
+else
+  AC_MSG_RESULT(${withval})
+  CPPFLAGS+=" -I${withval}/include"
+  LDFLAGS+=" -L${withval}/lib"
+fi,
+AC_MSG_RESULT(installed))])
+
+AC_DEFUN([NMS_ENABLE_BACKTRACE],
+[AC_REQUIRE([NMS_DISTRIBUTION])
+AC_ARG_ENABLE([backtrace],
+AS_HELP_STRING([--enable-backtrace],[provide backtrace on fatality.]),,
+[enable_backtrace="maybe"])
+if test "${enable_backtrace:-maybe}" != no ; then
+  AC_CHECK_HEADERS(execinfo.h)
+  AC_CHECK_FUNCS(backtrace)
+  if test "$nms_distribution" = no ; then
+    AC_DEFINE([HAVE_DECL_BASENAME], [1], [Needed for demangle.h])
+    # libiberty prevents distribution because of licensing
+    AC_CHECK_HEADERS([demangle.h libiberty/demangle.h],[break])
+    # libbfd prevents distribution because of licensing
+    AC_CHECK_HEADERS([bfd.h])
+    AC_SEARCH_LIBS([bfd_openr],[bfd],[LIBS+="-lz -liberty -ldl"],,[-lz -liberty -ldl])
+  fi
+  if test "$ac_cv_func_backtrace" = yes ; then
+    nms_backtrace=yes
+    ldbacktrace=-rdynamic
+    AC_DEFINE([NMS_BACKTRACE], [1], [Enable backtrace])
+  elif test "$enable_backtrace" = yes ; then
+    AC_MSG_ERROR([Backtrace unavailable])
+  fi
+  AC_SUBST([ldbacktrace])
+fi
+AC_MSG_CHECKING([backtrace])
+AC_MSG_RESULT([${nms_backtrace:-no}])])
+
+AC_DEFUN([NMS_CONFIG_FILES],
+[CONFIG_FILES="Makefile $1"
+SUBDIRS="$2"
+for generated in config.h.in configure ; do
+  if test $srcdir/configure.ac -nt $srcdir/$generated ; then
+    touch $srcdir/$generated
+  fi
+done
+for dir in . $SUBDIRS
+do
+  CONFIG_FILES+=" $dir/Makesub"
+  test -f ${srcdir}/$dir/tests/Makesub.in && CONFIG_FILES+=" $dir/tests/Makesub"
+done
+AC_CONFIG_FILES([$CONFIG_FILES])
+AC_SUBST(configure_args,[$ac_configure_args])
+AC_SUBST(SUBDIRS)
+AC_SUBST(CONFIG_FILES)])
diff --git c/libcody/configure w/libcody/configure
new file mode 100755
index 00000000000..458647afcc3
--- /dev/null
+++ w/libcody/configure
@@ -0,0 +1,4643 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69c for codylib 0.0.
+#
+# Report bugs to <github.com/urnathan/libcody>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2017, 2020 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null
+then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+IFS=$as_save_IFS
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+
+
+      if test "x$CONFIG_SHELL" != x
+then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno
+then :
+  printf "%s\n" "$0: This script requires a shell more modern than all"
+  printf "%s\n" "$0: the shells that I found on your system."
+  if test ${ZSH_VERSION+y} ; then
+    printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
+$0: github.com/urnathan/libcody about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='codylib'
+PACKAGE_TARNAME='codylib'
+PACKAGE_VERSION='0.0'
+PACKAGE_STRING='codylib 0.0'
+PACKAGE_BUGREPORT='github.com/urnathan/libcody'
+PACKAGE_URL=''
+
+ac_unique_file="cody.hh"
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+ALOY
+DOXYGEN
+AR
+EXCEPTIONS
+CONFIG_FILES
+SUBDIRS
+configure_args
+OBJEXT
+EXEEXT
+ac_ct_CXX
+CPPFLAGS
+LDFLAGS
+CXXFLAGS
+CXX
+MAINTAINER
+NUM_CPUS
+toolbin
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_tools
+with_toolbin
+enable_maintainer_mode
+with_compiler
+with_bugurl
+enable_checking
+enable_exceptions
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CXX
+CXXFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CCC'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures codylib 0.0 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/codylib]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of codylib 0.0:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-maintainer-mode
+                          enable maintainer mode. Add rules to rebuild
+                          configurey bits
+  --enable-checking       enable run-time checking
+  --enable-exceptions     enable exceptions & rtti
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-tools=DIR        tool directory
+  --with-toolbin=DIR      tool bin directory
+  --with-compiler=NAME    which compiler to use
+  --with-bugurl=URL       where to report bugs
+
+Some influential environment variables:
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <github.com/urnathan/libcody>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+codylib configure 0.0
+generated by GNU Autoconf 2.69c
+
+Copyright (C) 2020 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext
+then :
+  ac_retval=0
+else
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_cxx_try_link LINENO
+# -------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }
+then :
+  ac_retval=0
+else
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_link
+ac_configure_args_raw=
+for ac_arg
+do
+  case $ac_arg in
+  *\'*)
+    ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+  esac
+  as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+  *$as_nl*)
+    ac_safe_unquote= ;;
+  *)
+    ac_unsafe_z='|&;<>()$`\\"*?[ ''	' # This string ends in space, tab.
+    ac_unsafe_a="$ac_unsafe_z#~"
+    ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+    ac_configure_args_raw=`      printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by codylib $as_me 0.0, which was
+generated by GNU Autoconf 2.69c.  Invocation command line was
+
+  $ $0$ac_configure_args_raw
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    printf "%s\n" "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Sanitize IFS.
+  IFS=" ""	$as_nl"
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    printf "%s\n" "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    printf "%s\n" "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      printf "%s\n" "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      printf "%s\n" "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	printf "%s\n" "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      printf "%s\n" "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      printf "%s\n" "$as_me: caught signal $ac_signal"
+    printf "%s\n" "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+printf "%s\n" "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+printf "%s\n" "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+	    and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+# Nathan's Common Config -*- mode:autoconf -*-
+# Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_aux_dir=
+for ac_dir in build-aux "$srcdir"/build-aux
+do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+
+
+if test -e configure ; then
+as_fn_error $? "Do not build in the source tree.  Reasons" "$LINENO" 5
+fi
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+printf %s "checking build system type... " >&6; }
+if test ${ac_cv_build+y}
+then :
+  printf %s "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+printf %s "checking host system type... " >&6; }
+if test ${ac_cv_host+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking tools" >&5
+printf %s "checking tools... " >&6; }
+
+# Check whether --with-tools was given.
+if test ${with_tools+y}
+then :
+  withval=$with_tools; if test "$withval" = "yes" ; then
+  as_fn_error $? "tool location not specified" "$LINENO" 5
+elif test "$withval" = "no" ; then
+  :
+elif ! test -d "${withval%/bin}" ; then
+  as_fn_error $? "tools not present" "$LINENO" 5
+else
+  tools=${withval%/bin}
+fi
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tools" >&5
+printf "%s\n" "$tools" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking tool binaries" >&5
+printf %s "checking tool binaries... " >&6; }
+
+# Check whether --with-toolbin was given.
+if test ${with_toolbin+y}
+then :
+  withval=$with_toolbin; if test "$withval" = "yes" ; then
+  as_fn_error $? "tool bin location not specified" "$LINENO" 5
+elif test "$withval" = "no" ; then
+  :
+elif ! test "$withval" ; then
+  :
+elif ! test -d "${withval%/bin}/bin" ; then
+  as_fn_error $? "tool bin not present" "$LINENO" 5
+else
+  toolbin=${withval%/bin}/bin
+fi
+else
+  if test "$tools" && test -d $tools/bin ; then
+  toolbin=$tools/bin
+fi
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $toolbin" >&5
+printf "%s\n" "$toolbin" >&6; }
+if test "$toolbin" ; then
+  PATH="$toolbin:$PATH"
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking number of CPUs" >&5
+printf %s "checking number of CPUs... " >&6; }
+case $build in
+     (*-*-darwin*) NUM_CPUS=$(sysctl -n hw.ncpu 2>/dev/null) ;;
+     (*) NUM_CPUS=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null) ;;
+esac
+test "$NUM_CPUS" = 0 && NUM_CPUS=
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${NUM_CPUS:-unknown}" >&5
+printf "%s\n" "${NUM_CPUS:-unknown}" >&6; }
+test "$NUM_CPUS" = 1 && NUM_CPUS=
+
+# Check whether --enable-maintainer-mode was given.
+if test ${enable_maintainer_mode+y}
+then :
+  enableval=$enable_maintainer_mode;
+else
+  enable_maintainer_mode=no
+fi
+
+case "$enable_maintainer_mode" in
+  ("yes") maintainer_mode=yes ;;
+  ("no") maintainer=no ;;
+  (*) as_fn_error $? "unknown maintainer mode $enable_maintainer_mode" "$LINENO" 5 ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking maintainer-mode" >&5
+printf %s "checking maintainer-mode... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $maintainer_mode" >&5
+printf "%s\n" "$maintainer_mode" >&6; }
+test "$maintainer_mode" = yes && MAINTAINER=yes
+
+
+# Check whether --with-compiler was given.
+if test ${with_compiler+y}
+then :
+  withval=$with_compiler; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking C++ compiler" >&5
+printf %s "checking C++ compiler... " >&6; }
+if test "$withval" = "yes" ; then
+  as_fn_error $? "NAME not specified" "$LINENO" 5
+elif test "$withval" = "no" ; then
+  as_fn_error $? "Gonna need a C++ compiler!" "$LINENO" 5
+else
+  CXX="${withval}"
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+printf "%s\n" "$CXX" >&6; }
+fi
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CXX+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+printf "%s\n" "$CXX" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CXX+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+printf "%s\n" "$ac_ct_CXX" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
+printf %s "checking whether the C++ compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C++ compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
+printf %s "checking for C++ compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main (void)
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+  printf %s "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5
+printf %s "checking whether the compiler supports GNU C++... " >&6; }
+if test ${ac_cv_cxx_compiler_gnu+y}
+then :
+  printf %s "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+y}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+printf %s "checking whether $CXX accepts -g... " >&6; }
+if test ${ac_cv_prog_cxx_g+y}
+then :
+  printf %s "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+printf "%s\n" "$ac_cv_prog_cxx_g" >&6; }
+if test $ac_test_CXXFLAGS; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
+printf %s "checking for $CXX option to enable C++11 features... " >&6; }
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test ${ac_cv_prog_cxx_cxx11+y}
+then :
+  printf %s "(cached) " >&6
+else
+  ac_cv_prog_cxx_cxx11=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+// Does the compiler advertise C++ 2011 conformance?
+#if !defined __cplusplus || __cplusplus < 201103L
+# error "Compiler does not advertise C++11 conformance"
+#endif
+
+namespace cxx11test
+{
+  constexpr int get_val() { return 20; }
+
+  struct testinit
+  {
+    int i;
+    double d;
+  };
+
+  class delegate
+  {
+  public:
+    delegate(int n) : n(n) {}
+    delegate(): delegate(2354) {}
+
+    virtual int getval() { return this->n; };
+  protected:
+    int n;
+  };
+
+  class overridden : public delegate
+  {
+  public:
+    overridden(int n): delegate(n) {}
+    virtual int getval() override final { return this->n * 2; }
+  };
+
+  class nocopy
+  {
+  public:
+    nocopy(int i): i(i) {}
+    nocopy() = default;
+    nocopy(const nocopy&) = delete;
+    nocopy & operator=(const nocopy&) = delete;
+  private:
+    int i;
+  };
+
+  // for testing lambda expressions
+  template <typename Ret, typename Fn> Ret eval(Fn f, Ret v)
+  {
+    return f(v);
+  }
+
+  // for testing variadic templates and trailing return types
+  template <typename V> auto sum(V first) -> V
+  {
+    return first;
+  }
+  template <typename V, typename... Args> auto sum(V first, Args... rest) -> V
+  {
+    return first + sum(rest...);
+  }
+}
+
+// Does the compiler advertise C++98 conformance?
+#if !defined __cplusplus || __cplusplus < 199711L
+# error "Compiler does not advertise C++98 conformance"
+#endif
+
+// These inclusions are cheap compared to including any STL header, but will
+// reliably reject old compilers that lack the unsuffixed header files.
+#undef NDEBUG
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+// Namespaces, exceptions, and templates were all added after "C++ 2.0".
+using std::cout;
+using std::strcmp;
+
+namespace {
+
+void test_exception_syntax()
+{
+  try {
+    throw "test";
+  } catch (const char *s) {
+    // Extra parentheses suppress a warning when building autoconf itself,
+    // due to lint rules shared with more typical C programs.
+    assert (!(strcmp) (s, "test"));
+  }
+}
+
+template <typename T> struct test_template
+{
+  T const val;
+  explicit test_template(T t) : val(t) {}
+  template <typename U> T add(U u) { return static_cast<T>(u) + val; }
+};
+
+} // anonymous namespace
+
+int
+main (void)
+{
+
+{
+  // Test auto and decltype
+  auto a1 = 6538;
+  auto a2 = 48573953.4;
+  auto a3 = "String literal";
+
+  int total = 0;
+  for (auto i = a3; *i; ++i) { total += *i; }
+
+  decltype(a2) a4 = 34895.034;
+}
+{
+  // Test constexpr
+  short sa[cxx11test::get_val()] = { 0 };
+}
+{
+  // Test initializer lists
+  cxx11test::testinit il = { 4323, 435234.23544 };
+}
+{
+  // Test range-based for
+  int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3,
+                 14, 19, 17, 8, 6, 20, 16, 2, 11, 1};
+  for (auto &x : array) { x += 23; }
+}
+{
+  // Test lambda expressions
+  using cxx11test::eval;
+  assert (eval ([](int x) { return x*2; }, 21) == 42);
+  double d = 2.0;
+  assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0);
+  assert (d == 5.0);
+  assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0);
+  assert (d == 5.0);
+}
+{
+  // Test use of variadic templates
+  using cxx11test::sum;
+  auto a = sum(1);
+  auto b = sum(1, 2);
+  auto c = sum(1.0, 2.0, 3.0);
+}
+{
+  // Test constructor delegation
+  cxx11test::delegate d1;
+  cxx11test::delegate d2();
+  cxx11test::delegate d3(45);
+}
+{
+  // Test override and final
+  cxx11test::overridden o1(55464);
+}
+{
+  // Test nullptr
+  char *c = nullptr;
+}
+{
+  // Test template brackets
+  test_template<::test_template<int>> v(test_template<int>(12));
+}
+{
+  // Unicode literals
+  char const *utf8 = u8"UTF-8 string \u2500";
+  char16_t const *utf16 = u"UTF-8 string \u2500";
+  char32_t const *utf32 = U"UTF-32 string \u2500";
+}
+
+
+{
+  test_exception_syntax ();
+  test_template<double> tt (2.0);
+  assert (tt.add (4) == 6.0);
+  assert (true && !false);
+  cout << "ok\n";
+}
+
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu++11 -std=c++11 -std=gnu++0x -std=c++0x -qlanglvl=extended0x -AA
+do
+  CXX="$ac_save_CXX $ac_arg"
+  if ac_fn_cxx_try_compile "$LINENO"
+then :
+  ac_cv_prog_cxx_cxx11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cxx_cxx11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+
+fi
+# AC_CACHE_VAL
+ac_prog_cxx_stdcxx_options=
+case "x$ac_cv_prog_cxx_cxx11" in
+  x)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; } ;;
+  xno)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; } ;;
+  *)
+    ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx11"
+    CXX=$CXX$ac_prog_cxx_stdcxx_options
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } ;;
+esac
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test "x$ac_cv_prog_cxx_cxx11" != xno
+then :
+  ac_prog_cxx_stdcxx=cxx11
+		    ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
+		    ac_cv_prog_cxx_cxx98=$ac_cv_prog_cxx_cxx11
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
+printf %s "checking for $CXX option to enable C++98 features... " >&6; }
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test ${ac_cv_prog_cxx_cxx98+y}
+then :
+  printf %s "(cached) " >&6
+else
+  ac_cv_prog_cxx_cxx98=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+// Does the compiler advertise C++98 conformance?
+#if !defined __cplusplus || __cplusplus < 199711L
+# error "Compiler does not advertise C++98 conformance"
+#endif
+
+// These inclusions are cheap compared to including any STL header, but will
+// reliably reject old compilers that lack the unsuffixed header files.
+#undef NDEBUG
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+// Namespaces, exceptions, and templates were all added after "C++ 2.0".
+using std::cout;
+using std::strcmp;
+
+namespace {
+
+void test_exception_syntax()
+{
+  try {
+    throw "test";
+  } catch (const char *s) {
+    // Extra parentheses suppress a warning when building autoconf itself,
+    // due to lint rules shared with more typical C programs.
+    assert (!(strcmp) (s, "test"));
+  }
+}
+
+template <typename T> struct test_template
+{
+  T const val;
+  explicit test_template(T t) : val(t) {}
+  template <typename U> T add(U u) { return static_cast<T>(u) + val; }
+};
+
+} // anonymous namespace
+
+int
+main (void)
+{
+
+{
+  test_exception_syntax ();
+  test_template<double> tt (2.0);
+  assert (tt.add (4) == 6.0);
+  assert (true && !false);
+  cout << "ok\n";
+}
+
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA
+do
+  CXX="$ac_save_CXX $ac_arg"
+  if ac_fn_cxx_try_compile "$LINENO"
+then :
+  ac_cv_prog_cxx_cxx98=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cxx_cxx98" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+
+fi
+# AC_CACHE_VAL
+ac_prog_cxx_stdcxx_options=
+case "x$ac_cv_prog_cxx_cxx98" in
+  x)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; } ;;
+  xno)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; } ;;
+  *)
+    ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx98"
+    CXX=$CXX$ac_prog_cxx_stdcxx_options
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } ;;
+esac
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test "x$ac_cv_prog_cxx_cxx98" != xno
+then :
+  ac_prog_cxx_stdcxx=cxx98
+		        ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
+else
+  ac_prog_cxx_stdcxx=no
+		        ac_cv_prog_cxx_stdcxx=no
+fi
+
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX is for C++11" >&5
+printf %s "checking whether $CXX is for C++11... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#if __cplusplus != 201103
+#error "C++11 is required"
+#endif
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else
+  CXX_ORIG="$CXX"
+CXX+=" -std=c++11"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#if __cplusplus != 201103
+#error "C++11 is required"
+#endif
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: adding -std=c++11" >&5
+printf "%s\n" "adding -std=c++11" >&6; }
+else
+  CXX="$CXX_ORIG"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#if __cplusplus > 201103
+#error "C++11 is required"
+#endif
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: > C++11" >&5
+printf "%s\n" "> C++11" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+as_fn_error $? "C++11 is required" "$LINENO" 5
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+unset CXX_ORIG
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+if test "$tools" && test -d "$tools/include" ; then
+  CXX+=" -I$tools/include"
+fi
+if test "$tools" && test -d "$tools/lib" ; then
+  toollib="$tools/lib"
+  if os=$(CXX -print-multi-os-directory 2>/dev/null) ; then
+    toollib+="/${os}"
+  fi
+  LDFLAGS+=" -L $toollib"
+  unset toollib
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking adding -Wl,--no-undefined to linker" >&5
+printf %s "checking adding -Wl,--no-undefined to linker... " >&6; }
+ORIG_LDFLAGS="$LDFLAGS"
+LDFLAGS+=" -Wl,--no-undefined"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+printf "%s\n" "ok" >&6; }
+else
+  LDFLAGS="$ORIG_LDFLAGS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+unset ORIG_LDFLAGS
+CONFIG_FILES="Makefile gdbinit dox.cfg"
+SUBDIRS=""
+for generated in config.h.in configure ; do
+  if test $srcdir/configure.ac -nt $srcdir/$generated ; then
+    touch $srcdir/$generated
+  fi
+done
+for dir in . $SUBDIRS
+do
+  CONFIG_FILES+=" $dir/Makesub"
+  test -f ${srcdir}/$dir/tests/Makesub.in && CONFIG_FILES+=" $dir/tests/Makesub"
+done
+ac_config_files="$ac_config_files $CONFIG_FILES"
+
+configure_args=$ac_configure_args
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking bugurl" >&5
+printf %s "checking bugurl... " >&6; }
+
+# Check whether --with-bugurl was given.
+if test ${with_bugurl+y}
+then :
+  withval=$with_bugurl; if test "$withval" = "yes" ; then
+  as_fn_error $? "URL not specified" "$LINENO" 5
+elif test "$withval" = "no" ; then
+  BUGURL=""
+else
+  BUGURL="${withval}"
+fi
+else
+  BUGURL="${PACKAGE_BUGREPORT}"
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BUGURL" >&5
+printf "%s\n" "$BUGURL" >&6; }
+
+printf "%s\n" "#define BUGURL \"$BUGURL\"" >>confdefs.h
+
+# Check whether --enable-checking was given.
+if test ${enable_checking+y}
+then :
+  enableval=$enable_checking;
+else
+  enable_checking="yes"
+fi
+
+case "$enable_checking" in
+  (yes|all|yes,*) nms_checking=yes ;;
+  (no|none|release) nms_checking= ;;
+  (*) as_fn_error $? "unknown check \"$enable_checking\"" "$LINENO" 5 ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking checking" >&5
+printf %s "checking checking... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${nms_checking:-no}" >&5
+printf "%s\n" "${nms_checking:-no}" >&6; }
+if test "$nms_checking" = yes ; then
+
+printf "%s\n" "#define NMS_CHECKING 0${nms_checking:+1}" >>confdefs.h
+
+fi
+# Check whether --enable-exceptions was given.
+if test ${enable_exceptions+y}
+then :
+  enableval=$enable_exceptions;
+else
+  enable_exceptions="no"
+fi
+
+case "$enable_exceptions" in
+  ("yes") nms_exceptions=yes ;;
+  ("no") nms_exceptions=no ;;
+  (*) as_fn_error $? "unknown exceptions $enable_exceptions" "$LINENO" 5 ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking exceptions" >&5
+printf %s "checking exceptions... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $nms_exceptions" >&5
+printf "%s\n" "$nms_exceptions" >&6; }
+if test "$nms_exceptions" != no ; then
+  EXCEPTIONS=yes
+fi
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AR+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="${ac_tool_prefix}ar"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+printf "%s\n" "$AR" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+  ac_ct_AR=$AR
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_AR+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="ar"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+printf "%s\n" "$ac_ct_AR" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_AR" = x; then
+    AR=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+else
+  AR="$ac_cv_prog_AR"
+fi
+
+# Extract the first word of "doxygen", so it can be a program name with args.
+set dummy doxygen; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_DOXYGEN+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$DOXYGEN"; then
+  ac_cv_prog_DOXYGEN="$DOXYGEN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DOXYGEN="doxygen"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_prog_DOXYGEN" && ac_cv_prog_DOXYGEN=": NOTdoxygen"
+fi
+fi
+DOXYGEN=$ac_cv_prog_DOXYGEN
+if test -n "$DOXYGEN"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DOXYGEN" >&5
+printf "%s\n" "$DOXYGEN" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+# Extract the first word of "aloy", so it can be a program name with args.
+set dummy aloy; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ALOY+y}
+then :
+  printf %s "(cached) " >&6
+else
+  if test -n "$ALOY"; then
+  ac_cv_prog_ALOY="$ALOY" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ALOY="aloy"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_prog_ALOY" && ac_cv_prog_ALOY=": Joust testsuite missing"
+fi
+fi
+ALOY=$ac_cv_prog_ALOY
+if test -n "$ALOY"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ALOY" >&5
+printf "%s\n" "$ALOY" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by codylib $as_me 0.0, which was
+generated by GNU Autoconf 2.69c.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <github.com/urnathan/libcody>."
+
+_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config='$ac_cs_config_escaped'
+ac_cs_version="\\
+codylib config.status 0.0
+configured by $0, generated by GNU Autoconf 2.69c,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2020 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    printf "%s\n" "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    printf "%s\n" "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    printf "%s\n" "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  printf "%s\n" "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "$CONFIG_FILES") CONFIG_FILES="$CONFIG_FILES $CONFIG_FILES" ;;
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+  test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
diff --git c/libcody/configure.ac w/libcody/configure.ac
new file mode 100644
index 00000000000..31f041e6679
--- /dev/null
+++ w/libcody/configure.ac
@@ -0,0 +1,37 @@
+# CODYlib  -*- mode:autoconf -*-
+# Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+AC_INIT([codylib],[0.0],[github.com/urnathan/libcody])
+AC_CONFIG_SRCDIR(cody.hh)
+m4_include(config.m4)
+
+AC_CONFIG_AUX_DIR(build-aux)
+AC_SUBST(PACKAGE_VERSION)
+
+NMS_NOT_IN_SOURCE
+AC_CANONICAL_HOST
+
+NMS_TOOLS
+NMS_NUM_CPUS
+NMS_MAINTAINER_MODE
+NMS_CXX_COMPILER
+AC_LANG(C++)
+AC_PROG_CXX
+NMS_CXX_11
+NMS_TOOL_DIRS
+NMS_LINK_OPT([-Wl,--no-undefined])
+NMS_CONFIG_FILES([gdbinit dox.cfg])
+
+NMS_BUGURL
+NMS_ENABLE_CHECKING
+NMS_ENABLE_EXCEPTIONS
+
+AC_CONFIG_HEADERS([config.h])
+AC_CHECK_TOOL([AR],[ar])
+AC_CHECK_PROG([DOXYGEN],[doxygen],[doxygen],[: NOTdoxygen])
+AC_CHECK_PROG([ALOY],[aloy],[aloy],[: Joust testsuite missing])
+AH_VERBATIM([_GNU_SOURCE],[#define _GNU_SOURCE 1])
+AH_VERBATIM([_FORTIFY_SOURCE],[#undef _FORTIFY_SOURCE])
+
+AC_OUTPUT
diff --git c/libcody/dox.cfg.in w/libcody/dox.cfg.in
new file mode 100644
index 00000000000..1a39b2aa6ee
--- /dev/null
+++ w/libcody/dox.cfg.in
@@ -0,0 +1,2478 @@
+# Doxyfile 1.8.15
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "libcody"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "A Compiler/Build System Interface"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = dox
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      = no_extension=C++ \
+		       	 hh=C++
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = @srcdir@
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS          = *.cc *.hh
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         =
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: \makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = \makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git c/libcody/fatal.cc w/libcody/fatal.cc
new file mode 100644
index 00000000000..b35094e6b19
--- /dev/null
+++ w/libcody/fatal.cc
@@ -0,0 +1,78 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2019-2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+// C
+#include <csignal>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+namespace Cody {
+
+#if NMS_CHECKING
+void (AssertFailed) (Location loc) noexcept
+{
+  (HCF) ("assertion failed", loc);
+}
+void (Unreachable) (Location loc) noexcept
+{
+  (HCF) ("unreachable reached", loc);
+}
+#endif
+
+void (HCF) (char const *msg
+#if NMS_CHECKING
+	  , Location const loc
+#endif
+	  ) noexcept
+{ // HCF - you goofed!
+  __asm__ volatile ("nop");  // HCF - you goofed!
+
+#if !NMS_CHECKING
+  constexpr Location loc (nullptr, 0);
+#endif
+
+  fprintf (stderr, "CODYlib: %s", msg ? msg : "internal error");
+  if (char const *file = loc.File ())
+    {
+      char const *src = SRCDIR;
+
+      if (src[0])
+	{
+	  size_t l = strlen (src);
+
+	  if (!strncmp (src, file, l) && file[l] == '/')
+	    file += l + 1;
+	}
+      fprintf (stderr, " at %s:%u", file, loc.Line ());
+    }
+  fprintf (stderr, "\n");
+  raise (SIGABRT);
+  exit (2);
+}
+
+void BuildNote (FILE *stream) noexcept
+{
+  fprintf (stream, "Version %s.\n", PACKAGE_NAME " " PACKAGE_VERSION);
+  fprintf (stream, "Report bugs to %s.\n", BUGURL[0] ? BUGURL : "you");
+  if (PACKAGE_URL[0])
+    fprintf (stream, "See %s for more information.\n", PACKAGE_URL);
+  if (REVISION[0])
+    fprintf (stream, "Source %s.\n", REVISION);
+
+  fprintf (stream, "Build is %s & %s.\n",
+#if !NMS_CHECKING
+	   "un"
+#endif
+	   "checked",
+#if !__OPTIMIZE__
+	   "un"
+#endif
+	   "optimized");
+}
+
+}
diff --git c/libcody/gdbinit.in w/libcody/gdbinit.in
new file mode 100644
index 00000000000..b81b7d83981
--- /dev/null
+++ w/libcody/gdbinit.in
@@ -0,0 +1,11 @@
+# CODYlib  -*- mode:autoconf -*-
+# Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+dir @srcdir@
+# some default breakpoints, to catch the fatal exit
+set breakpoint pending on
+break HCF
+set complaints 0
+break exit
+set breakpoint pending off
diff --git c/libcody/internal.hh w/libcody/internal.hh
new file mode 100644
index 00000000000..d744b564cda
--- /dev/null
+++ w/libcody/internal.hh
@@ -0,0 +1,125 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+#include "cody.hh"
+
+// C++
+#if __GNUC__ >= 10
+#define CODY_LOC_BUILTIN 1
+#elif !defined (__has_include)
+#elif __has_include (<source_location>)
+#include <source_location>
+#define CODY_LOC_SOURCE 1
+#endif
+// C
+#include <cstdio>
+
+namespace Cody {
+
+// Location is needed regardless of checking, to make the fatal
+// handler simpler
+class Location
+{
+protected:
+  char const *file;
+  unsigned line;
+
+public:
+  constexpr Location (char const *file_
+#if CODY_LOC_BUILTIN
+		      = __builtin_FILE ()
+#elif !CODY_LOC_SOURCE
+		      = nullptr
+#endif
+		      , unsigned line_
+#if CODY_LOC_BUILTIN
+		      = __builtin_LINE ()
+#elif !CODY_LOC_SOURCE
+		      = 0
+#endif
+		      )
+    :file (file_), line (line_)
+  {
+  }
+
+#if !CODY_LOC_BUILTIN && CODY_LOC_SOURCE
+  constexpr Location (source_location loc = source_location::current ())
+    : Location (loc.file (), loc.line ())
+  {
+  }
+#endif
+
+public:
+  constexpr char const *File () const
+  {
+    return file;
+  }
+  constexpr unsigned Line () const
+  {
+    return line;
+  }
+};
+
+void HCF [[noreturn]]
+(
+ char const *msg
+#if NMS_CHECKING
+ , Location const = Location ()
+#if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE
+#define HCF(M) HCF ((M), Cody::Location (__FILE__, __LINE__))
+#endif
+#endif
+ ) noexcept;
+
+#if NMS_CHECKING
+void AssertFailed [[noreturn]] (Location loc = Location ()) noexcept;
+void Unreachable [[noreturn]] (Location loc = Location ()) noexcept;
+#if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE
+#define AssertFailed() AssertFailed (Cody::Location (__FILE__, __LINE__))
+#define Unreachable() Unreachable (Cody::Location (__FILE__, __LINE__))
+#endif
+
+// Do we have __VA_OPT__, alas no specific feature macro for it :(
+// From stack overflow
+// https://stackoverflow.com/questions/48045470/portably-detect-va-opt-support
+// Relies on having variadic macros, but they're a C++11 thing, so
+// we're good
+#define HAVE_ARG_3(a,b,c,...) c
+#define HAVE_VA_OPT_(...) HAVE_ARG_3(__VA_OPT__(,),true,false,)
+#define HAVE_VA_OPT HAVE_VA_OPT_(?)
+
+// Oh, for lazily evaluated function parameters
+#if HAVE_VA_OPT
+// Assert is variadic, so you can write Assert (TPL<A,B>(C)) without
+// extraneous parens.  I don't think we need that though.
+#define Assert(EXPR, ...)						\
+  (__builtin_expect (bool (EXPR __VA_OPT__ (, __VA_ARGS__)), true)	\
+   ? (void)0 : AssertFailed ())
+#else
+// If you don't have the GNU ,##__VA_ARGS__ pasting extension, we'll
+// need another fallback
+#define Assert(EXPR, ...)						\
+  (__builtin_expect (bool (EXPR, ##__VA_ARGS__), true)			\
+   ? (void)0 : AssertFailed ())
+#endif
+#else
+// Not asserting, use EXPR in an unevaluated context
+#if  HAVE_VA_OPT
+#define Assert(EXPR, ...)					\
+  ((void)sizeof (bool (EXPR __VA_OPT__ (, __VA_ARGS__))), (void)0)
+#else
+#define Assert(EXPR, ...)					\
+  ((void)sizeof (bool (EXPR, ##__VA_ARGS__)), (void)0)
+#endif
+
+inline void Unreachable () noexcept
+{
+  __builtin_unreachable ();
+}
+#endif
+
+// FIXME: This should be user visible in some way
+void BuildNote (FILE *stream) noexcept;
+
+}
diff --git c/libcody/netclient.cc w/libcody/netclient.cc
new file mode 100644
index 00000000000..b09dcefff39
--- /dev/null
+++ w/libcody/netclient.cc
@@ -0,0 +1,136 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+
+#if CODY_NETWORKING
+// C
+#include <cerrno>
+#include <cstring>
+// OS
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+// Client-side networking helpers
+
+namespace Cody {
+
+int OpenSocket (char const **e, sockaddr const *addr, socklen_t len)
+{
+  char const *errstr = nullptr;
+
+  int fd = socket (addr->sa_family, SOCK_STREAM, 0);
+  if (fd < 0)
+    {
+      errstr = "creating socket";
+
+    fail:;
+      int err = errno;
+      if (e)
+	*e = errstr;
+      if (fd >= 0)
+	close (fd);
+      errno = err;
+      return -1;
+    }
+
+  if (connect (fd, addr, len) < 0)
+    {
+      errstr = "connecting socket";
+      goto fail;
+    }
+
+  return fd;
+}
+
+int OpenLocal (char const **e, char const *name)
+{
+  sockaddr_un addr;
+  size_t len = strlen (name);
+
+  if (len >= sizeof (addr.sun_path))
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+
+  memset (&addr, 0, offsetof (sockaddr_un, sun_path));
+  addr.sun_family = AF_UNIX;
+  memcpy (addr.sun_path, name, len + 1);
+  return OpenSocket (e, (sockaddr *)&addr, sizeof (addr));
+}
+
+int OpenInet6 (char const **e, char const *name, int port)
+{
+  addrinfo *addrs = nullptr;
+  int fd = -1;
+  char const *errstr = nullptr;
+
+  fd = socket (AF_INET6, SOCK_STREAM, 0);
+  if (fd < 0)
+    {
+      errstr = "socket";
+
+    fail:;
+      int err = errno;
+      if (e)
+	*e = errstr;
+      if (fd >= 0)
+	close (fd);
+      if (addrs)
+	freeaddrinfo (addrs);
+      errno = err;
+      return -1;
+    }
+
+  addrinfo hints;
+  hints.ai_flags = AI_NUMERICSERV;
+  hints.ai_family = AF_INET6;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+  hints.ai_addrlen = 0;
+  hints.ai_addr = nullptr;
+  hints.ai_canonname = nullptr;
+  hints.ai_next = nullptr;
+
+  /* getaddrinfo requires a port number, but is quite happy to accept
+     invalid ones.  So don't rely on it.  */
+  if (int err = getaddrinfo (name, "0", &hints, &addrs))
+    {
+      errstr = gai_strerror (err);
+      // What's the best errno to set?
+      errno = 0;
+      goto fail;
+    }
+
+  sockaddr_in6 addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sin6_family = AF_INET6;
+
+  for (struct addrinfo *next = addrs; next; next = next->ai_next)
+    if (next->ai_family == AF_INET6
+	&& next->ai_socktype == SOCK_STREAM)
+      {
+	sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr;
+	in6->sin6_port = htons (port);
+	if (ntohs (in6->sin6_port) != port)
+	  errno = EINVAL;
+	else if (!connect (fd, next->ai_addr, next->ai_addrlen))
+	  goto done;
+      }
+  errstr = "connecting";
+  goto fail;
+
+ done:;
+  freeaddrinfo (addrs);
+
+  return fd;
+}
+
+}
+
+#endif
diff --git c/libcody/netserver.cc w/libcody/netserver.cc
new file mode 100644
index 00000000000..5bfc7989eb4
--- /dev/null
+++ w/libcody/netserver.cc
@@ -0,0 +1,149 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+#if CODY_NETWORKING
+// C
+#include <cerrno>
+#include <cstring>
+// OS
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+// Server-side networking helpers
+
+namespace Cody {
+
+int ListenSocket (char const **e, sockaddr const *addr, socklen_t len,
+		  unsigned backlog)
+{
+  char const *errstr = nullptr;
+
+  int fd = socket (addr->sa_family, SOCK_STREAM, 0);
+  if (fd < 0)
+    {
+      errstr = "creating socket";
+
+    fail:;
+      int err = errno;
+      if (e)
+	*e = errstr;
+      if (fd >= 0)
+	close (fd);
+      errno = err;
+      return -1;
+    }
+
+  if (bind (fd, addr, len) < 0)
+    {
+      errstr = "binding socket";
+      goto fail;
+    }
+
+  if (listen (fd, backlog ? backlog : 17) < 0)
+    {
+      errstr = "listening socket";
+      goto fail;
+    }
+
+  return fd;
+}
+
+int ListenLocal (char const **e, char const *name, unsigned backlog)
+{
+  sockaddr_un addr;
+  size_t len = strlen (name);
+
+  if (len >= sizeof (addr.sun_path))
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+
+  memset (&addr, 0, offsetof (sockaddr_un, sun_path));
+  addr.sun_family = AF_UNIX;
+  memcpy (addr.sun_path, name, len + 1);
+
+  return ListenSocket (e, (sockaddr *)&addr, sizeof (addr), backlog);
+}
+
+int ListenInet6 (char const **e, char const *name, int port, unsigned backlog)
+{
+  addrinfo *addrs = nullptr;
+  int fd = -1;
+  char const *errstr = nullptr;
+
+  fd = socket (AF_INET6, SOCK_STREAM, 0);
+  if (fd < 0)
+    {
+      errstr = "creating socket";
+
+    fail:;
+      int err = errno;
+      if (e)
+	*e = errstr;
+      if (fd >= 0)
+	close (fd);
+      if (addrs)
+	freeaddrinfo (addrs);
+      errno = err;
+      return -1;
+    }
+
+  addrinfo hints;
+  hints.ai_flags = AI_NUMERICSERV;
+  hints.ai_family = AF_INET6;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+  hints.ai_addrlen = 0;
+  hints.ai_addr = nullptr;
+  hints.ai_canonname = nullptr;
+  hints.ai_next = nullptr;
+
+  /* getaddrinfo requires a port number, but is quite happy to accept
+     invalid ones.  So don't rely on it.  */
+  if (int err = getaddrinfo (name, "0", &hints, &addrs))
+    {
+      errstr = gai_strerror (err);
+      // What's the best errno to set?
+      errno = 0;
+      goto fail;
+    }
+
+  sockaddr_in6 addr;
+  memset (&addr, 0, sizeof (addr));
+  addr.sin6_family = AF_INET6;
+
+  for (struct addrinfo *next = addrs; next; next = next->ai_next)
+    if (next->ai_family == AF_INET6
+	&& next->ai_socktype == SOCK_STREAM)
+      {
+	sockaddr_in6 *in6 = (sockaddr_in6 *)next->ai_addr;
+	in6->sin6_port = htons (port);
+	if (ntohs (in6->sin6_port) != port)
+	  errno = EINVAL;
+	else if (!bind (fd, next->ai_addr, next->ai_addrlen))
+	  goto listen;
+      }
+
+  errstr = "binding socket";
+  goto fail;
+
+ listen:;
+  freeaddrinfo (addrs);
+
+  if (listen (fd, backlog ? backlog : 17) < 0)
+    {
+      errstr = "listening socket";
+      goto fail;
+    }
+
+  return fd;
+}
+
+}
+#endif
diff --git c/libcody/packet.cc w/libcody/packet.cc
new file mode 100644
index 00000000000..65dc8f43155
--- /dev/null
+++ w/libcody/packet.cc
@@ -0,0 +1,50 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+
+namespace Cody {
+
+void Packet::Destroy ()
+{
+  switch (cat)
+    {
+    case STRING:
+      // Silly scope destructor name rules
+      using S = std::string;
+      string.~S ();
+      break;
+
+    case VECTOR:
+      using V = std::vector<std::string>;
+      vector.~V ();
+      break;
+
+    default:;
+    }
+}
+
+void Packet::Create (Packet &&t)
+{
+  cat = t.cat;
+  code = t.code;
+  request = t.request;
+  switch (cat)
+    {
+    case STRING:
+      new (&string) std::string (std::move (t.string));
+      break;
+
+    case VECTOR:
+      new (&vector) std::vector<std::string> (std::move (t.vector));
+      break;
+
+    default:
+      integer = t.integer;
+      break;
+    }
+}
+
+}
diff --git c/libcody/resolver.cc w/libcody/resolver.cc
new file mode 100644
index 00000000000..6100ece76a6
--- /dev/null
+++ w/libcody/resolver.cc
@@ -0,0 +1,211 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+// OS
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined (__unix__) || defined (__MACH__)
+// Autoconf test?
+#define HAVE_FSTATAT 1
+#else
+#define HAVE_FSTATAT 0
+#endif
+
+// Resolver code
+
+#if __windows__
+inline bool IsDirSep (char c)
+{
+  return c == '/' || c == '\\';
+}
+inline bool IsAbsPath (char const *str)
+{
+  // IIRC windows has the concept of per-drive current directories,
+  // which make drive-using paths confusing.  Let's not get into that.
+  return IsDirSep (str)
+    || (((str[0] >= 'A' && str[1] <= 'Z')
+	 || (str[0] >= 'a' && str[1] <= 'z'))&& str[1] == ':');
+}
+#else
+inline bool IsDirSep (char c)
+{
+  return c == '/';
+}
+inline bool IsAbsPath (char const *str)
+{
+  return IsDirSep (str[0]);
+}
+#endif
+
+constexpr char DIR_SEPARATOR = '/';
+
+constexpr char DOT_REPLACE = ','; // Replace . directories
+constexpr char COLON_REPLACE = '-'; // Replace : (partition char)
+constexpr char const REPO_DIR[] = "cmi.cache";
+
+namespace Cody {
+
+Resolver::~Resolver ()
+{
+}
+
+char const *Resolver::GetCMISuffix ()
+{
+  return "cmi";
+}
+
+std::string Resolver::GetCMIName (std::string const &module)
+{
+  std::string result;
+
+  result.reserve (module.size () + 8);
+  bool is_header = false;
+  bool is_abs = false;
+
+  if (IsAbsPath (module.c_str ()))
+    is_header = is_abs = true;
+  else if (module.front () == '.' && IsDirSep (module.c_str ()[1]))
+    is_header = true;
+
+  if (is_abs)
+    {
+      result.push_back ('.');
+      result.append (module);
+    }
+  else
+    result = std::move (module);
+
+  if (is_header)
+    {
+      if (!is_abs)
+	result[0] = DOT_REPLACE;
+
+      /* Map .. to DOT_REPLACE, DOT_REPLACE.  */
+      for (size_t ix = 1; ; ix++)
+	{
+	  ix = result.find ('.', ix);
+	  if (ix == result.npos)
+	    break;
+	  if (ix + 2 > result.size ())
+	    break;
+	  if (result[ix + 1] != '.')
+	    continue;
+	  if (!IsDirSep (result[ix - 1]))
+	    continue;
+	  if (!IsDirSep (result[ix + 2]))
+	    continue;
+	  result[ix] = DOT_REPLACE;
+	  result[ix + 1] = DOT_REPLACE;
+	}
+    }
+  else if (COLON_REPLACE != ':')
+    {
+      // There can only be one colon in a module name
+      auto colon = result.find (':');
+      if (colon != result.npos)
+	result[colon] = COLON_REPLACE;
+    }
+
+  if (char const *suffix = GetCMISuffix ())
+    {
+      result.push_back ('.');
+      result.append (suffix);
+    }
+
+  return result;
+}
+
+void Resolver::WaitUntilReady (Server *)
+{
+}
+
+Resolver *Resolver::ConnectRequest (Server *s, unsigned version,
+			       std::string &, std::string &)
+{
+  if (version > Version)
+    s->ErrorResponse ("version mismatch");
+  else
+    s->ConnectResponse ("default");
+
+  return this;
+}
+
+int Resolver::ModuleRepoRequest (Server *s)
+{
+  s->PathnameResponse (REPO_DIR);
+  return 0;
+}
+
+int Resolver::ModuleExportRequest (Server *s, std::string &module)
+{
+  auto cmi = GetCMIName (module);
+  s->PathnameResponse (cmi);
+  return 0;
+}
+
+int Resolver::ModuleImportRequest (Server *s, std::string &module)
+{
+  auto cmi = GetCMIName (module);
+  s->PathnameResponse (cmi);
+  return 0;
+}
+
+int Resolver::ModuleCompiledRequest (Server *s, std::string &)
+{
+  s->OKResponse ();
+  return 0;
+}
+
+int Resolver::IncludeTranslateRequest (Server *s, std::string &include)
+{
+  bool xlate = false;
+
+  // This is not the most efficient
+  auto cmi = GetCMIName (include);
+  struct stat statbuf;
+
+#if HAVE_FSTATAT
+  int fd_dir = open (REPO_DIR, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+  if (fd_dir >= 0
+      && fstatat (fd_dir, cmi.c_str (), &statbuf, 0) == 0
+      && S_ISREG (statbuf.st_mode))
+    // Sadly can't easily check if this proces has read access,
+    // except by trying to open it.
+    xlate = true;
+  if (fd_dir >= 0)
+    close (fd_dir);
+#else
+  std::string append = REPO_DIR;
+  append.push_back (DIR_SEPARATOR);
+  append.append (cmi);
+  if (stat (append.c_str (), &statbuf) == 0
+      || S_ISREG (statbuf.st_mode))
+    xlate = true;
+#endif
+
+  if (xlate)
+    s->PathnameResponse (cmi);
+  else
+    s->BoolResponse (false);
+
+  return 0;
+}
+
+int Resolver::InvokeSubProcessRequest (Server *s, std::vector<std::string> &)
+{
+  s->ErrorResponse ("unimplemented");
+  return 0;
+}
+
+void Resolver::ErrorResponse (Server *server, std::string &&msg)
+{
+  server->ErrorResponse (msg);
+}
+
+}
diff --git c/libcody/server.cc w/libcody/server.cc
new file mode 100644
index 00000000000..19a6e9f842f
--- /dev/null
+++ w/libcody/server.cc
@@ -0,0 +1,271 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Cody
+#include "internal.hh"
+// C++
+#include <tuple>
+// C
+#include <cerrno>
+#include <cstring>
+
+// Server code
+
+namespace Cody {
+
+// These do not need to be members
+static Resolver *ConnectRequest (Server *, Resolver *,
+				 std::vector<std::string> &words);
+static int ModuleRepoRequest (Server *, Resolver *,
+			      std::vector<std::string> &words);
+static int ModuleExportRequest (Server *, Resolver *,
+				std::vector<std::string> &words);
+static int ModuleImportRequest (Server *, Resolver *,
+				std::vector<std::string> &words);
+static int ModuleCompiledRequest (Server *, Resolver *,
+				  std::vector<std::string> &words);
+static int IncludeTranslateRequest (Server *, Resolver *,
+				     std::vector<std::string> &words);
+static int InvokeSubProcessRequest (Server *, Resolver *,
+				     std::vector<std::string> &words);
+
+namespace {
+using RequestFn = int (Server *, Resolver *, std::vector<std::string> &);
+using RequestPair = std::tuple<char const *, RequestFn *>;
+static RequestPair
+  const requestTable[Detail::RC_HWM] =
+  {
+    // Same order as enum RequestCode
+    RequestPair {u8"HELLO", nullptr},
+    RequestPair {u8"MODULE-REPO", ModuleRepoRequest},
+    RequestPair {u8"MODULE-EXPORT", ModuleExportRequest},
+    RequestPair {u8"MODULE-IMPORT", ModuleImportRequest},
+    RequestPair {u8"MODULE-COMPILED", ModuleCompiledRequest},
+    RequestPair {u8"INCLUDE-TRANSLATE", IncludeTranslateRequest},
+    RequestPair {u8"INVOKE", InvokeSubProcessRequest},
+  };
+}
+
+Server::Server (Resolver *r)
+  : resolver (r), direction (READING)
+{
+  PrepareToRead ();
+}
+
+Server::Server (Server &&src)
+  : write (std::move (src.write)),
+    read (std::move (src.read)),
+    resolver (src.resolver),
+    is_connected (src.is_connected),
+    direction (src.direction)
+{
+  fd.from = src.fd.from;
+  fd.to = src.fd.to;
+}
+
+Server::~Server ()
+{
+}
+
+Server &Server::operator= (Server &&src)
+{
+  write = std::move (src.write);
+  read = std::move (src.read);
+  resolver = src.resolver;
+  is_connected = src.is_connected;
+  direction = src.direction;
+  fd.from = src.fd.from;
+  fd.to = src.fd.to;
+
+  return *this;
+}
+
+void Server::DirectProcess (Detail::MessageBuffer &from,
+			    Detail::MessageBuffer &to)
+{
+  read.PrepareToRead ();
+  std::swap (read, from);
+  ProcessRequests ();
+  resolver->WaitUntilReady (this);
+  write.PrepareToWrite ();
+  std::swap (to, write);
+}
+
+void Server::ProcessRequests (void)
+{
+  std::vector<std::string> words;
+
+  direction = PROCESSING;
+  while (!read.IsAtEnd ())
+    {
+      int err = 0;
+      unsigned ix = Detail::RC_HWM;
+      if (!read.Lex (words))
+	{
+	  Assert (!words.empty ());
+	  while (ix--)
+	    {
+	      if (words[0] != std::get<0> (requestTable[ix]))
+		continue; // not this one
+
+	      if (ix == Detail::RC_CONNECT)
+		{
+		  // CONNECT
+		  if (IsConnected ())
+		    err = -1;
+		  else if (auto *r = ConnectRequest (this, resolver, words))
+		    resolver = r;
+		  else
+		    err = -1;
+		}
+	      else
+		{
+		  if (!IsConnected ())
+		    err = -1;
+		  else if (int res = (std::get<1> (requestTable[ix])
+				      (this, resolver, words)))
+		    err = res;
+		}
+	      break;
+	    }
+	}
+
+      if (err || ix >= Detail::RC_HWM)
+	{
+	  // Some kind of error
+	  std::string msg;
+
+	  if (err > 0)
+	    msg = u8"error processing '";
+	  else if (ix >= Detail::RC_HWM)
+	    msg = u8"unrecognized '";
+	  else if (IsConnected () && ix == Detail::RC_CONNECT)
+	    msg = u8"already connected '";
+	  else if (!IsConnected () && ix != Detail::RC_CONNECT)
+	    msg = u8"not connected '";
+	  else
+	    msg = u8"malformed '";
+
+	  read.LexedLine (msg);
+	  msg.append (u8"'");
+	  if (err > 0)
+	    {
+	      msg.append (u8" ");
+	      msg.append (strerror (err));
+	    }
+	  resolver->ErrorResponse (this, std::move (msg));
+	}
+    }
+}
+
+Resolver *ConnectRequest (Server *s, Resolver *r,
+			  std::vector<std::string> &words)
+{
+  if (words.size () < 3 || words.size () > 4)
+    return nullptr;
+
+  if (words.size () == 3)
+    words.emplace_back (u8"");
+  char *eptr;
+  unsigned long version = strtoul (words[1].c_str (), &eptr, 10);
+  if (*eptr)
+    return nullptr;
+
+  return r->ConnectRequest (s, unsigned (version), words[2], words[3]);
+}
+
+int ModuleRepoRequest (Server *s, Resolver *r,std::vector<std::string> &words)
+{
+  if (words.size () != 1)
+    return -1;
+
+  return r->ModuleRepoRequest (s);
+}
+
+int ModuleExportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
+{
+  if (words.size () != 2 || words[1].empty ())
+    return EINVAL;
+
+  return r->ModuleExportRequest (s, words[1]);
+}
+
+int ModuleImportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
+{
+  if (words.size () != 2 || words[1].empty ())
+    return -1;
+
+  return r->ModuleExportRequest (s, words[1]);
+}
+
+int ModuleCompiledRequest (Server *s, Resolver *r,
+			   std::vector<std::string> &words)
+{
+  if (words.size () != 2 || words[1].empty ())
+    return -1;
+
+  return r->ModuleCompiledRequest (s, words[1]);
+}
+
+int IncludeTranslateRequest (Server *s, Resolver *r,
+			     std::vector<std::string> &words)
+{
+  if (words.size () != 2 || words[1].empty ())
+    return -1;
+
+  return r->IncludeTranslateRequest (s, words[1]);
+}
+
+int InvokeSubProcessRequest (Server *s, Resolver *r,
+			     std::vector<std::string> &args)
+{
+  if (args.size () < 2 || args[1].empty ())
+    return -1;
+
+  return r->InvokeSubProcessRequest (s, args);
+}
+
+void Server::ErrorResponse (char const *error, size_t elen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"ERROR");
+  write.AppendWord (error, true, elen);
+  write.EndLine ();
+}
+
+void Server::OKResponse ()
+{
+  write.BeginLine ();
+  write.AppendWord (u8"OK");
+  write.EndLine ();
+}
+
+void Server::ConnectResponse (char const *agent, size_t alen)
+{
+  is_connected = true;
+
+  write.BeginLine ();
+  write.AppendWord (u8"HELLO");
+  write.AppendInteger (Version);
+  write.AppendWord (agent, true, alen);
+  write.EndLine ();
+}
+
+void Server::PathnameResponse (char const *cmi, size_t clen)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"PATHNAME");
+  write.AppendWord (cmi, true, clen);
+  write.EndLine ();
+}
+
+void Server::BoolResponse (bool truthiness)
+{
+  write.BeginLine ();
+  write.AppendWord (u8"BOOL");
+  write.AppendWord (truthiness ? u8"TRUE" : u8"FALSE");
+  write.EndLine ();
+}
+
+}
diff --git c/libcody/tests/01-serialize/connect.cc w/libcody/tests/01-serialize/connect.cc
new file mode 100644
index 00000000000..85277c844af
--- /dev/null
+++ w/libcody/tests/01-serialize/connect.cc
@@ -0,0 +1,30 @@
+
+// Test client connection handshake
+// RUN: <<HELLO 1 TESTING
+// RUN: $subdir$stem | ezio -p OUT $test |& ezio -p ERR $test
+// RUN-END:
+
+// OUT-NEXT:^HELLO {:[0-9]+} TEST IDENT$
+// OUT-NEXT:$EOF
+
+// ERR-NEXT:Code:{:[0-9]+}$
+// ERR-NEXT:Version:1$
+// ERR-NEXT:$EOF
+
+
+// Cody
+#include "cody.hh"
+// C++
+#include <iostream>
+
+using namespace Cody;
+
+int main (int, char *[])
+{
+  Client client (0, 1);
+
+  auto token = client.Connect ("TEST", "IDENT");
+
+  std::cerr << "Code:" << token.GetCode () << '\n';
+  std::cerr << "Version:" << token.GetInteger () << '\n';
+}
diff --git c/libcody/tests/01-serialize/decoder.cc w/libcody/tests/01-serialize/decoder.cc
new file mode 100644
index 00000000000..a3495d145da
--- /dev/null
+++ w/libcody/tests/01-serialize/decoder.cc
@@ -0,0 +1,73 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// RUN: <<bob 'frob dob''\nF\_b\20\61\\'
+// RUN: $subdir$stem |& ezio $test
+// CHECK-NEXT: ^line:0 word:0 'bob'
+// CHECK-NEXT: ^line:0 word:1 'frob dob$
+// CHECK-OPTION: matchSpace
+// CHECK-NEXT: ^F b a\'$
+// CHECK-NEXT: $EOF
+
+/* RUN: <<line-1 word:1 ;
+   RUN: <<'line 2' ;
+   RUN: <<
+*/
+// RUN: $subdir$stem |& ezio -p CHECK2 $test
+// CHECK2-NEXT: line:0 word:0 'line-1'
+// CHECK2-NEXT: line:0 word:1 'word:1'
+// CHECK2-NEXT: line:1 word:0 'line 2'
+// CHECK2-NEXT: error:No 
+// CHECK2-NEXT: $EOF
+
+// RUN: <<'
+// RUN: $subdir$stem |& ezio -p CHECK3 $test
+// CHECK3-NEXT: error:Invalid argument
+// CHECK3-NEXT: line:0 word:0 '''
+// CHECK3-NEXT: $EOF
+
+/* RUN: << ;
+   RUN: <<'\g'
+*/
+// RUN: $subdir$stem |& ezio -p CHECK4 $test
+// CHECK4-NEXT: error:No 
+// CHECK4-NEXT: error:Invalid argument
+// CHECK4-NEXT: line:1 word:0 ''\g''
+// CHECK4-NEXT: $EOF
+
+// RUN-END:
+
+// Cody
+#include "cody.hh"
+// C++
+#include <iostream>
+// C
+#include <cstring>
+
+using namespace Cody;
+
+int main (int, char *[])
+{
+  Detail::MessageBuffer reader;
+
+  reader.PrepareToRead ();
+  while (int e = reader.Read (0))
+    if (e != EAGAIN && e != EINTR)
+      break;
+
+  std::vector<std::string> words;
+  for (unsigned line = 0; !reader.IsAtEnd (); line++)
+    {
+      if (int e = reader.Lex (words))
+	std::cerr << "error:" << strerror (e) << '\n';
+      for (unsigned ix = 0; ix != words.size (); ix++)
+	{
+	  auto &word = words[ix];
+
+	  std::cerr << "line:" << line << " word:" << ix
+		    << " '" << word << "'\n";
+	}
+    }
+  return 0;
+}
diff --git c/libcody/tests/01-serialize/encoder.cc w/libcody/tests/01-serialize/encoder.cc
new file mode 100644
index 00000000000..c4cab6c630f
--- /dev/null
+++ w/libcody/tests/01-serialize/encoder.cc
@@ -0,0 +1,48 @@
+// CODYlib		-*- mode:c++ -*-
+// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
+// License: Apache v2.0
+
+// Test message encoding, both string quoting and continuation lines
+
+// RUN: $subdir$stem |& ezio $test
+// RUN-END:
+// The ¯ is utf8-encoded as c2 af
+// CHECK-NEXT: ^bob 'frob dob''\n¯\\'$
+// CHECK-NEXT: ^2 ;$
+// CHECK-NEXT: ^3$
+// CHECK-NEXT: $EOF
+
+// Cody
+#include "cody.hh"
+
+using namespace Cody;
+
+int main (int, char *[])
+{
+  Detail::MessageBuffer writer;
+
+  writer.BeginLine ();
+  writer.AppendWord ("bob");
+  writer.AppendWord ("frob dob", true);
+  writer.Append ("\n\xc2\xaf\\", true);
+  writer.EndLine ();
+
+  writer.PrepareToWrite ();
+  while (int err = writer.Write (2))
+    if (err != EAGAIN && err != EINTR)
+      break;
+
+  writer.BeginLine ();
+  writer.Append ("2", true);
+  writer.EndLine ();
+  writer.BeginLine ();
+  writer.Append ("3", true);
+  writer.EndLine ();
+
+  writer.PrepareToWrite ();
+  while (int err = writer.Write (2))
+    if (err != EAGAIN && err != EINTR)
+      break;
+
+  return 0;
+}
diff --git c/libcody/tests/02-comms/client-1.cc w/libcody/tests/02-comms/client-1.cc
new file mode 100644
index 00000000000..edff0ab4732
--- /dev/null
+++ w/libcody/tests/02-comms/client-1.cc
@@ -0,0 +1,97 @@
+
+// Test client message round tripping
+/*
+  RUN: <<HELLO 1 TESTING ;
+  RUN: <<PATHNAME REPO ;
+  RUN: <<PATHNAME biz/bar ;
+  RUN: <<PATHNAME blob ;
+  RUN: <<BOOL FALSE ;
+  RUN: << BOOL TRUE ;
+  RUN: << PATHNAME foo ;
+  RUN: <<OK
+*/
+// RUN: $subdir$stem | ezio -p OUT $test |& ezio -p ERR $test
+// RUN-END:
+
+/*
+  OUT-NEXT:^HELLO {:[0-9]+} TEST IDENT ;$
+  OUT-NEXT:^MODULE-REPO ;
+  OUT-NEXT:^MODULE-EXPORT bar ;
+  OUT-NEXT:^MODULE-IMPORT foo ;
+  OUT-NEXT:^INCLUDE-TRANSLATE baz.frob ;
+  OUT-NEXT:^INCLUDE-TRANSLATE ./corge ;
+  OUT-NEXT:^INCLUDE-TRANSLATE ./quux ;
+  OUT-NEXT:^MODULE-COMPILED bar
+*/
+// OUT-NEXT:$EOF
+
+// ERR-NEXT:Code:1$
+// ERR-NEXT:Integer:1$
+// ERR-NEXT:Code:5$
+// ERR-NEXT:String:REPO$
+// ERR-NEXT:Code:5$
+// ERR-NEXT:String:biz/bar$
+// ERR-NEXT:Code:5$
+// ERR-NEXT:String:blob$
+// ERR-NEXT:Code:4$
+// ERR-NEXT:Integer:0$
+// ERR-NEXT:Code:4$
+// ERR-NEXT:Integer:1$
+// ERR-NEXT:Code:5$
+// ERR-NEXT:String:foo
+// ERR-NEXT:Code:3$
+// ERR-NEXT:Integer:
+// ERR-NEXT:$EOF
+
+
+// Cody
+#include "cody.hh"
+// C++
+#include <iostream>
+
+using namespace Cody;
+
+int main (int, char *[])
+{
+  Client client (0, 1);
+
+  client.Cork ();
+  if (client.Connect ("TEST", "IDENT").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.ModuleRepo ().GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.ModuleExport ("bar").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.ModuleImport ("foo").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.IncludeTranslate ("baz.frob").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.IncludeTranslate ("./corge").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.IncludeTranslate ("./quux").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+  if (client.ModuleCompiled ("bar").GetCode () != Client::PC_CORKED)
+    std::cerr << "Not corked!\n";
+
+  auto result = client.Uncork ();
+  for (auto iter = result.begin (); iter != result.end (); ++iter)
+    {
+      std::cerr << "Code:" << iter->GetCode () << '\n';
+      switch (iter->GetCategory ())
+	{
+	case Packet::INTEGER:
+	  std::cerr << "Integer:" << iter->GetInteger () << '\n';
+	  break;
+	case Packet::STRING:
+	  std::cerr << "String:" << iter->GetString () << '\n';
+	  break;
+	case Packet::VECTOR:
+	  {
+	    auto const &v = iter->GetVector ();
+	    for (unsigned ix = 0; ix != v.size (); ix++)
+	      std::cerr << "Vector[" << ix << "]:" << v[ix] << '\n';
+	  }
+	  break;
+	}
+    }
+}
diff --git c/libcody/tests/02-comms/pivot-1.cc w/libcody/tests/02-comms/pivot-1.cc
new file mode 100644
index 00000000000..b98c833ab27
--- /dev/null
+++ w/libcody/tests/02-comms/pivot-1.cc
@@ -0,0 +1,76 @@
+
+// Test resolver pivot
+
+// RUN:<<HELLO 1 TEST IDENT ;
+// RUN:<<MODULE-REPO ;
+// RUN:<<HELLO 1 TEST IDENT
+// RUN: $subdir$stem | ezio -p OUT1 $test |& ezio -p ERR1 $test
+// OUT1-NEXT:HELLO 1 default ;
+// OUT1-NEXT:PATHNAME cmi.cache ;
+// OUT1-NEXT:ERROR 'already connected
+// OUT1-NEXT:$EOF
+// ERR1-NEXT:resolver is handler
+// ERR1-NEXT:$EOF
+
+// RUN:<<MODULE-REPO ;
+// RUN:<<HELLO 1 TEST IDENT ;
+// RUN:<<MODULE-REPO
+// RUN: $subdir$stem | ezio -p OUT2 $test |& ezio -p ERR2 $test
+// OUT2-NEXT:ERROR 'not connected
+// OUT2-NEXT:HELLO 1 default ;
+// OUT2-NEXT:PATHNAME cmi.cache
+// OUT2-NEXT:$EOF
+// ERR2-NEXT:resolver is handler
+// ERR2-NEXT:$EOF
+
+// RUN-END:
+#include "cody.hh"
+#include <iostream>
+
+using namespace Cody;
+
+class Handler : public Resolver
+{
+  virtual Handler *ConnectRequest (Server *s, unsigned ,
+				   std::string &, std::string &)
+  {
+    ErrorResponse (s, "unexpected connect call");
+    return nullptr;
+  }
+};
+
+Handler handler;
+
+class Initial : public Resolver
+{
+  virtual Handler *ConnectRequest (Server *s, unsigned v,
+				   std::string &agent, std::string &ident)
+  {
+    Resolver::ConnectRequest (s, v, agent, ident);
+    return &handler;
+  }
+};
+
+Initial initial;
+
+int main (int, char *[])
+{
+  Server server (&initial, 0, 1);
+
+  while (int e = server.Read ())
+    if (e != EAGAIN && e != EINTR)
+      break;
+
+  server.ProcessRequests ();
+  if (server.GetResolver () == &handler)
+    std::cerr << "resolver is handler\n";
+  else if (server.GetResolver () == &initial)
+    std::cerr << "resolver is initial\n";
+  else
+    std::cerr << "resolver is surprising\n";
+
+  server.PrepareToWrite ();
+  while (int e = server.Write ())
+    if (e != EAGAIN && e != EINTR)
+      break;
+}
diff --git c/libcody/tests/02-comms/server-1.cc w/libcody/tests/02-comms/server-1.cc
new file mode 100644
index 00000000000..0a8694e94df
--- /dev/null
+++ w/libcody/tests/02-comms/server-1.cc
@@ -0,0 +1,68 @@
+
+// Test server message round tripping
+/*
+  RUN:<<HELLO 1 TEST IDENT ;
+  RUN:<<MODULE-REPO ;
+  RUN:<<MODULE-EXPORT bar ;
+  RUN:<<MODULE-IMPORT foo ;
+  RUN:<<NOT A COMMAND ;
+  RUN:<<INCLUDE-TRANSLATE baz.frob ;
+  RUN:<<INCLUDE-TRANSLATE ./quux ;
+  RUN:<<MODULE-COMPILED bar ;
+  RUN:<<MODULE-IMPORT ''
+*/
+// RUN: $subdir$stem | ezio -p OUT1 $test |& ezio -p ERR1 $test
+
+// These all fail because there's nothing in the server interpretting stuff
+/*
+  OUT1-NEXT: ^HELLO 1 default	;
+  OUT1-NEXT: ^PATHNAME cmi.cache	;
+  OUT1-NEXT: ^PATHNAME bar.cmi	;
+  OUT1-NEXT: ^PATHNAME foo.cmi	;
+  OUT1-NEXT: ^ERROR 'unrecognized \'NOT
+  OUT1-NEXT: ^BOOL FALSE	;
+  OUT1-NEXT: ^BOOL FALSE	;
+  OUT1-NEXT: ^OK
+  OUT1-NEXT: ^ERROR 'malformed
+*/
+// OUT1-NEXT:$EOF
+// ERR1-NEXT:$EOF
+
+/*
+  RUN:<<HELLO 1 TEST IDENT
+  RUN:<<MODULE-REPO
+*/
+// RUN: $subdir$stem | ezio -p OUT2 $test |& ezio -p ERR2 $test
+/*
+  OUT2-NEXT: ^HELLO 1 default
+*/
+// OUT2-NEXT:$EOF
+// ERR2-NEXT:$EOF
+
+// RUN-END:
+
+// Cody
+#include "cody.hh"
+// C++
+#include <iostream>
+
+using namespace Cody;
+
+int main (int, char *[])
+{
+  Resolver r;
+  Server server (&r, 0, 1);
+
+  while (int e = server.Read ())
+    if (e != EAGAIN && e != EINTR)
+      break;
+
+  server.ProcessRequests ();
+  if (server.GetResolver () != &r)
+    std::cerr << "resolver changed\n";
+  server.PrepareToWrite ();
+
+  while (int e = server.Write ())
+    if (e != EAGAIN && e != EINTR)
+      break;
+}
diff --git c/libcody/tests/Makesub.in w/libcody/tests/Makesub.in
new file mode 100644
index 00000000000..329e94609ee
--- /dev/null
+++ w/libcody/tests/Makesub.in
@@ -0,0 +1,36 @@
+# CODYlib		-*- mode:Makefile -*-
+# Copyright (C) 2019-2020 Nathan Sidwell, nathan@acm.org
+# License: Apache v2.0
+
+ALOY := @ALOY@
+TESTS := $(patsubst $(srcdir)/%.cc,%,\
+	$(wildcard $(srcdir)/tests/*/*.cc))
+TESTDIRS = $(shell cd $(srcdir)/${<D} ; echo *(/))
+testdir := $(and $(filter-out /%,$(srcdir)),../)$(srcdir)/tests
+
+check:: tests/cody.defs $(TESTS)
+	+cd ${<D} && srcbuilddir=$(srcdir)/tests JOUST=${<F} \
+	  $(ALOY) -t kratos -o cody -g $(testdir)/jouster $(TESTDIRS)
+ifeq ($(firstword $(aloy)),:)
+	@echo WARNING: tests were not run as Joust test harness was not found
+endif
+
+tests/cody.defs: tests/Makesub
+	echo '# Automatically generated by Make' >$@
+	echo 'testdir=$(testdir)' >>$@
+	echo 'timelimit=60' >>$@
+	echo 'memlimit=1' >>$@
+	echo 'cpulimit=60' >>$@
+	echo 'filelimit=1' >>$@
+	echo 'SHELL=$(SHELL)' >>$@
+
+$(TESTS): %: %.o libcody.a
+	$(CXX) $(LDFLAGS) $< -lcody $(LIBS) -o $@
+
+clean::
+	rm -f $(TESTS)
+	rm -f $(TESTS:=.o) $(TESTS:=.d)
+
+ifeq ($(filter clean%,$(MAKECMDGOALS)),)
+-include $(TESTS:=.d)
+endif
diff --git c/libcody/tests/jouster w/libcody/tests/jouster
new file mode 100755
index 00000000000..c7a913c8a19
--- /dev/null
+++ w/libcody/tests/jouster
@@ -0,0 +1,11 @@
+#! /bin/zsh
+# CODYlib		-*- mode:Makefile -*-
+# Copyright (C) 2019-2020 Nathan Sidwell, nathan@acm.org
+# License Apache v2.0
+
+pushd ${0%/*}
+setopt nullglob
+for subdir in $@ ; do
+    echo $subdir/*(.^*)
+done
+popd

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

* [24/32] module mapper
       [not found]                                           ` <c839f3f8-9ce6-0c28-b981-d81f236a3a34@acm.org>
@ 2020-11-03 21:17                                             ` Nathan Sidwell
  2020-11-09  6:42                                               ` Boris Kolpackov
  2020-11-12 16:24                                               ` Nathan Sidwell
       [not found]                                             ` <efe68976-5562-ef6a-7e86-b1daeae73670@acm.org>
  1 sibling, 2 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

this is the module mapper client and server pieces.  It features a 
default resolver that can read a text file, or generate default mappings 
from module name to cmi name.

By default the compiler will use an in-process resolved, but with 
suitable options can be instructed to communicate with a server over
a) a pipe to a spawned process's stdin/stdout
b) a pair of specified filenos
c) a unix-domain socket (if supported)
d) an ipv6 host/port (if supported)

the server can operat in modes a, c or d.


-- 
Nathan Sidwell


[-- Attachment #2: 24-c++-mapper.diff --]
[-- Type: text/x-patch, Size: 41302 bytes --]

diff --git c/gcc/cp/mapper-client.cc w/gcc/cp/mapper-client.cc
new file mode 100644
index 00000000000..de259b0564c
--- /dev/null
+++ w/gcc/cp/mapper-client.cc
@@ -0,0 +1,315 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "line-map.h"
+#include "diagnostic-core.h"
+#define MAPPER_FOR_GCC 1
+#include "mapper.h"
+#include "intl.h"
+
+module_client::module_client (pex_obj *p, int fd_from, int fd_to)
+  : Client (fd_from, fd_to), pex (p)
+{
+}
+
+static module_client *
+spawn_mapper_program (char const **errmsg, std::string &name,
+		      char const *full_program_name)
+{
+  /* Split writable at white-space.  No space-containing args for
+     you!  */
+  // At most every other char could be an argument
+  char **argv = new char *[name.size () / 2 + 2];
+  unsigned arg_no = 0;
+  char *str = new char[name.size ()];
+  memcpy (str, name.c_str () + 1, name.size ());
+
+  for (auto ptr = str; ; ++ptr)
+    {
+      while (*ptr == ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+      argv[arg_no++] = ptr;
+      while (*ptr && *ptr != ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+      *ptr = 0;
+    }
+  argv[arg_no] = nullptr;
+
+  auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
+  FILE *to = pex_input_pipe (pex, false);
+  name = argv[0];
+  if (!to)
+    *errmsg = "connecting input";
+  else
+    {
+      int flags = PEX_SEARCH;
+	  
+      if (full_program_name)
+	{
+	  /* Prepend the invoking path.  */
+	  size_t dir_len = progname - full_program_name;
+	  std::string argv0;
+	  argv0.reserve (dir_len + name.size ());
+	  argv0.append (full_program_name, dir_len).append (name);
+	  name = std::move (argv0);
+	  argv[0] = const_cast <char *> (name.c_str ());
+	  flags = 0;
+	}
+      int err;
+      *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
+    }
+  delete[] str;
+  delete[] argv;
+
+  int fd_from = -1, fd_to = -1;
+  if (!*errmsg)
+    {
+      FILE *from = pex_read_output (pex, false);
+      if (from && (fd_to = dup (fileno (to))) >= 0)
+	fd_from = fileno (from);
+      else
+	*errmsg = "connecting output";
+      fclose (to);
+    }
+
+  if (*errmsg)
+    {
+      pex_free (pex);
+      return nullptr;
+    }
+
+  return new module_client (pex, fd_from, fd_to);
+}
+
+module_client *
+module_client::open_module_client (location_t loc, const char *o,
+				   void (*set_repo) (const char *),
+				   char const *full_program_name)
+{
+  module_client *c = nullptr;
+  std::string ident;
+  std::string name;
+  char const *errmsg = nullptr;
+  unsigned line = 0;
+
+  if (o && o[0])
+    {
+      /* Maybe a local or ipv6 address.  */
+      name = o;
+      auto last = name.find_last_of ('?');
+      if (last != name.npos)
+	{
+	  ident = name.substr (last + 1);
+	  name.erase (last);
+	}
+
+      if (name.size ())
+	{
+	  switch (name[0])
+	    {
+	    case '<':
+	      // <from>to or <>fromto, or <>
+	      {
+		int fd_from = -1, fd_to = -1;
+		char const *ptr = name.c_str ();
+		char *eptr;
+
+		fd_from = strtoul (++ptr, &eptr, 0);
+		if (*eptr == '>')
+		  {
+		    ptr = eptr;
+		    fd_to = strtoul (++ptr, &eptr, 0);
+		    if (eptr != ptr && ptr == name.c_str () + 1)
+		      fd_from = fd_to;
+		  }
+
+		if (*eptr)
+		  errmsg = "parsing";
+		else
+		  {
+		    if (name.size () == 2)
+		      {
+			fd_from = fileno (stdin);
+			fd_to = fileno (stdout);
+		      }
+		    c = new module_client (fd_from, fd_to);
+		  }
+	      }
+	      break;
+
+	    case '=':
+	      // =localsocket
+	      {
+		int fd = -1;
+#if CODY_NETWORKING
+		fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
+#endif
+		if (fd >= 0)
+		  c = new module_client (fd, fd);
+	      }
+	      break;
+
+	    case '|':
+	      // |program and args
+	      c = spawn_mapper_program (&errmsg, name, full_program_name);
+	      break;
+
+	    default:
+	      // file or hostname:port
+	      {
+		auto colon = name.find_last_of (':');
+		if (colon != name.npos)
+		  {
+		    char const *cptr = name.c_str () + colon;
+		    char *endp;
+		    unsigned port = strtoul (cptr + 1, &endp, 10);
+
+		    if (port && endp != cptr + 1 && !*endp)
+		      {
+			name[colon] = 0;
+			int fd = 01;
+#if CODY_NETWORKING
+			fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
+#endif
+			name[colon] = ':';
+
+			if (fd >= 0)
+			  c = new module_client (fd, fd);
+		      }
+		  }
+		
+	      }
+	      break;
+	    }
+	}
+    }
+
+  if (!c)
+    {
+      // Make a default in-process client
+      bool file = !errmsg && !name.empty ();
+      auto r = new module_resolver (!file, true);
+
+      if (file)
+	{
+	int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
+	if (fd < 0)
+	  errmsg = "opening";
+	else
+	  {
+	    if (int l = r->read_tuple_file (fd, ident, false))
+	      {
+		if (l > 0)
+		  line = l;
+		errmsg = "reading";
+	      }
+	      
+	    close (fd);
+	  }
+	}
+      else
+	r->set_repo ("gcm.cache");
+
+      auto *s = new Cody::Server (r);
+      c = new module_client (s);
+    }
+
+#ifdef SIGPIPE
+  if (!c->IsDirect ())
+    /* We need to ignore sig pipe for a while.  */
+    c->sigpipe = signal (SIGPIPE, SIG_IGN);
+#endif
+
+  if (errmsg)
+    error_at (loc, line ? G_("failed %s mapper %qs line %u")
+	      : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
+
+  // now wave hello!
+  c->Cork ();
+  c->Connect (std::string ("GCC"), ident);
+  c->ModuleRepo ();
+  auto packets = c->Uncork ();
+
+  auto &connect = packets[0];
+  if (connect.GetCode () == Cody::Client::PC_CONNECT)
+    ;
+  else if (connect.GetCode () == Cody::Client::PC_ERROR)
+    error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
+
+  auto &repo = packets[1];
+  if (repo.GetCode () == Cody::Client::PC_PATHNAME)
+    set_repo (repo.GetString ().c_str ());
+
+  return c;
+}
+
+void
+module_client::close_module_client (location_t loc, module_client *mapper)
+{
+  if (mapper->IsDirect ())
+    {
+      auto *s = mapper->GetServer ();
+      auto *r = s->GetResolver ();
+      delete s;
+      delete r;
+    }
+  else
+    {
+      if (mapper->pex)
+	{
+	  int fd_write = mapper->GetFDWrite ();
+	  if (fd_write >= 0)
+	    close (fd_write);
+
+	  int status;
+	  pex_get_status (mapper->pex, 1, &status);
+
+	  pex_free (mapper->pex);
+	  mapper->pex = NULL;
+
+	  if (WIFSIGNALED (status))
+	    error_at (loc, "mapper died by signal %s",
+		      strsignal (WTERMSIG (status)));
+	  else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
+	    error_at (loc, "mapper exit status %d",
+		      WEXITSTATUS (status));
+	}
+      else
+	{
+	  int fd_read = mapper->GetFDRead ();
+	  close (fd_read);
+	}
+
+#ifdef SIGPIPE
+      // Restore sigpipe
+      if (mapper->sigpipe != SIG_IGN)
+	signal (SIGPIPE, mapper->sigpipe);
+#endif
+    }
+
+  delete mapper;
+}
diff --git c/gcc/cp/mapper-resolver.cc w/gcc/cp/mapper-resolver.cc
new file mode 100644
index 00000000000..7692380812a
--- /dev/null
+++ w/gcc/cp/mapper-resolver.cc
@@ -0,0 +1,266 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "mapper.h"
+// C++
+#include <algorithm>
+// C
+#include <cstring>
+// OS
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+module_resolver::module_resolver (bool map, bool xlate)
+  : default_map (map), default_translate (xlate)
+{
+}
+
+module_resolver::~module_resolver ()
+{
+  if (fd_repo >= 0)
+    close (fd_repo);
+}
+
+bool
+module_resolver::set_repo (std::string &&r, bool force)
+{
+  if (force || repo.empty ())
+    {
+      repo = std::move (r);
+      force = true;
+    }
+  return force;
+}
+
+bool
+module_resolver::add_mapping (std::string &&module, std::string &&file,
+			      bool force)
+{
+  auto res = map.emplace (std::move (module), std::move (file));
+  if (res.second)
+    force = true;
+  else if (force)
+    res.first->second = std::move (file);
+
+  return force;
+}
+
+int
+module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
+{
+  struct stat stat;
+  if (fstat (fd, &stat) < 0)
+    return -errno;
+
+  if (!stat.st_size)
+    return 0;
+
+  // Just map the file, we're gonna read all of it, so no need for
+  // line buffering
+  void *buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (buffer == MAP_FAILED)
+    return -errno;
+
+  size_t prefix_len = prefix ? strlen (prefix) : 0;
+  unsigned lineno = 0;
+
+  for (char const *begin = reinterpret_cast <char const *> (buffer),
+	 *end = begin + stat.st_size, *eol;
+       begin != end; begin = eol + 1)
+    {
+      lineno++;
+      eol = std::find (begin, end, '\n');
+      if (eol == end)
+	// last line has no \n, ignore the line, you lose
+	break;
+
+      auto *pos = begin;
+      bool pfx_search = prefix_len != 0;
+
+    pfx_search:
+      while (*pos == ' ' || *pos == '\t')
+	pos++;
+
+      auto *space = pos;
+      while (*space != '\n' && *space != ' ' && *space != '\t')
+	space++;
+
+      if (pos == space)
+	// at end of line, nothing here	
+	continue;
+
+      if (pfx_search)
+	{
+	  if (size_t (space - pos) == prefix_len
+	      && std::equal (pos, space, prefix))
+	    pfx_search = false;
+	  pos = space;
+	  goto pfx_search;
+	}
+
+      std::string module (pos, space);
+      while (*space == ' ' || *space == '\t')
+	space++;
+      std::string file (space, eol);
+
+      if (module[0] == '$')
+	{
+	  if (module == "$root")
+	    set_repo (std::move (file));
+	  else
+	    return lineno;
+	}
+      else
+	{
+	  if (file.empty ())
+	    file = GetCMIName (module);
+	  add_mapping (std::move (module), std::move (file), force);
+	}
+    }
+
+  munmap (buffer, stat.st_size);
+
+  return 0;
+}
+
+char const *
+module_resolver::GetCMISuffix ()
+{
+  return "gcm";
+}
+
+module_resolver *
+module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
+				 std::string &a, std::string &i)
+{
+  if (!version || version > Cody::Version)
+    s->ErrorResponse ("version mismatch");
+  else if (a != "GCC")
+    // Refuse anything but GCC
+    ErrorResponse (s, std::string ("only GCC supported"));
+  else if (!ident.empty () && ident != i)
+    // Failed ident check
+    ErrorResponse (s, std::string ("bad ident"));
+  else
+    // Success!
+    s->ConnectResponse ("gcc");
+
+  return this;
+}
+
+int
+module_resolver::ModuleRepoRequest (Cody::Server *s)
+{
+  s->PathnameResponse (repo);
+  return 0;
+}
+
+int
+module_resolver::cmi_response (Cody::Server *s, std::string &module)
+{
+  auto iter = map.find (module);
+  if (iter == map.end ())
+    {
+      std::string file;
+      if (default_map)
+	file = std::move (GetCMIName (module));
+      auto res = map.emplace (module, file);
+      iter = res.first;
+    }
+
+  if (iter->second.empty ())
+    s->ErrorResponse ("no such module");
+  else
+    s->PathnameResponse (iter->second);
+
+  return 0;
+}
+
+int
+module_resolver::ModuleExportRequest (Cody::Server *s, std::string &module)
+{
+  return cmi_response (s, module);
+}
+
+int
+module_resolver::ModuleImportRequest (Cody::Server *s, std::string &module)
+{
+  return cmi_response (s, module);
+}
+
+int
+module_resolver::IncludeTranslateRequest (Cody::Server *s, std::string &include)
+{
+  auto iter = map.find (include);
+  if (iter == map.end () && default_translate)
+    {
+      // Not found, look for it
+      auto file = GetCMIName (include);
+      struct stat statbuf;
+      bool ok = true;
+
+#if HAVE_FSTATAT
+      int fd_dir = AT_FDCWD;
+      if (!repo.empty ())
+	{
+	  if (fd_repo == -1)
+	    {
+	      fd_repo = open (repo.c_str (),
+			      O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+	      if (fd_repo < 0)
+		fd_repo = -2;
+	    }
+	  fd_dir = fd_repo;
+	}
+
+      if (!repo.empty () && fd_repo < 0)
+	ok = false;
+      else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
+	       || !S_ISREG (statbuf.st_mode))
+	ok = false;
+#else
+      auto append = repo;
+      append.push_back (DIR_SEPARATOR);
+      append.append (file);
+      if (stat (append.c_str (), &statbuf) < 0
+	  || !S_ISREG (statbuf.st_mode))
+	ok = false;
+#endif
+      if (!ok)
+	// Mark as not present
+	file.clear ();
+      auto res = map.emplace (include, file);
+      iter = res.first;
+    }
+
+  if (iter == map.end () || iter->second.empty ())
+    s->BoolResponse (false);
+  else
+    s->PathnameResponse (iter->second);
+
+  return 0;
+}
+
diff --git c/gcc/cp/mapper-server.cc w/gcc/cp/mapper-server.cc
new file mode 100644
index 00000000000..f0d26810501
--- /dev/null
+++ w/gcc/cp/mapper-server.cc
@@ -0,0 +1,968 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2018-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "mapper.h"
+
+// C++
+#include <set>
+#include <vector>
+// GCC
+#define INCLUDE_VECTOR
+#define INCLUDE_MAP
+#define INCLUDE_SET
+
+// Network
+/* Include network stuff first.  Excitingly OSX10.14 uses bcmp here, which
+   we poison later!  */
+#if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
+/* socket, bind, listen, accept{4}  */
+# define NETWORKING 1
+# include <sys/socket.h>
+# ifdef HAVE_AF_UNIX
+/* sockaddr_un  */
+#  include <sys/un.h>
+# endif
+# include <netinet/in.h>
+# ifdef HAVE_AF_INET6
+/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons.  */
+#  include <netdb.h>
+# endif
+#ifdef HAVE_INET_NTOP
+/* inet_ntop.  */
+#include <arpa/inet.h>
+#endif
+#endif
+#ifndef HAVE_AF_INET6
+# define gai_strerror(X) ""
+#endif
+
+// Excitingly Darwin uses bcmp in its network headers, and we poison
+// that in our setup.
+#include "system.h"
+#include "version.h"
+#include "intl.h"
+#include <getopt.h>
+
+// Select or epoll
+#ifdef NETWORKING
+#ifdef HAVE_EPOLL
+/* epoll_create, epoll_ctl, epoll_pwait  */
+#include <sys/epoll.h>
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+/* pselect or select  */
+#include <sys/select.h>
+#endif
+#endif
+
+#if !HOST_HAS_O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef HAVE_SIGHANDLER_T
+typedef void (*sighandler_t) (int);
+#endif
+
+#ifdef NETWORKING
+struct netmask {
+  in6_addr addr;
+  unsigned bits;
+
+  netmask (const in6_addr &a, unsigned b)
+  {
+    if (b > sizeof (in6_addr) * 8)
+      b = sizeof (in6_addr) * 8;
+    bits = b;
+    unsigned byte = (b + 7) / 8;
+    unsigned ix = 0;
+    for (ix = 0; ix < byte; ix++)
+      addr.s6_addr[ix] = a.s6_addr[ix];
+    for (; ix != sizeof (in6_addr); ix++)
+      addr.s6_addr[ix] = 0;
+    if (b & 3)
+      addr.s6_addr[b/7] &= (255 << 8) >> (b & 3);
+  }
+
+  bool includes (const in6_addr &a) const
+  {
+    unsigned byte = bits / 8;
+    for (unsigned ix = 0; ix != byte; ix++)
+      if (addr.s6_addr[ix] != a.s6_addr[ix])
+	return false;
+    if (bits & 3)
+      if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3)))
+	return false;
+    return true;
+  }
+};
+
+/* Netmask comparison.  */
+struct netmask_cmp {
+  bool operator() (const netmask &a, const netmask &b) const
+  {
+    if (a.bits != b.bits)
+      return a.bits < b.bits;
+    for (unsigned ix = 0; ix != sizeof (in6_addr); ix++)
+      if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix])
+	return a.addr.s6_addr[ix] < b.addr.s6_addr[ix];
+    return false;
+  }
+};
+
+typedef std::set<netmask, netmask_cmp> netmask_set_t;
+typedef std::vector<netmask> netmask_vec_t;
+#endif
+
+const char *progname;
+
+/* Speak thoughts out loud.  */
+static bool flag_noisy = false;
+
+/* One and done.  */
+static bool flag_one = false;
+
+/* Serialize connections.  */
+static bool flag_sequential = false;
+
+/* Fallback to default if map file is unrewarding.  */
+static bool flag_map = false;
+
+/* Fallback to xlate if map file is unrewarding.  */
+static bool flag_xlate = false;
+
+/* Root binary directory.  */
+static const char *flag_root = "gcm.cache";
+
+#ifdef NETWORKING
+static netmask_set_t netmask_set;
+
+static netmask_vec_t accept_addrs;
+#endif
+
+/* Strip out the source directory from FILE.  */
+
+static const char *
+trim_src_file (const char *file)
+{
+  static const char me[] = __FILE__;
+  unsigned pos = 0;
+
+  while (file[pos] == me[pos] && me[pos])
+    pos++;
+  while (pos && !IS_DIR_SEPARATOR (me[pos-1]))
+    pos--;
+
+  return file + pos;
+}
+
+/* Die screaming.  */
+
+void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
+internal_error (const char *fmt, ...)
+{
+  fprintf (stderr, "%s:Internal error ", progname);
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  exit (FATAL_EXIT_CODE);
+}
+
+/* Hooked to from gcc_assert & gcc_unreachable.  */
+
+void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
+fancy_abort (const char *file, int line, const char *func)
+{
+  internal_error ("in %s, at %s:%d", func, trim_src_file (file), line);
+}
+
+/* Exploded on a signal.  */
+
+static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
+crash_signal (int sig)
+{
+  signal (sig, SIG_DFL);
+  internal_error ("signal %s", strsignal (sig));
+}
+
+/* A fatal error of some kind.  */
+
+static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
+error (const char *msg, ...)
+{
+  fprintf (stderr, "%s:error: ", progname);
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  exit (1);
+}
+
+#ifdef NETWORKING
+/* Progress messages to the user.  */
+static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
+noisy (const char *fmt, ...)
+{
+  fprintf (stderr, "%s:", progname);
+  va_list args;
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  return false;
+}
+#endif
+
+/* More messages to the user.  */
+
+static void ATTRIBUTE_PRINTF_2
+fnotice (FILE *file, const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (file, _(fmt), args);
+  va_end (args);
+}
+
+static void ATTRIBUTE_NORETURN
+print_usage (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
+
+  fnotice (file, "Usage: cxx-mapper [OPTION...] [CONNECTION] [MAPPINGS...] \n\n");
+  fnotice (file, "C++ Module Mapper.\n\n");
+  fnotice (file, "  -a, --accept     Netmask to accept from\n");
+  fnotice (file, "  -f, --fallback   Use fallback for missing mappings\n");
+  fnotice (file, "  -h, --help       Print this help, then exit\n");
+  fnotice (file, "  -n, --noisy      Print progress messages\n");
+  fnotice (file, "  -1, --one        One connection and then exit\n");
+  fnotice (file, "  -r, --root DIR   Root compiled module directory\n");
+  fnotice (file, "  -s, --sequential Process connections sequentially\n");
+  fnotice (file, "  -v, --version    Print version number, then exit\n");
+  fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM);
+  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
+	   bug_report_url);
+  exit (status);
+}
+
+/* Print version information and exit.  */
+
+static void ATTRIBUTE_NORETURN
+print_version (void)
+{
+  fnotice (stdout, "cxx-mapper %s%s\n", pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2018-2020 Free Software Foundation, Inc.\n",
+	   _("(C)"));
+  fnotice (stdout,
+	   _("This is free software; see the source for copying conditions.\n"
+	     "There is NO warranty; not even for MERCHANTABILITY or \n"
+	     "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
+  exit (SUCCESS_EXIT_CODE);
+}
+
+/* ARG is a netmask to accept from.  Add it to the table.  Return
+   false if we fail to resolve it.  */
+
+static bool
+accept_from (char *arg ATTRIBUTE_UNUSED)
+{
+  bool ok = true;
+#if HAVE_AF_INET6
+  unsigned bits = sizeof (in6_addr) * 8;
+  char *slash = strrchr (arg, '/');
+  if (slash)
+    {
+      *slash = 0;
+      if (slash[1])
+	{
+	  char *endp;
+	  bits = strtoul (slash + 1, &endp, 0);
+	}
+    }
+
+  addrinfo hints;
+
+  hints.ai_flags = AI_NUMERICSERV;
+  hints.ai_family = AF_INET6;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+  hints.ai_addrlen = 0;
+  hints.ai_addr = NULL;
+  hints.ai_canonname = NULL;
+  hints.ai_next = NULL;
+
+  struct addrinfo *addrs = NULL;
+  if (int e = getaddrinfo (slash == arg ? NULL : arg, "0", &hints, &addrs))
+    {
+      noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
+      ok = false;
+    }
+  else
+    for (addrinfo *next = addrs; next; next = next->ai_next)
+      if (next->ai_family == AF_INET6)
+	{
+	  netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
+	  netmask_set.insert (mask);
+	}
+  freeaddrinfo (addrs);
+#endif
+  return ok;
+}
+
+/* Process args, return index to first non-arg.  */
+
+static int
+process_args (int argc, char **argv)
+{
+  static const struct option options[] =
+    {
+     { "accept", required_argument, NULL, 'a' },
+     { "help",	no_argument,	NULL, 'h' },
+     { "map",   no_argument,	NULL, 'm' },
+     { "noisy",	no_argument,	NULL, 'n' },
+     { "one",	no_argument,	NULL, '1' },
+     { "root",	required_argument, NULL, 'r' },
+     { "sequential", no_argument, NULL, 's' },
+     { "translate",no_argument,	NULL, 't' },
+     { "version", no_argument,	NULL, 'v' },
+     { 0, 0, 0, 0 }
+    };
+  int opt;
+  bool bad_accept = false;
+  const char *opts = "a:fhmn1r:stv";
+  while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
+    {
+      switch (opt)
+	{
+	case 'a':
+	  if (!accept_from (optarg))
+	    bad_accept = true;
+	  break;
+	case 'h':
+	  print_usage (false);
+	  /* print_usage will exit.  */
+	case 'f': // deprecated alias
+	case 'm':
+	  flag_map = true;
+	  break;
+	case 'n':
+	  flag_noisy = true;
+	  break;
+	case '1':
+	  flag_one = true;
+	  break;
+	case 'r':
+	  flag_root = optarg;
+	  break;
+	case 's':
+	  flag_sequential = true;
+	  break;
+	case 't':
+	  flag_xlate = true;
+	  break;
+	case 'v':
+	  print_version ();
+	  /* print_version will exit.  */
+	default:
+	  print_usage (true);
+	  /* print_usage will exit.  */
+	}
+    }
+
+  if (bad_accept)
+    error ("failed to resolve all accept addresses");
+
+  return optind;
+}
+
+#ifdef NETWORKING
+
+/* Manipulate the EPOLL state, or do nothing, if there is epoll.  */
+
+#ifdef HAVE_EPOLL
+static inline void
+do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
+{
+  epoll_event ev;
+  ev.events = event;
+  ev.data.u32 = data;
+  if (epoll_ctl (epoll_fd, code, fd, &ev))
+    {
+      noisy ("epoll_ctl error:%s", xstrerror (errno));
+      gcc_unreachable ();
+    }
+}
+#define my_epoll_ctl(EFD,C,EV,FD,CL) \
+  ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
+#else
+#define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
+#endif
+
+/* We increment this to tell the server to shut down.  */
+static volatile int term = false;
+static volatile int kill_sock_fd = -1;
+#if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
+static int term_pipe[2] = {-1, -1};
+#else
+#define term_pipe ((int *)NULL)
+#endif
+
+/* A terminate signal.  Shutdown gracefully.  */
+
+static void
+term_signal (int sig)
+{
+  signal (sig, term_signal);
+  term = term + 1;
+  if (term_pipe && term_pipe[1] >= 0)
+    write (term_pipe[1], &term_pipe[1], 1);
+}
+
+/* A kill signal.  Shutdown immediately.  */
+
+static void
+kill_signal (int sig)
+{
+  signal (sig, SIG_DFL);
+  int sock_fd = kill_sock_fd;
+  if (sock_fd >= 0)
+    close (sock_fd);
+  exit (2);
+}
+
+bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
+{
+  switch (server->GetDirection ())
+    {
+    case Cody::Server::READING:
+      if (int err = server->Read ())
+	return !(err == EINTR || err == EAGAIN);
+      server->ProcessRequests ();
+      server->PrepareToWrite ();
+      break;
+
+    case Cody::Server::WRITING:
+      if (int err = server->Write ())
+	return !(err == EINTR || err == EAGAIN);
+      server->PrepareToRead ();
+      break;
+
+    default:
+      // We should never get here
+      return true;
+    }
+
+  // We've changed direction, so update epoll
+  gcc_assert (server->GetFDRead () == server->GetFDWrite ());
+  my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD,
+		server->GetDirection () == Cody::Server::READING
+		? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1);
+
+  return false;
+}
+
+void close_server (Cody::Server *server, int epoll_fd)
+{
+  my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
+
+  close (server->GetFDRead ());
+  
+  delete server;
+}
+
+int open_server (bool ip6, int sock_fd)
+{
+  sockaddr_in6 addr;
+  socklen_t addr_len = sizeof (addr);
+
+#ifdef HAVE_ACCEPT4
+  int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr,
+			   &addr_len, SOCK_NONBLOCK);
+#else
+  int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len);
+#endif
+  if (client_fd < 0)
+    {
+      error ("cannot accept: %s", xstrerror (errno));
+      flag_one = true;
+    }
+  else if (ip6)
+    {
+      const char *str = NULL;
+#if HAVE_INET_NTOP
+      char name[INET6_ADDRSTRLEN];
+      str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name));
+#endif
+      if (!accept_addrs.empty ())
+	{
+	  netmask_vec_t::iterator e = accept_addrs.end ();
+	  for (netmask_vec_t::iterator i = accept_addrs.begin ();
+	       i != e; ++i)
+	    if (i->includes (addr.sin6_addr))
+	      goto present;
+	  close (client_fd);
+	  client_fd = -1;
+	  noisy ("Rejecting connection from disallowed source '%s'",
+		 str ? str : "");
+	present:;
+	}
+      if (client_fd >= 0)
+	flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
+    }
+
+  return client_fd;
+}
+
+/* A server listening on bound socket SOCK_FD.  */
+
+static void
+server (bool ipv6, int sock_fd, module_resolver *resolver)
+{
+  int epoll_fd = -1;
+
+  signal (SIGTERM, term_signal);
+#ifdef HAVE_EPOLL
+  epoll_fd = epoll_create (1);
+#endif
+  if (epoll_fd >= 0)
+    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
+
+#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  sigset_t mask;
+  {
+    sigset_t block;
+    sigemptyset (&block);
+    sigaddset (&block, SIGTERM);
+    sigprocmask (SIG_BLOCK, &block, &mask);
+  }
+#endif
+
+#ifdef HAVE_EPOLL
+  const unsigned max_events = 20;
+  epoll_event events[max_events];
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  fd_set readers, writers;
+#endif
+  if (term_pipe)
+    pipe (term_pipe);
+
+  // We need stable references to servers, so this array can contain nulls
+  std::vector<Cody::Server *> connections;
+  unsigned live = 0;
+  while (sock_fd >= 0 || live)
+    {
+      /* Wait for one or more events.  */
+      bool eintr = false;
+      int event_count;
+
+      if (epoll_fd >= 0)
+	{
+#ifdef HAVE_EPOLL
+	  event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
+#endif
+	}
+      else
+	{
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+	  FD_ZERO (&readers);
+	  FD_ZERO (&writers);
+
+	  unsigned limit = 0;
+	  if (sock_fd >= 0
+	      && !(term || (live && (flag_one || flag_sequential))))
+	    {
+	      FD_SET (sock_fd, &readers);
+	      limit = sock_fd + 1;
+	    }
+
+	  if (term_pipe && term_pipe[0] >= 0)
+	    {
+	      FD_SET (term_pipe[0], &readers);
+	      if (unsigned (term_pipe[0]) >= limit)
+		limit = term_pipe[0] + 1;
+	    }
+
+	  for (auto iter = connections.begin ();
+	       iter != connections.end (); ++iter)
+	    if (auto *server = *iter)
+	      {
+		int fd = -1;
+		switch (server->GetDirection ())
+		  {
+		  case Cody::Server::READING:
+		    fd = server->GetFDRead ();
+		    FD_SET (fd, &readers);
+		    break;
+		  case Cody::Server::WRITING:
+		    fd = server->GetFDWrite ();
+		    FD_SET (fd, &writers);
+		    break;
+		  default:
+		    break;
+		  }
+
+		if (fd >= 0 && limit <= unsigned (fd))
+		  limit = fd + 1;
+	      }
+
+#ifdef HAVE_PSELECT
+	  event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask);
+#else
+	  event_count = select (limit, &readers, &writers, NULL, NULL);
+#endif
+	  if (term_pipe && FD_ISSET (term_pipe[0], &readers))
+	    {
+	      /* Fake up an interrupted system call.  */
+	      event_count = -1;
+	      errno = EINTR;
+	    }
+#endif
+	}
+
+      if (event_count < 0)
+	{
+	  // Error in waiting
+	  if (errno == EINTR)
+	    {
+	      flag_noisy && noisy ("Interrupted wait");
+	      eintr = true;
+	    }
+	  else
+	    error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait"
+#ifdef HAVE_PSELECT
+		   : "pselect",
+#else
+		   : "select",
+#endif
+		   xstrerror (errno));
+	  event_count = 0;
+	}
+
+      auto iter = connections.begin ();
+      while (event_count--)
+	{
+	  // Process an event
+	  int active = -2;
+
+	  if (epoll_fd >= 0)
+	    {
+#ifdef HAVE_EPOLL
+	      /* See PR c++/88664 for why a temporary is used.  */
+	      unsigned data = events[event_count].data.u32;
+	      active = int (data) - 1;
+#endif
+	    }
+	  else
+	    {
+	      for (; iter != connections.end (); ++iter)
+		if (auto *server = *iter)
+		  {
+		    bool found = false;
+		    switch (server->GetDirection ())
+		      {
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+		      case Cody::Server::READING:
+			found = FD_ISSET (server->GetFDRead (), &readers);
+			break;
+		      case Cody::Server::WRITING:
+			found = FD_ISSET (server->GetFDWrite (), &writers);
+			break;
+#endif
+		      default:
+			break;
+		      }
+
+		    if (found)
+		      {
+			active = iter - connections.begin ();
+			++iter;
+			break;
+		      }
+		  }
+
+	      if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
+		active = -1;
+	    }
+
+	  if (active >= 0)
+	    {
+	      // Do the action
+	      auto *server = connections[active];
+	      if (process_server (server, active, epoll_fd))
+		{
+		  connections[active] = nullptr;
+		  close_server (server, epoll_fd);
+		  live--;
+		  if (flag_sequential)
+		    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
+		}
+	    }
+	  else if (active == -1 && !eintr)
+	    {
+	      // New connection
+	      int fd = open_server (ipv6, sock_fd);
+	      if (fd >= 0)
+		{
+#if !defined (HAVE_ACCEPT4) \
+  && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
+		  int flags = fcntl (fd, F_GETFL, 0);
+		  fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+#endif
+		  auto *server = new Cody::Server (resolver, fd);
+
+		  unsigned slot = connections.size ();
+		  if (live == slot)
+		    connections.push_back (server);
+		  else
+		    for (auto iter = connections.begin (); ; ++iter)
+		      if (!*iter)
+			{
+			  *iter = server;
+			  slot = iter - connections.begin ();
+			  break;
+			}
+		  live++;
+		  my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
+		}
+	    }
+
+	  if (sock_fd >= 0
+	      && (term || (live && (flag_one || flag_sequential))))
+	    {
+	      /* Stop paying attention to sock_fd.  */
+	      my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
+	      if (flag_one || term)
+		{
+		  close (sock_fd);
+		  sock_fd = -1;
+		}
+	    }
+	}
+    }
+#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  /* Restore the signal mask.  */
+  sigprocmask (SIG_SETMASK, &mask, NULL);
+#endif
+
+  gcc_assert (sock_fd < 0);
+  if (epoll_fd >= 0)
+    close (epoll_fd);
+
+  if (term_pipe && term_pipe[0] >= 0)
+    {
+      close (term_pipe[0]);
+      close (term_pipe[1]);
+    }
+}
+
+#endif
+
+static int maybe_parse_socket (std::string &option, module_resolver *r)
+{
+  /* Local or ipv6 address.  */
+  auto last = option.find_last_of ('?');
+  if (last != option.npos)
+    {
+      r->set_ident (option.c_str () + last + 1);
+      option.erase (last);
+    }
+  int fd = -2;
+  char const *errmsg = nullptr;
+
+  /* Does it look like a socket?  */
+  if (option[0] == '=')
+    {
+      /* A local socket.  */
+#if CODY_NETWORKING
+      fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
+#endif
+    }
+  else
+    {
+      auto colon = option.find_last_of (':');
+      if (colon != option.npos)
+	{
+	  /* Try a hostname:port address.  */
+	  char const *cptr = option.c_str () + colon;
+	  char *endp;
+	  unsigned port = strtoul (cptr + 1, &endp, 10);
+
+	  if (port && endp != cptr + 1 && !*endp)
+	    {
+	      /* Ends in ':number', treat as ipv6 domain socket.  */
+	      option.erase (colon);
+#if CODY_NETWORKING
+	      fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
+#endif
+	    }
+	}
+    }
+
+  if (errmsg)
+    error ("failed to open socket: %s", errmsg);
+
+  return fd;
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *p = argv[0] + strlen (argv[0]);
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
+  progname = p;
+
+  xmalloc_set_program_name (progname);
+
+#ifdef SIGSEGV
+  signal (SIGSEGV, crash_signal);
+#endif
+#ifdef SIGILL
+  signal (SIGILL, crash_signal);
+#endif
+#ifdef SIGBUS
+  signal (SIGBUS, crash_signal);
+#endif
+#ifdef SIGABRT
+  signal (SIGABRT, crash_signal);
+#endif
+#ifdef SIGFPE
+  signal (SIGFPE, crash_signal);
+#endif
+#ifdef SIGPIPE
+  /* Ignore sigpipe, so read/write get an error.  */
+  signal (SIGPIPE, SIG_IGN);
+#endif
+#ifdef NETWORKING
+#ifdef SIGINT
+  signal (SIGINT, kill_signal);
+#endif
+#endif
+
+  int argno = process_args (argc, argv);
+
+  std::string name;
+  int sock_fd = -1; /* Socket fd, otherwise stdin/stdout.  */
+  module_resolver r (flag_map, flag_xlate);
+
+  if (argno != argc)
+    {
+      name = argv[argno];
+      sock_fd = maybe_parse_socket (name, &r);
+      if (!name.empty ())
+	argno++;
+    }
+
+  if (argno != argc)
+    for (; argno != argc; argno++)
+      {
+	std::string option = argv[argno];
+	char const *prefix = nullptr;
+	auto ident = option.find_last_of ('?');
+	if (ident != option.npos)
+	  {
+	    prefix = option.c_str () + ident + 1;
+	    option[ident] = 0;
+	  }
+	int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
+	int err = 0;
+	if (fd < 0)
+	  err = errno;
+	else
+	  {
+	    err = r.read_tuple_file (fd, prefix, false);
+	    close (fd);
+	  }
+
+	if (err)
+	  error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
+      }
+  else
+    r.set_default_map (true);
+
+  if (flag_root)
+    r.set_repo (flag_root);
+
+#ifdef HAVE_AF_INET6
+  netmask_set_t::iterator end = netmask_set.end ();
+  for (netmask_set_t::iterator iter = netmask_set.begin ();
+       iter != end; ++iter)
+    {
+      netmask_vec_t::iterator e = accept_addrs.end ();
+      for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
+	if (i->includes (iter->addr))
+	  goto present;
+      accept_addrs.push_back (*iter);
+    present:;
+    }
+#endif
+
+#ifdef NETWORKING
+  if (sock_fd >= 0)
+    {
+      server (name[0] != '=', sock_fd, &r);
+      if (name[0] == '=')
+	unlink (name.c_str () + 1);
+    }
+  else
+#endif
+    {
+      gcc_assert (sock_fd < 0);
+      auto server = Cody::Server (&r, 0, 1);
+
+      int err = 0;
+      for (;;)
+	{
+	  server.PrepareToRead ();
+	  while ((err = server.Read ()))
+	    {
+	      if (err == EINTR || err == EAGAIN)
+		continue;
+	      goto done;
+	    }
+
+	  server.ProcessRequests ();
+
+	  server.PrepareToWrite ();
+	  while ((err = server.Write ()))
+	    {
+	      if (err == EINTR || err == EAGAIN)
+		continue;
+	      goto done;
+	    }
+	}
+    done:;
+      if (err > 0)
+	error ("communication error:%s", xstrerror (err));
+    }
+
+  return 0;
+}
diff --git c/gcc/cp/mapper.h w/gcc/cp/mapper.h
new file mode 100644
index 00000000000..86562252b53
--- /dev/null
+++ w/gcc/cp/mapper.h
@@ -0,0 +1,122 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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/>.  */
+
+// Mapper interface for client and server bits
+#include "cody.hh"
+// C++
+#include <string>
+#include <map>
+
+// This is a GCC class, so GCC coding conventions on new bits.  
+class module_resolver : public Cody::Resolver
+{
+public:
+  using parent = Cody::Resolver;
+  using module_map = std::map<std::string, std::string>;
+
+private:
+  std::string repo;
+  std::string ident;
+  module_map map;
+  int fd_repo = -1;
+  bool default_map = true;
+  bool default_translate = true;
+
+public:
+  module_resolver (bool map = true, bool xlate = false);
+  virtual ~module_resolver () override;
+
+public:
+  void set_default_map (bool d)
+  {
+    default_map = d;
+  }
+  void set_default_translate (bool d)
+  {
+    default_translate = d;
+  }
+  void set_ident (char const *i)
+  {
+    ident = i;
+  }
+  bool set_repo (std::string &&repo, bool force = false);
+  bool add_mapping (std::string &&module, std::string &&file,
+		    bool force = false);
+
+  // Return +ve line number of error, or -ve errno
+  int read_tuple_file (int fd, char const *prefix, bool force = false);
+  int read_tuple_file (int fd, std::string const &prefix,
+			    bool force = false)
+  {
+    return read_tuple_file (fd, prefix.empty () ? nullptr : prefix.c_str (),
+			    force);
+  }
+
+public:
+  // Virtual overriders, names are controlle by Cody::Resolver
+  virtual module_resolver *ConnectRequest (Cody::Server *, unsigned version,
+					   std::string &agent,
+					   std::string &ident)
+    override;
+  virtual int ModuleRepoRequest (Cody::Server *) override;
+  virtual int ModuleExportRequest (Cody::Server *s, std::string &module)
+    override;
+  virtual int ModuleImportRequest (Cody::Server *s, std::string &module)
+    override;
+  virtual int IncludeTranslateRequest (Cody::Server *s, std::string &include)
+    override;
+
+private:
+  virtual char const *GetCMISuffix () override;
+
+private:
+  int cmi_response (Cody::Server *s, std::string &module);
+};
+
+#ifdef MAPPER_FOR_GCC
+#ifndef HAVE_SIGHANDLER_T
+typedef void (*sighandler_t) (int);
+#endif
+
+class module_client : public Cody::Client
+{
+  pex_obj *pex = nullptr;
+  sighandler_t sigpipe = SIG_IGN;
+
+public:
+  module_client (Cody::Server *s)
+    : Client (s)
+  {
+  }
+  module_client (pex_obj *pex, int fd_from, int fd_to);
+
+  module_client (int fd_from, int fd_to)
+    : Client (fd_from, fd_to)
+  {
+  }
+
+public:
+  static module_client *open_module_client (location_t loc, const char *option,
+					    void (*set_repo) (const char *),
+					    char const *);
+  static void close_module_client (location_t loc, module_client *);
+};
+
+#endif


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

* [25/32] modules!
       [not found]                                             ` <efe68976-5562-ef6a-7e86-b1daeae73670@acm.org>
@ 2020-11-03 21:17                                               ` Nathan Sidwell
  2020-11-04 12:48                                                 ` Nathan Sidwell
       [not found]                                               ` <3d138aa4-df19-df5d-54c6-ec7299749f0f@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

this is the new modules file. (also resolvesa long standing fixme in pt.c)

I'll leave the introductory comment to speak for itself, but ask if 
something's unclear.  As mentioned in the introduction, this is where 
the bulk of the fixmes remain.


nathan
-- 
Nathan Sidwell


[-- Attachment #2: 25-c++-modules.diff --]
[-- Type: text/x-patch, Size: 537304 bytes --]

diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index aa162d2a4f9..497ac5aafec 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -22,7 +22,9 @@ along with GCC; see the file COPYING3.  If not see
 /* Known bugs or deficiencies include:
 
      all methods must be provided in header files; can't use a source
-     file that contains only the method templates and "just win".  */
+     file that contains only the method templates and "just win".
+
+     Fixed by: C++20 modules.  */
 
 #include "config.h"
 #include "system.h"
diff --git c/gcc/cp/module.cc w/gcc/cp/module.cc
new file mode 100644
index 00000000000..2d0517f50e6
--- /dev/null
+++ w/gcc/cp/module.cc
@@ -0,0 +1,19835 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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/>.  */
+
+/* Comments in this file have a non-negligible chance of being wrong
+   or at least inaccurate.  Due to (a) my misunderstanding, (b)
+   ambiguities that I have interpretted differently to original intent
+   (c) changes in the specification, (d) my poor wording, (e) source
+   changes.  */
+
+/* (Incomplete) Design Notes
+
+   A hash table contains all module names.  Imported modules are
+   present in a modules array, which by construction places an
+   import's dependencies before the import itself.  The single
+   exception is the current TU, which always occupies slot zero (even
+   when it is not a module).
+
+   Imported decls occupy an entity_ary, an array of mc_slots, indexed
+   by importing module and index within that module.  A flat index is
+   used, as each module reserves a contiguous range of indices.
+   Initially each slot indicates the CMI section containing the
+   streamed decl.  When the decl is imported it will point to the decl
+   itself.
+
+   Additionally each imported decl is mapped in the entity_map via its
+   DECL_UID to the flat index in the entity_ary.  Thus we can locate
+   the index for any imported decl by using this map and then
+   de-flattening the index via a binary seach of the module vector.
+   Cross-module references are by (remapped) module number and
+   module-local index.
+
+   Each importable DECL contains several flags.  The simple set are
+   DECL_EXPORT_P, DECL_MODULE_PURVIEW_P and DECL_MODULE_IMPORT_P.  The
+   first indicates whether it is exported, the second whether it is in
+   the module purview (as opposed to the global module fragment), and
+   the third indicates whether it was an import into this TU or not.
+
+   The more detailed flags are DECL_MODULE_PARTITION_P,
+   DECL_MODULE_ENTITY_P & DECL_MODULE_PENDING_SPECIALIZATIONS_P.  The
+   first is set in a primary interface unit on decls that were read
+   from module partitions (these will have DECL_MODULE_IMPORT_P set
+   too).  Such decls will be streamed out to the primary's CMI.
+   DECL_MODULE_ENTITY_P is set when an entity is imported, even if it
+   matched a non-imported entity.  Such a decl will not have
+   DECL_MODULE_IMPORT_P set, even though it has an entry in the entity
+   map and array.  DECL_MODULE_PENDING_SPECIALIZATIONS_P is set on a
+   primary template, and indicates there are specializations that
+   should be streamed in before trying to specialize this template.
+
+   Header units are module-like.
+
+   For namespace-scope lookup, the decls for a particular module are
+   held located in a sparse array hanging off the binding of the name.
+   This is partitioned into two: a few fixed slots at the start
+   followed by the sparse slots afterwards.  By construction we only
+   need to append new slots to the end -- there is never a need to
+   insert in the middle.  The fixed slots are MODULE_SLOT_CURRENT for
+   the current TU (regardless of whether it is a module or not),
+   MODULE_SLOT_GLOBAL and MODULE_SLOT_PARTITION.  These latter two
+   slots are used for merging entities across the global module and
+   module partitions respectively.  MODULE_SLOT_PARTITION is only
+   present in a module.  Neither of those two slots is searched during
+   name lookup -- they are internal use only.  This vector is created
+   lazily once we require it, if there is only a declaration from the
+   current TU, a regular binding is present.  It is converted on
+   demand.
+
+   OPTIMIZATION: Outside of the current TU, we only need ADL to work.
+   We could optimize regular lookup for the current TU by glomming all
+   the visible decls on its slot.  Perhaps wait until design is a
+   little more settled though.
+
+   There is only one instance of each extern-linkage namespace.  It
+   appears in every module slot that makes it visible.  It also
+   appears in MODULE_SLOT_GLOBAL.  (It is an ODR violation if they
+   collide with some other global module entity.)  We also have an
+   optimization that shares the slot for adjacent modules that declare
+   the same such namespace.
+
+   A module interface compilation produces a Compiled Module Interface
+   (CMI).  The format used is Encapsulated Lazy Records Of Numbered
+   Declarations, which is essentially ELF's section encapsulation. (As
+   all good nerds are aware, Elrond is half Elf.)  Some sections are
+   named, and contain information about the module as a whole (indices
+   etc), and other sections are referenced by number.  Although I
+   don't defend against actively hostile CMIs, there is some
+   checksumming involved to verify data integrity.  When dumping out
+   an interface, we generate a graph of all the
+   independently-redeclarable DECLS that are needed, and the decls
+   they reference.  From that we determine the strongly connected
+   components (SCC) within this TU.  Each SCC is dumped to a separate
+   numbered section of the CMI.  We generate a binding table section,
+   mapping each namespace&name to a defining section.  This allows
+   lazy loading.
+
+   Lazy loading employs mmap to map a read-only image of the CMI.
+   It thus only occupies address space and is paged in on demand,
+   backed by the CMI file itself.  If mmap is unavailable, regular
+   FILEIO is used.  Also, there's a bespoke ELF reader/writer here,
+   which implements just the section table and sections (including
+   string sections) of a 32-bit ELF in host byte-order.  You can of
+   course inspect it with readelf.  I figured 32-bit is sufficient,
+   for a single module.  I detect running out of section numbers, but
+   do not implement the ELF overflow mechanism.  At least you'll get
+   an error if that happens.
+
+   We do not separate declarations and definitions.  My guess is that
+   if you refer to the declaration, you'll also need the definition
+   (template body, inline function, class definition etc).  But this
+   does mean we can get larger SCCs than if we separated them.  It is
+   unclear whether this is a win or not.
+
+   Notice that we embed section indices into the contents of other
+   sections.  Thus random manipulation of the CMI file by ELF tools
+   may well break it.  The kosher way would probably be to introduce
+   indirection via section symbols, but that would require defining a
+   relocation type.
+
+   Notice that lazy loading of one module's decls can cause lazy
+   loading of other decls in the same or another module.  Clearly we
+   want to avoid loops.  In a correct program there can be no loops in
+   the module dependency graph, and the above-mentioned SCC algorithm
+   places all intra-module circular dependencies in the same SCC.  It
+   also orders the SCCs wrt each other, so dependent SCCs come first.
+   As we load dependent modules first, we know there can be no
+   reference to a higher-numbered module, and because we write out
+   dependent SCCs first, likewise for SCCs within the module.  This
+   allows us to immediately detect broken references.  When loading,
+   we must ensure the rest of the compiler doesn't cause some
+   unconnected load to occur (for instance, instantiate a template).
+
+Classes used:
+
+   dumper - logger
+
+   data - buffer
+
+   bytes - data streamer
+   bytes_in : bytes - scalar reader
+   bytes_out : bytes - scalar writer
+
+   elf - ELROND format
+   elf_in : elf - ELROND reader
+   elf_out : elf - ELROND writer
+
+   trees_in : bytes_in - tree reader
+   trees_out : bytes_out - tree writer
+
+   depset - dependency set
+   depset::hash - hash table of depsets
+   depset::tarjan - SCC determinator
+
+   uidset<T> - set T's related to a UID
+   uidset<T>::hash hash table of uidset<T>
+
+   loc_spans - location map data
+
+   module_state - module object
+
+   slurping - data needed during loading
+
+   macro_import - imported macro data
+   macro_export - exported macro data
+
+   The ELROND objects use mmap, for both reading and writing.  If mmap
+   is unavailable, fileno IO is used to read and write blocks of data.
+
+   The mapper object uses fileno IO to communicate with the server or
+   program.   */
+
+/* In expermental (trunk) sources, MODULE_VERSION is a #define passed
+   in from the Makefile.  It records the modification date of the
+   source directory -- that's the only way to stay sane.  In release
+   sources, we (plan to) use the compiler's major.minor versioning.
+   While the format might not change between at minor versions, it
+   seems simplest to tie the two together.  There's no concept of
+   inter-version compatibility.  */
+#define IS_EXPERIMENTAL(V) ((V) >= (1U << 20))
+#define MODULE_MAJOR(V) ((V) / 10000)
+#define MODULE_MINOR(V) ((V) % 10000)
+#define EXPERIMENT(A,B) (IS_EXPERIMENTAL (MODULE_VERSION) ? (A) : (B))
+#ifndef MODULE_VERSION
+#error "Shtopp! What are you doing? This is not ready yet."
+#include "bversion.h"
+#define MODULE_VERSION (BUILDING_GCC_MAJOR * 10000U + BUILDING_GCC_MINOR)
+#elif !IS_EXPERIMENTAL (MODULE_VERSION)
+#error "This is not the version I was looking for."
+#endif
+
+#define _DEFAULT_SOURCE 1 /* To get TZ field of struct tm, if available.  */
+#include "config.h"
+
+#include "system.h"
+#include "coretypes.h"
+#include "cp-tree.h"
+#include "timevar.h"
+#include "stringpool.h"
+#include "dumpfile.h"
+#include "bitmap.h"
+#include "cgraph.h"
+#include "tree-iterator.h"
+#include "cpplib.h"
+#include "mkdeps.h"
+#include "incpath.h"
+#include "libiberty.h"
+#include "stor-layout.h"
+#include "version.h"
+#include "tree-diagnostic.h"
+#include "toplev.h"
+#include "opts.h"
+#include "attribs.h"
+#include "intl.h"
+#include "langhooks.h"
+#define MAPPER_FOR_GCC 1
+#include "mapper.h"
+
+#if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
+/* mmap, munmap.  */
+#define MAPPED_READING 1
+#if HAVE_SYSCONF && defined (_SC_PAGE_SIZE)
+/* msync, sysconf (_SC_PAGE_SIZE), ftruncate  */
+/* posix_fallocate used if available.  */
+#define MAPPED_WRITING 1
+#else
+#define MAPPED_WRITING 0
+#endif
+#else
+#define MAPPED_READING 0
+#define MAPPED_WRITING 0
+#endif
+#if 0 // for testing
+#undef MAPPED_READING
+#undef MAPPED_WRITING
+#define MAPPED_READING 0
+#define MAPPED_WRITING 0
+#endif
+
+#if !HOST_HAS_O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static inline cpp_hashnode *cpp_node (tree id)
+{
+  return CPP_HASHNODE (GCC_IDENT_TO_HT_IDENT (id));
+}
+static inline tree identifier (cpp_hashnode *node)
+{
+  return HT_IDENT_TO_GCC_IDENT (HT_NODE (node));
+}
+static inline const_tree identifier (const cpp_hashnode *node)
+{
+  return identifier (const_cast <cpp_hashnode *> (node));
+}
+
+/* Id for dumping module information.  */
+int module_dump_id;
+
+/* We have a special module owner.  */
+#define MODULE_UNKNOWN (~0U)    /* Not yet known.  */
+
+/* Prefix for section names.  */
+#define MOD_SNAME_PFX ".gnu.c++"
+
+/* Format a version for user consumption.  */
+
+typedef char verstr_t[32];
+static void
+version2string (unsigned version, verstr_t &out)
+{
+  unsigned major = MODULE_MAJOR (version);
+  unsigned minor = MODULE_MINOR (version);
+
+  if (IS_EXPERIMENTAL (version))
+    sprintf (out, "%04u/%02u/%02u-%02u:%02u%s",
+	     2000 + major / 10000, (major / 100) % 100, (major % 100),
+	     minor / 100, minor % 100,
+	     EXPERIMENT ("", " (experimental)"));
+  else
+    sprintf (out, "%u.%u", major, minor);
+}
+
+/* Include files to note translation for.  */
+static vec<const char *, va_heap, vl_embed> *note_includes;
+
+/* Traits to hash an arbitrary pointer.  Entries are not deletable,
+   and removal is a noop (removal needed upon destruction).  */
+template <typename T>
+struct nodel_ptr_hash : pointer_hash<T>, typed_noop_remove <T *> {
+  /* Nothing is deletable.  Everything is insertable.  */
+  static bool is_deleted (T *) { return false; }
+  static void mark_deleted (T *) { gcc_unreachable (); }
+};
+
+/* Map from pointer to signed integer.   */
+typedef simple_hashmap_traits<nodel_ptr_hash<void>, int> ptr_int_traits;
+typedef hash_map<void *,signed,ptr_int_traits> ptr_int_hash_map;
+
+/********************************************************************/
+/* Basic streaming & ELF.  Serialization is usually via mmap.  For
+   writing we slide a buffer over the output file, syncing it
+   approproiately.  For reading we simply map the whole file (as a
+   file-backed read-only map -- it's just address space, leaving the
+   OS pager to deal with getting the data to us).  Some buffers need
+   to be more conventional malloc'd contents.   */
+
+/* Variable length buffer.  */
+
+class data {
+public:
+  class allocator {
+  public:
+    /* Tools tend to moan if the dtor's not virtual.  */
+    virtual ~allocator () {}
+
+  public:
+    void grow (data &obj, unsigned needed, bool exact);
+    void shrink (data &obj);
+
+  public:
+    virtual char *grow (char *ptr, unsigned needed);
+    virtual void shrink (char *ptr);
+  };
+
+public:
+  char *buffer;		/* Buffer being transferred.  */
+  /* Although size_t would be the usual size, we know we never get
+     more than 4GB of buffer -- because that's the limit of the
+     encapsulation format.  And if you need bigger imports, you're
+     doing it wrong.  */
+  unsigned size;	/* Allocated size of buffer.  */
+  unsigned pos;		/* Position in buffer.  */
+
+public:
+  data ()
+    :buffer (NULL), size (0), pos (0)
+  {
+  }
+  ~data ()
+  {
+    /* Make sure the derived and/or using class know what they're
+       doing.  */
+    gcc_checking_assert (!buffer);
+  }
+
+protected:
+  char *use (unsigned count)
+  {
+    if (size < pos + count)
+      return NULL;
+    char *res = &buffer[pos];
+    pos += count;
+    return res;
+  }
+
+public:
+  void unuse (unsigned count)
+  {
+    pos -= count;
+  }
+
+public:
+  static allocator simple_memory;
+};
+
+/* The simple data allocator.  */
+data::allocator data::simple_memory;
+
+/* Grow buffer to at least size NEEDED.  */
+
+void
+data::allocator::grow (data &obj, unsigned needed, bool exact)
+{
+  gcc_checking_assert (needed ? needed > obj.size : !obj.size);
+  if (!needed)
+    /* Pick a default size.  */
+    needed = EXPERIMENT (100, 1000);
+
+  if (!exact)
+    needed *= 2;
+  obj.buffer = grow (obj.buffer, needed);
+  if (obj.buffer)
+    obj.size = needed;
+  else
+    obj.pos = obj.size = 0;
+}
+
+/* Free a buffer.  */
+
+void
+data::allocator::shrink (data &obj)
+{
+  shrink (obj.buffer);
+  obj.buffer = NULL;
+  obj.size = 0;
+}
+
+char *
+data::allocator::grow (char *ptr, unsigned needed)
+{
+  return XRESIZEVAR (char, ptr, needed);
+}
+
+void
+data::allocator::shrink (char *ptr)
+{
+  XDELETEVEC (ptr);
+}
+
+/* Byte streamer base.   Buffer with read/write position and smarts
+   for single bits.  */
+
+class bytes : public data {
+public:
+  typedef data parent;
+
+protected:
+  uint32_t bit_val;	/* Bit buffer.  */
+  unsigned bit_pos;	/* Next bit in bit buffer.  */
+
+public:
+  bytes ()
+    :parent (), bit_val (0), bit_pos (0)
+  {}
+  ~bytes () 
+  {
+  }
+
+protected:
+  unsigned calc_crc (unsigned) const;
+
+protected:
+  /* Finish bit packet.  Rewind the bytes not used.  */
+  unsigned bit_flush ()
+  {
+    gcc_assert (bit_pos);
+    unsigned bytes = (bit_pos + 7) / 8;
+    unuse (4 - bytes);
+    bit_pos = 0;
+    bit_val = 0;
+    return bytes;
+  }
+};
+
+/* Calculate the crc32 of the buffer.  Note the CRC is stored in the
+   first 4 bytes, so don't include them.  */
+
+unsigned
+bytes::calc_crc (unsigned l) const
+{
+  unsigned crc = 0;
+  for (size_t ix = 4; ix < l; ix++)
+    crc = crc32_byte (crc, buffer[ix]);
+  return crc;
+}
+
+class elf_in;
+
+/* Byte stream reader.  */
+
+class bytes_in : public bytes {
+  typedef bytes parent;
+
+protected:
+  bool overrun;  /* Sticky read-too-much flag.  */
+
+public:
+  bytes_in ()
+    : parent (), overrun (false)
+  {
+  }
+  ~bytes_in ()
+  {
+  }
+
+public:
+  /* Begin reading a named section.  */
+  bool begin (location_t loc, elf_in *src, const char *name);
+  /* Begin reading a numbered section with optional name.  */
+  bool begin (location_t loc, elf_in *src, unsigned, const char * = NULL);
+  /* Complete reading a buffer.  Propagate errors and return true on
+     success.  */
+  bool end (elf_in *src);
+  /* Return true if there is unread data.  */
+  bool more_p () const
+  {
+    return pos != size;
+  }
+
+public:
+  /* Start reading at OFFSET.  */
+  void random_access (unsigned offset)
+  {
+    if (offset > size)
+      set_overrun ();
+    pos = offset;
+    bit_pos = bit_val = 0;
+  }
+
+public:
+  void align (unsigned boundary)
+  {
+    if (unsigned pad = pos & (boundary - 1))
+      read (boundary - pad);
+  }
+
+public:
+  const char *read (unsigned count)
+  {
+    char *ptr = use (count);
+    if (!ptr)
+      set_overrun ();
+    return ptr;
+  }
+
+public:
+  bool check_crc () const;
+  /* We store the CRC in the first 4 bytes, using host endianness.  */
+  unsigned get_crc () const
+  {
+    return *(const unsigned *)&buffer[0];
+  }
+
+public:
+  /* Manipulate the overrun flag.  */
+  bool get_overrun () const
+  {
+    return overrun;
+  }
+  void set_overrun ()
+  {
+    overrun = true;
+  }
+
+public:
+  unsigned u32 ();  	/* Read uncompressed integer.  */
+
+public:
+  bool b ();	    	/* Read a bool.  */
+  void bflush ();	/* Completed a block of bools.  */
+
+private:
+  void bfill ();	/* Get the next block of bools.  */
+
+public:
+  int c ();		/* Read a char.  */
+  int i ();		/* Read a signed int.  */
+  unsigned u ();	/* Read an unsigned int.  */
+  size_t z ();		/* Read a size_t.  */
+  HOST_WIDE_INT wi ();  /* Read a HOST_WIDE_INT.  */
+  unsigned HOST_WIDE_INT wu (); /* Read an unsigned HOST_WIDE_INT.  */
+  const char *str (size_t * = NULL); /* Read a string.  */
+  const void *buf (size_t); /* Read a fixed-length buffer.  */
+  cpp_hashnode *cpp_node (); /* Read a cpp node.  */
+};
+
+/* Verify the buffer's CRC is correct.  */
+
+bool
+bytes_in::check_crc () const
+{
+  if (size < 4)
+    return false;
+
+  unsigned c_crc = calc_crc (size);
+  if (c_crc != get_crc ())
+    return false;
+
+  return true;
+}
+
+class elf_out;
+
+/* Byte stream writer.  */
+
+class bytes_out : public bytes {
+  typedef bytes parent;
+
+public:
+  allocator *memory;	/* Obtainer of memory.  */
+  
+public:
+  bytes_out (allocator *memory)
+    : parent (), memory (memory)
+  {
+  }
+  ~bytes_out ()
+  {
+  }
+
+public:
+  bool streaming_p () const
+  {
+    return memory != NULL;
+  }
+
+public:
+  void set_crc (unsigned *crc_ptr);
+
+public:
+  /* Begin writing, maybe reserve space for CRC.  */
+  void begin (bool need_crc = true);
+  /* Finish writing.  Spill to section by number.  */
+  unsigned end (elf_out *, unsigned, unsigned *crc_ptr = NULL);
+
+public:
+  void align (unsigned boundary)
+  {
+    if (unsigned pad = pos & (boundary - 1))
+      write (boundary - pad);
+  }
+  
+public:
+  char *write (unsigned count, bool exact = false)
+  {
+    if (size < pos + count)
+      memory->grow (*this, pos + count, exact);
+    return use (count);
+  }
+
+public:
+  void u32 (unsigned);  /* Write uncompressed integer.  */
+
+public:
+  void b (bool);	/* Write bool.  */
+  void bflush ();	/* Finish block of bools.  */
+
+public:
+  void c (unsigned char); /* Write unsigned char.  */
+  void i (int);		/* Write signed int.  */
+  void u (unsigned);	/* Write unsigned int.  */
+  void z (size_t s);	/* Write size_t.  */
+  void wi (HOST_WIDE_INT); /* Write HOST_WIDE_INT.  */
+  void wu (unsigned HOST_WIDE_INT);  /* Write unsigned HOST_WIDE_INT.  */
+  void str (const char *ptr)
+  {
+    str (ptr, strlen (ptr));
+  }
+  void cpp_node (const cpp_hashnode *node)
+  {
+    str ((const char *)NODE_NAME (node), NODE_LEN (node));
+  }
+  void str (const char *, size_t);  /* Write string of known length.  */
+  void buf (const void *, size_t);  /* Write fixed length buffer.  */
+  void *buf (size_t); /* Create a writable buffer */
+
+public:
+  /* Format a NUL-terminated raw string.  */
+  void printf (const char *, ...) ATTRIBUTE_PRINTF_2;
+  void print_time (const char *, const tm *, const char *);
+
+public:
+  /* Dump instrumentation.  */
+  static void instrument ();
+
+protected:
+  /* Instrumentation.  */
+  static unsigned spans[4];
+  static unsigned lengths[4];
+  static int is_set;
+};
+
+/* Instrumentation.  */
+unsigned bytes_out::spans[4];
+unsigned bytes_out::lengths[4];
+int bytes_out::is_set = -1;
+
+/* If CRC_PTR non-null, set the CRC of the buffer.  Mix the CRC into
+   that pointed to by CRC_PTR.  */
+
+void
+bytes_out::set_crc (unsigned *crc_ptr)
+{
+  if (crc_ptr)
+    {
+      gcc_checking_assert (pos >= 4);
+
+      unsigned crc = calc_crc (pos);
+      unsigned accum = *crc_ptr;
+      /* Only mix the existing *CRC_PTR if it is non-zero.  */
+      accum = accum ? crc32_unsigned (accum, crc) : crc;
+      *crc_ptr = accum;
+
+      /* Buffer will be sufficiently aligned.  */
+      *(unsigned *)buffer = crc;
+    }
+}
+
+/* Finish a set of bools.  */
+
+void
+bytes_out::bflush ()
+{
+  if (bit_pos)
+    {
+      u32 (bit_val);
+      lengths[2] += bit_flush ();
+    }
+  spans[2]++;
+  is_set = -1;
+}
+
+void
+bytes_in::bflush ()
+{
+  if (bit_pos)
+    bit_flush ();
+}
+
+/* When reading, we don't know how many bools we'll read in.  So read
+   4 bytes-worth, and then rewind when flushing if we didn't need them
+   all.  You can't have a block of bools closer than 4 bytes to the
+   end of the buffer.  */
+
+void
+bytes_in::bfill ()
+{
+  bit_val = u32 ();
+}
+
+/* Bools are packed into bytes.  You cannot mix bools and non-bools.
+   You must call bflush before emitting another type.  So batch your
+   bools.
+
+   It may be worth optimizing for most bools being zero.  Some kind of
+   run-length encoding?  */
+
+void
+bytes_out::b (bool x)
+{
+  if (is_set != x)
+    {
+      is_set = x;
+      spans[x]++;
+    }
+  lengths[x]++;
+  bit_val |= unsigned (x) << bit_pos++;
+  if (bit_pos == 32)
+    {
+      u32 (bit_val);
+      lengths[2] += bit_flush ();
+    }
+}
+
+bool
+bytes_in::b ()
+{
+  if (!bit_pos)
+    bfill ();
+  bool v = (bit_val >> bit_pos++) & 1;
+  if (bit_pos == 32)
+    bit_flush ();
+  return v;
+}
+
+/* Exactly 4 bytes.  Used internally for bool packing and a few other
+   places.  We can't simply use uint32_t because (a) alignment and
+   (b) we need little-endian for the bool streaming rewinding to make
+   sense.  */
+
+void
+bytes_out::u32 (unsigned val)
+{
+  if (char *ptr = write (4))
+    {
+      ptr[0] = val;
+      ptr[1] = val >> 8;
+      ptr[2] = val >> 16;
+      ptr[3] = val >> 24;
+    }
+}
+
+unsigned
+bytes_in::u32 ()
+{
+  unsigned val = 0;
+  if (const char *ptr = read (4))
+    {
+      val |= (unsigned char)ptr[0];
+      val |= (unsigned char)ptr[1] << 8;
+      val |= (unsigned char)ptr[2] << 16;
+      val |= (unsigned char)ptr[3] << 24;
+    }
+
+  return val;
+}
+
+/* Chars are unsigned and written as single bytes. */
+
+void
+bytes_out::c (unsigned char v)
+{
+  if (char *ptr = write (1))
+    *ptr = v;
+}
+
+int
+bytes_in::c ()
+{
+  int v = 0;
+  if (const char *ptr = read (1))
+    v = (unsigned char)ptr[0];
+  return v;
+}
+
+/* Ints 7-bit as a byte. Otherwise a 3bit count of following bytes in
+   big-endian form.  4 bits are in the first byte.  */
+
+void
+bytes_out::i (int v)
+{
+  if (char *ptr = write (1))
+    {
+      if (v <= 0x3f && v >= -0x40)
+	*ptr = v & 0x7f;
+      else
+	{
+	  unsigned bytes = 0;
+	  int probe;
+	  if (v >= 0)
+	    for (probe = v >> 8; probe > 0x7; probe >>= 8)
+	      bytes++;
+	  else
+	    for (probe = v >> 8; probe < -0x8; probe >>= 8)
+	      bytes++;
+	  *ptr = 0x80 | bytes << 4 | (probe & 0xf);
+	  if ((ptr = write (++bytes)))
+	    for (; bytes--; v >>= 8)
+	      ptr[bytes] = v & 0xff;
+	}
+    }
+}
+
+int
+bytes_in::i ()
+{
+  int v = 0;
+  if (const char *ptr = read (1))
+    {
+      v = *ptr & 0xff;
+      if (v & 0x80)
+	{
+	  unsigned bytes = (v >> 4) & 0x7;
+	  v &= 0xf;
+	  if (v & 0x8)
+	    v |= -1 ^ 0x7;
+	  if ((ptr = read (++bytes)))
+	    while (bytes--)
+	      v = (v << 8) | (*ptr++ & 0xff);
+	}
+      else if (v & 0x40)
+	v |= -1 ^ 0x3f;
+    }
+
+  return v;
+}
+
+void
+bytes_out::u (unsigned v)
+{
+  if (char *ptr = write (1))
+    {
+      if (v <= 0x7f)
+	*ptr = v;
+      else
+	{
+	  unsigned bytes = 0;
+	  unsigned probe;
+	  for (probe = v >> 8; probe > 0xf; probe >>= 8)
+	    bytes++;
+	  *ptr = 0x80 | bytes << 4 | probe;
+	  if ((ptr = write (++bytes)))
+	    for (; bytes--; v >>= 8)
+	      ptr[bytes] = v & 0xff;
+	}
+    }
+}
+
+unsigned
+bytes_in::u ()
+{
+  unsigned v = 0;
+
+  if (const char *ptr = read (1))
+    {
+      v = *ptr & 0xff;
+      if (v & 0x80)
+	{
+	  unsigned bytes = (v >> 4) & 0x7;
+	  v &= 0xf;
+	  if ((ptr = read (++bytes)))
+	    while (bytes--)
+	      v = (v << 8) | (*ptr++ & 0xff);
+	}
+    }
+
+  return v;
+}
+
+void
+bytes_out::wi (HOST_WIDE_INT v)
+{
+  if (char *ptr = write (1))
+    {
+      if (v <= 0x3f && v >= -0x40)
+	*ptr = v & 0x7f;
+      else
+	{
+	  unsigned bytes = 0;
+	  HOST_WIDE_INT probe;
+	  if (v >= 0)
+	    for (probe = v >> 8; probe > 0x7; probe >>= 8)
+	      bytes++;
+	  else
+	    for (probe = v >> 8; probe < -0x8; probe >>= 8)
+	      bytes++;
+	  *ptr = 0x80 | bytes << 4 | (probe & 0xf);
+	  if ((ptr = write (++bytes)))
+	    for (; bytes--; v >>= 8)
+	      ptr[bytes] = v & 0xff;
+	}
+    }
+}
+
+HOST_WIDE_INT
+bytes_in::wi ()
+{
+  HOST_WIDE_INT v = 0;
+  if (const char *ptr = read (1))
+    {
+      v = *ptr & 0xff;
+      if (v & 0x80)
+	{
+	  unsigned bytes = (v >> 4) & 0x7;
+	  v &= 0xf;
+	  if (v & 0x8)
+	    v |= -1 ^ 0x7;
+	  if ((ptr = read (++bytes)))
+	    while (bytes--)
+	      v = (v << 8) | (*ptr++ & 0xff);
+	}
+      else if (v & 0x40)
+	v |= -1 ^ 0x3f;
+    }
+
+  return v;
+}
+
+/* unsigned wide ints are just written as signed wide ints.  */
+
+inline void
+bytes_out::wu (unsigned HOST_WIDE_INT v)
+{
+  wi ((HOST_WIDE_INT) v);
+}
+
+inline unsigned HOST_WIDE_INT
+bytes_in::wu ()
+{
+  return (unsigned HOST_WIDE_INT) wi ();
+}
+
+/* size_t written as unsigned or unsigned wide int.  */
+
+inline void
+bytes_out::z (size_t s)
+{
+  if (sizeof (s) == sizeof (unsigned))
+    u (s);
+  else
+    wu (s);
+}
+
+inline size_t
+bytes_in::z ()
+{
+  if (sizeof (size_t) == sizeof (unsigned))
+    return u ();
+  else
+    return wu ();
+}
+
+/* Buffer simply memcpied.  */
+void *
+bytes_out::buf (size_t len)
+{
+  align (sizeof (void *) * 2);
+  return write (len);
+}
+
+void
+bytes_out::buf (const void *src, size_t len)
+{
+  if (void *ptr = buf (len))
+    memcpy (ptr, src, len);
+}
+
+const void *
+bytes_in::buf (size_t len)
+{
+  align (sizeof (void *) * 2);
+  const char *ptr = read (len);
+
+  return ptr;
+}
+
+/* strings as an size_t length, followed by the buffer.  Make sure
+   there's a NUL terminator on read.  */
+
+void
+bytes_out::str (const char *string, size_t len)
+{
+  z (len);
+  if (len)
+    {
+      gcc_checking_assert (!string[len]);
+      buf (string, len + 1);
+    }
+}
+
+const char *
+bytes_in::str (size_t *len_p)
+{
+  size_t len = z ();
+
+  /* We're about to trust some user data.  */
+  if (overrun)
+    len = 0;
+  if (len_p)
+    *len_p = len;
+  const char *str = NULL;
+  if (len)
+    {
+      str = reinterpret_cast<const char *> (buf (len + 1));
+      if (!str || str[len])
+	{
+	  set_overrun ();
+	  str = NULL;
+	}
+    }
+  return str ? str : "";
+}
+
+cpp_hashnode *
+bytes_in::cpp_node ()
+{
+  size_t len;
+  const char *s = str (&len);
+  if (!len)
+    return NULL;
+  return ::cpp_node (get_identifier_with_length (s, len));
+}
+
+/* Format a string directly to the buffer, including a terminating
+   NUL.  Intended for human consumption.  */
+
+void
+bytes_out::printf (const char *format, ...)
+{
+  va_list args;
+  /* Exercise buffer expansion.  */
+  size_t len = EXPERIMENT (10, 500);
+
+  while (char *ptr = write (len))
+    {
+      va_start (args, format);
+      size_t actual = vsnprintf (ptr, len, format, args) + 1;
+      va_end (args);
+      if (actual <= len)
+	{
+	  unuse (len - actual);
+	  break;
+	}
+      unuse (len);
+      len = actual;
+    }
+}
+
+void
+bytes_out::print_time (const char *kind, const tm *time, const char *tz)
+{
+  printf ("%stime: %4u/%02u/%02u %02u:%02u:%02u %s",
+	  kind, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
+	  time->tm_hour, time->tm_min, time->tm_sec, tz);
+}
+
+/* Encapsulated Lazy Records Of Named Declarations.
+   Header: Stunningly Elf32_Ehdr-like
+   Sections: Sectional data
+     [1-N) : User data sections
+     N .strtab  : strings, stunningly ELF STRTAB-like
+   Index: Section table, stunningly ELF32_Shdr-like.   */
+
+class elf {
+protected:
+  /* Constants used within the format.  */
+  enum private_constants {
+    /* File kind. */
+    ET_NONE = 0,
+    EM_NONE = 0,
+    OSABI_NONE = 0,
+
+    /* File format. */
+    EV_CURRENT = 1,
+    CLASS32 = 1,
+    DATA2LSB = 1,
+    DATA2MSB = 2,
+
+    /* Section numbering.  */
+    SHN_UNDEF = 0,
+    SHN_LORESERVE = 0xff00,
+    SHN_XINDEX = 0xffff,
+
+    /* Section types.  */
+    SHT_NONE = 0,	/* No contents.  */
+    SHT_PROGBITS = 1, /* Random bytes.  */
+    SHT_STRTAB = 3,	/* A string table.  */
+
+    /* Section flags.  */
+    SHF_NONE = 0x00,	/* Nothing.  */
+    SHF_STRINGS = 0x20,  /* NUL-Terminated strings.  */
+
+    /* I really hope we do not get CMI files larger than 4GB.  */
+    MY_CLASS = CLASS32,
+    /* It is host endianness that is relevant.  */
+    MY_ENDIAN = DATA2LSB
+#ifdef WORDS_BIGENDIAN
+    ^ DATA2LSB ^ DATA2MSB
+#endif
+  };
+
+public:
+  /* Constants visible to users.  */
+  enum public_constants {
+    /* Special error codes.  Breaking layering a bit.  */
+    E_BAD_DATA = -1,  /* Random unexpected data errors.  */
+    E_BAD_LAZY = -2,  /* Badly ordered laziness.  */
+    E_BAD_IMPORT = -3 /* A nested import failed.  */
+  };
+
+protected:
+  /* File identification.  On-disk representation.  */
+  struct ident {
+    uint8_t magic[4];	/* 0x7f, 'E', 'L', 'F' */
+    uint8_t klass;	/* 4:CLASS32 */
+    uint8_t data;	/* 5:DATA2[LM]SB */
+    uint8_t version;	/* 6:EV_CURRENT  */
+    uint8_t osabi;	/* 7:OSABI_NONE */
+    uint8_t abiver;	/* 8: 0 */
+    uint8_t pad[7];	/* 9-15 */
+  };
+  /* File header.  On-disk representation.  */
+  struct header {
+    struct ident ident;
+    uint16_t type;	/* ET_NONE */
+    uint16_t machine;	/* EM_NONE */
+    uint32_t version;	/* EV_CURRENT */
+    uint32_t entry;	/* 0 */
+    uint32_t phoff;	/* 0 */
+    uint32_t shoff;	/* Section Header Offset in file */
+    uint32_t flags; 
+    uint16_t ehsize;	/* ELROND Header SIZE -- sizeof (header) */
+    uint16_t phentsize; /* 0 */
+    uint16_t phnum;	/* 0 */
+    uint16_t shentsize; /* Section Header SIZE -- sizeof (section) */
+    uint16_t shnum;	/* Section Header NUM */
+    uint16_t shstrndx;	/* Section Header STRing iNDeX */
+  };
+  /* File section.  On-disk representation.  */
+  struct section {
+    uint32_t name;	/* String table offset.  */
+    uint32_t type;	/* SHT_* */
+    uint32_t flags;	/* SHF_* */
+    uint32_t addr;	/* 0 */
+    uint32_t offset;	/* OFFSET in file */
+    uint32_t size;	/* SIZE of section */
+    uint32_t link;	/* 0 */
+    uint32_t info;	/* 0 */
+    uint32_t addralign; /* 0 */
+    uint32_t entsize;	/* ENTry SIZE, usually 0 */
+  };
+
+protected:
+  data hdr;	/* The header.  */
+  data sectab; 	/* The section table.  */
+  data strtab;  /* String table.  */
+  int fd;   	/* File descriptor we're reading or writing.  */
+  int err; 	/* Sticky error code.  */
+
+public:
+  /* Construct from STREAM.  E is errno if STREAM NULL.  */
+  elf (int fd, int e)
+    :hdr (), sectab (), strtab (), fd (fd), err (fd >= 0 ? 0 : e)
+  {}
+  ~elf ()
+  {
+    gcc_checking_assert (fd < 0 && !hdr.buffer
+			 && !sectab.buffer && !strtab.buffer);
+  }
+
+public:
+  /* Return the error, if we have an error.  */
+  int get_error () const
+  {
+    return err;
+  }
+  /* Set the error, unless it's already been set.  */
+  void set_error (int e = E_BAD_DATA)
+  {
+    if (!err)
+      err = e;
+  }
+  /* Get an error string.  */
+  const char *get_error (const char *) const;
+
+public:
+  /* Begin reading/writing file.  Return false on error.  */
+  bool begin () const
+  {
+    return !get_error ();
+  }
+  /* Finish reading/writing file.  Return false on error.  */
+  bool end ();
+};
+
+/* Return error string.  */
+
+const char *
+elf::get_error (const char *name) const
+{
+  if (!name)
+    return "Unknown CMI mapping";
+
+  switch (err)
+    {
+    case 0:
+      gcc_unreachable ();
+    case E_BAD_DATA:
+      return "Bad file data";
+    case E_BAD_IMPORT:
+      return "Bad import dependency";
+    case E_BAD_LAZY:
+      return "Bad lazy ordering";
+    default:
+      return xstrerror (err);
+    }
+}
+
+/* Finish file, return true if there's an error.  */
+
+bool
+elf::end ()
+{
+  /* Close the stream and free the section table.  */
+  if (fd >= 0 && close (fd))
+    set_error (errno);
+  fd = -1;
+
+  return !get_error ();
+}
+
+/* ELROND reader.  */
+
+class elf_in : public elf {
+  typedef elf parent;
+
+private:
+  /* For freezing & defrosting.  */
+#if !defined (HOST_LACKS_INODE_NUMBERS)
+  dev_t device;
+  ino_t inode;
+#endif
+
+public:
+  elf_in (int fd, int e)
+    :parent (fd, e)
+  {
+  }
+  ~elf_in ()
+  {
+  }
+
+public:
+  bool is_frozen () const
+  {
+    return fd < 0 && hdr.pos;
+  }
+  bool is_freezable () const
+  {
+    return fd >= 0 && hdr.pos;
+  }
+  void freeze ();
+  bool defrost (const char *);
+
+  /* If BYTES is in the mmapped area, allocate a new buffer for it.  */
+  void preserve (bytes_in &bytes ATTRIBUTE_UNUSED)
+  {
+#if MAPPED_READING
+    if (hdr.buffer && bytes.buffer >= hdr.buffer
+	&& bytes.buffer < hdr.buffer + hdr.pos)
+      {
+	char *buf = bytes.buffer;
+	bytes.buffer = data::simple_memory.grow (NULL, bytes.size);
+	memcpy (bytes.buffer, buf, bytes.size);
+      }
+#endif
+  }
+  /* If BYTES is not in SELF's mmapped area, free it.  SELF might be
+     NULL. */
+  static void release (elf_in *self ATTRIBUTE_UNUSED, bytes_in &bytes)
+  {
+#if MAPPED_READING
+    if (!(self && self->hdr.buffer && bytes.buffer >= self->hdr.buffer
+	  && bytes.buffer < self->hdr.buffer + self->hdr.pos))
+#endif
+      data::simple_memory.shrink (bytes.buffer);
+    bytes.buffer = NULL;
+    bytes.size = 0;
+  }
+
+public:
+  static void grow (data &data, unsigned needed)
+  {
+    gcc_checking_assert (!data.buffer);
+#if !MAPPED_READING
+    data.buffer = XNEWVEC (char, needed);
+#endif
+    data.size = needed;
+  }
+  static void shrink (data &data)
+  {
+#if !MAPPED_READING
+    XDELETEVEC (data.buffer);
+#endif
+    data.buffer = NULL;
+    data.size = 0;
+  }
+
+public:
+  const section *get_section (unsigned s) const
+  {
+    if (s * sizeof (section) < sectab.size)
+      return reinterpret_cast<const section *>
+	(&sectab.buffer[s * sizeof (section)]);
+    else
+      return NULL;
+  }
+  unsigned get_section_limit () const
+  {
+    return sectab.size / sizeof (section);
+  }
+
+protected:
+  const char *read (data *, unsigned, unsigned);
+
+public:
+  /* Read section by number.  */
+  bool read (data *d, const section *s)
+  {
+    return s && read (d, s->offset, s->size);
+  }
+
+  /* Find section by name.  */
+  unsigned find (const char *name);
+  /* Find section by index.  */
+  const section *find (unsigned snum, unsigned type = SHT_PROGBITS);
+
+public:
+  /* Release the string table, when we're done with it.  */
+  void release ()
+  {
+    shrink (strtab);
+  }
+
+public:
+  bool begin (location_t);
+  bool end ()
+  {
+    release ();
+#if MAPPED_READING
+    if (hdr.buffer)
+      munmap (hdr.buffer, hdr.pos);
+    hdr.buffer = NULL;
+#endif
+    shrink (sectab);
+
+    return parent::end ();
+  }
+
+public:
+  /* Return string name at OFFSET.  Checks OFFSET range.  Always
+     returns non-NULL.  We know offset 0 is an empty string.  */
+  const char *name (unsigned offset)
+  {
+    return &strtab.buffer[offset < strtab.size ? offset : 0];
+  }
+};
+
+/* ELROND writer.  */
+
+class elf_out : public elf, public data::allocator {
+  typedef elf parent;
+  /* Desired section alignment on disk.  */
+  static const int SECTION_ALIGN = 16;
+
+private:
+  ptr_int_hash_map identtab;	/* Map of IDENTIFIERS to strtab offsets. */
+  unsigned pos;			/* Write position in file.  */
+#if MAPPED_WRITING
+  unsigned offset;		/* Offset of the mapping.  */
+  unsigned extent;		/* Length of mapping.  */
+  unsigned page_size;		/* System page size.  */
+#endif
+
+public:
+  elf_out (int fd, int e)
+    :parent (fd, e), identtab (500), pos (0)
+  {
+#if MAPPED_WRITING
+    offset = extent = 0;
+    page_size = sysconf (_SC_PAGE_SIZE);
+    if (page_size < SECTION_ALIGN)
+      /* Something really strange.  */
+      set_error (EINVAL);
+#endif
+  }
+  ~elf_out ()
+  {
+    data::simple_memory.shrink (hdr);
+    data::simple_memory.shrink (sectab);
+    data::simple_memory.shrink (strtab);
+  }
+
+#if MAPPED_WRITING
+private:
+  void create_mapping (unsigned ext, bool extending = true);
+  void remove_mapping ();
+#endif
+
+protected:
+  using allocator::grow;
+  virtual char *grow (char *, unsigned needed);
+#if MAPPED_WRITING
+  using allocator::shrink;
+  virtual void shrink (char *);
+#endif
+
+public:
+  unsigned get_section_limit () const
+  {
+    return sectab.pos / sizeof (section);
+  }
+
+protected:
+  unsigned add (unsigned type, unsigned name = 0,
+		unsigned off = 0, unsigned size = 0, unsigned flags = SHF_NONE);
+  unsigned write (const data &);
+#if MAPPED_WRITING
+  unsigned write (const bytes_out &);
+#endif
+
+public:
+  /* IDENTIFIER to strtab offset.  */
+  unsigned name (tree ident);
+  /* String literal to strtab offset.  */
+  unsigned name (const char *n);
+  /* Qualified name of DECL to strtab offset.  */
+  unsigned qualified_name (tree decl, bool is_defn);
+
+private:
+  unsigned strtab_write (const char *s, unsigned l);
+  void strtab_write (tree decl, int);
+
+public:
+  /* Add a section with contents or strings.  */
+  unsigned add (const bytes_out &, bool string_p, unsigned name);
+
+public:
+  /* Begin and end writing.  */
+  bool begin ();
+  bool end ();
+};
+
+/* Begin reading section NAME (of type PROGBITS) from SOURCE.
+   Data always checked for CRC.  */
+
+bool
+bytes_in::begin (location_t loc, elf_in *source, const char *name)
+{
+  unsigned snum = source->find (name);
+
+  return begin (loc, source, snum, name);
+}
+
+/* Begin reading section numbered SNUM with NAME (may be NULL).  */
+
+bool
+bytes_in::begin (location_t loc, elf_in *source, unsigned snum, const char *name)
+{
+  if (!source->read (this, source->find (snum))
+      || !size || !check_crc ())
+    {
+      source->set_error (elf::E_BAD_DATA);
+      source->shrink (*this);
+      if (name)
+	error_at (loc, "section %qs is missing or corrupted", name);
+      else
+	error_at (loc, "section #%u is missing or corrupted", snum);
+      return false;
+    }
+  pos = 4;
+  return true;
+}
+
+/* Finish reading a section.  */
+
+bool
+bytes_in::end (elf_in *src)
+{
+  if (more_p ())
+    set_overrun ();
+  if (overrun)
+    src->set_error ();
+
+  src->shrink (*this);
+
+  return !overrun;
+}
+
+/* Begin writing buffer.  */
+
+void
+bytes_out::begin (bool need_crc)
+{
+  if (need_crc)
+    pos = 4;
+  memory->grow (*this, 0, false);
+}
+
+/* Finish writing buffer.  Stream out to SINK as named section NAME.
+   Return section number or 0 on failure.  If CRC_PTR is true, crc
+   the data.  Otherwise it is a string section.  */
+
+unsigned
+bytes_out::end (elf_out *sink, unsigned name, unsigned *crc_ptr)
+{
+  lengths[3] += pos;
+  spans[3]++;
+
+  set_crc (crc_ptr);
+  unsigned sec_num = sink->add (*this, !crc_ptr, name);
+  memory->shrink (*this);
+
+  return sec_num;
+}
+
+/* Close and open the file, without destroying it.  */
+
+void
+elf_in::freeze ()
+{
+  gcc_checking_assert (!is_frozen ());
+#if MAPPED_READING
+  if (munmap (hdr.buffer, hdr.pos) < 0)
+    set_error (errno);
+#endif
+  if (close (fd) < 0)
+    set_error (errno);
+  fd = -1;
+}
+
+bool
+elf_in::defrost (const char *name)
+{
+  gcc_checking_assert (is_frozen ());
+  struct stat stat;
+
+  fd = open (name, O_RDONLY | O_CLOEXEC);
+  if (fd < 0 || fstat (fd, &stat) < 0)
+    set_error (errno);
+  else
+    {
+      bool ok = hdr.pos == unsigned (stat.st_size);
+#ifndef HOST_LACKS_INODE_NUMBERS
+      if (device != stat.st_dev
+	  || inode != stat.st_ino)
+	ok = false;
+#endif
+      if (!ok)
+	set_error (EMFILE);
+#if MAPPED_READING
+      if (ok)
+	{
+	  char *mapping = reinterpret_cast<char *>
+	    (mmap (NULL, hdr.pos, PROT_READ, MAP_SHARED, fd, 0));
+	  if (mapping == MAP_FAILED)
+	  fail:
+	      set_error (errno);
+	  else
+	    {
+	      if (madvise (mapping, hdr.pos, MADV_RANDOM))
+		goto fail;
+
+	      /* These buffers are never NULL in this case.  */
+	      strtab.buffer = mapping + strtab.pos;
+	      sectab.buffer = mapping + sectab.pos;
+	      hdr.buffer = mapping;
+	    }
+	}
+#endif
+    }
+
+  return !get_error ();
+}
+
+/* Read at current position into BUFFER.  Return true on success.  */
+
+const char *
+elf_in::read (data *data, unsigned pos, unsigned length)
+{
+#if MAPPED_READING
+  if (pos + length > hdr.pos)
+    {
+      set_error (EINVAL);
+      return NULL;
+    }
+#else
+  if (pos != ~0u && lseek (fd, pos, SEEK_SET) < 0)
+    {
+      set_error (errno);
+      return NULL;
+    }
+#endif
+  grow (*data, length);
+#if MAPPED_READING  
+  data->buffer = hdr.buffer + pos;
+#else
+  if (::read (fd, data->buffer, data->size) != length)
+    {
+      set_error (errno);
+      shrink (*data);
+      return NULL;
+    }
+#endif
+
+  return data->buffer;
+}
+
+/* Read section SNUM of TYPE.  Return section pointer or NULL on error.  */
+
+const elf::section *
+elf_in::find (unsigned snum, unsigned type)
+{
+  const section *sec = get_section (snum);
+  if (!snum || !sec || sec->type != type)
+    return NULL;
+  return sec;
+}
+
+/* Find a section NAME and TYPE.  Return section number, or zero on
+   failure.  */
+
+unsigned
+elf_in::find (const char *sname)
+{
+  for (unsigned pos = sectab.size; pos -= sizeof (section); )
+    {
+      const section *sec
+	= reinterpret_cast<const section *> (&sectab.buffer[pos]);
+
+      if (0 == strcmp (sname, name (sec->name)))
+	return pos / sizeof (section);
+    }
+
+  return 0;
+}
+
+/* Begin reading file.  Verify header.  Pull in section and string
+   tables.  Return true on success.  */
+
+bool
+elf_in::begin (location_t loc)
+{
+  if (!parent::begin ())
+    return false;
+
+  struct stat stat;
+  unsigned size = 0;
+  if (!fstat (fd, &stat))
+    {
+#if !defined (HOST_LACKS_INODE_NUMBERS)
+      device = stat.st_dev;
+      inode = stat.st_ino;
+#endif
+      /* Never generate files > 4GB, check we've not been given one.  */
+      if (stat.st_size == unsigned (stat.st_size))
+	size = unsigned (stat.st_size);
+    }
+
+#if MAPPED_READING
+  /* MAP_SHARED so that the file is backing store.  If someone else
+     concurrently writes it, they're wrong.  */
+  void *mapping = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+  if (mapping == MAP_FAILED)
+    {
+    fail:
+      set_error (errno);
+      return false;
+    }
+  /* We'll be hopping over this randomly.  Some systems declare the
+     first parm as char *, and other declare it as void *.  */
+  if (madvise (reinterpret_cast <char *> (mapping), size, MADV_RANDOM))
+    goto fail;
+
+  hdr.buffer = (char *)mapping;
+#else
+  read (&hdr, 0, sizeof (header));
+#endif
+  hdr.pos = size; /* Record size of the file.  */
+
+  const header *h = reinterpret_cast<const header *> (hdr.buffer);
+  if (!h)
+    return false;
+
+  if (h->ident.magic[0] != 0x7f
+      || h->ident.magic[1] != 'E'
+      || h->ident.magic[2] != 'L'
+      || h->ident.magic[3] != 'F')
+    {
+      error_at (loc, "not Encapsulated Lazy Records of Named Declarations");
+    failed:
+      shrink (hdr);
+      return false;
+    }
+
+  /* We expect a particular format -- the ELF is not intended to be
+     distributable.  */
+  if (h->ident.klass != MY_CLASS
+      || h->ident.data != MY_ENDIAN
+      || h->ident.version != EV_CURRENT
+      || h->type != ET_NONE
+      || h->machine != EM_NONE
+      || h->ident.osabi != OSABI_NONE)
+    {
+      error_at (loc, "unexpected encapsulation format or type");
+      goto failed;
+    }
+
+  int e = -1;
+  if (!h->shoff || h->shentsize != sizeof (section))
+    {
+    malformed:
+      set_error (e);
+      error_at (loc, "encapsulation is malformed");
+      goto failed;
+    }
+
+  unsigned strndx = h->shstrndx;
+  unsigned shnum = h->shnum;
+  if (shnum == SHN_XINDEX)
+    {
+      if (!read (&sectab, h->shoff, sizeof (section)))
+	{
+	section_table_fail:
+	  e = errno;
+	  goto malformed;
+	}
+      shnum = get_section (0)->size;
+      /* Freeing does mean we'll re-read it in the case we're not
+	 mapping, but this is going to be rare.  */
+      shrink (sectab);
+    }
+
+  if (!shnum)
+    goto malformed;
+
+  if (!read (&sectab, h->shoff, shnum * sizeof (section)))
+    goto section_table_fail;
+
+  if (strndx == SHN_XINDEX)
+    strndx = get_section (0)->link;
+
+  if (!read (&strtab, find (strndx, SHT_STRTAB)))
+    goto malformed;
+
+  /* The string table should be at least one byte, with NUL chars
+     at either end.  */
+  if (!(strtab.size && !strtab.buffer[0]
+	&& !strtab.buffer[strtab.size - 1]))
+    goto malformed;
+
+#if MAPPED_READING
+  /* Record the offsets of the section and string tables.  */
+  sectab.pos = h->shoff;
+  strtab.pos = shnum * sizeof (section);
+#else
+  shrink (hdr);
+#endif
+
+  return true;
+}
+
+/* Create a new mapping.  */
+
+#if MAPPED_WRITING
+void
+elf_out::create_mapping (unsigned ext, bool extending)
+{
+#ifndef HAVE_POSIX_FALLOCATE
+#define posix_fallocate(fd,off,len) ftruncate (fd, off + len)
+#endif
+  void *mapping = MAP_FAILED;
+  if (extending && ext < 1024 * 1024)
+    {
+      if (!posix_fallocate (fd, offset, ext * 2))
+	mapping = mmap (NULL, ext * 2, PROT_READ | PROT_WRITE,
+			MAP_SHARED, fd, offset);
+      if (mapping != MAP_FAILED)
+	ext *= 2;
+    }
+  if (mapping == MAP_FAILED)
+    {
+      if (!extending || !posix_fallocate (fd, offset, ext))
+	mapping = mmap (NULL, ext, PROT_READ | PROT_WRITE,
+			MAP_SHARED, fd, offset);
+      if (mapping == MAP_FAILED)
+	{
+	  set_error (errno);
+	  mapping = NULL;
+	  ext = 0;
+	}
+    }
+#undef posix_fallocate
+  hdr.buffer = (char *)mapping;
+  extent = ext;
+}
+#endif
+
+/* Flush out the current mapping.  */
+
+#if MAPPED_WRITING
+void
+elf_out::remove_mapping ()
+{
+  if (hdr.buffer)
+    {
+      /* MS_ASYNC dtrt with the removed mapping, including a
+	 subsequent overlapping remap.  */
+      if (msync (hdr.buffer, extent, MS_ASYNC)
+	  || munmap (hdr.buffer, extent))
+	/* We're somewhat screwed at this point.  */
+	set_error (errno);
+    }
+
+  hdr.buffer = NULL;
+}
+#endif
+
+/* Grow a mapping of PTR to be NEEDED bytes long.  This gets
+   interesting if the new size grows the EXTENT.  */
+
+char *
+elf_out::grow (char *data, unsigned needed)
+{
+  if (!data)
+    {
+      /* First allocation, check we're aligned.  */
+      gcc_checking_assert (!(pos & (SECTION_ALIGN - 1)));
+#if MAPPED_WRITING
+      data = hdr.buffer + (pos - offset);
+#endif
+    }
+
+#if MAPPED_WRITING
+  unsigned off = data - hdr.buffer;
+  if (off + needed > extent)
+    {
+      /* We need to grow the mapping.  */
+      unsigned lwm = off & ~(page_size - 1);
+      unsigned hwm = (off + needed + page_size - 1) & ~(page_size - 1);
+
+      gcc_checking_assert (hwm > extent);
+
+      remove_mapping ();
+
+      offset += lwm;
+      create_mapping (extent < hwm - lwm ? hwm - lwm : extent);
+
+      data = hdr.buffer + (off - lwm);
+    }
+#else
+  data = allocator::grow (data, needed);
+#endif
+
+  return data;
+}
+
+#if MAPPED_WRITING
+/* Shrinking is a NOP.  */
+void
+elf_out::shrink (char *)
+{
+}
+#endif
+
+/* Write S of length L to the strtab buffer.  L must include the ending
+   NUL, if that's what you want.  */
+
+unsigned
+elf_out::strtab_write (const char *s, unsigned l)
+{
+  if (strtab.pos + l > strtab.size)
+    data::simple_memory.grow (strtab, strtab.pos + l, false);
+  memcpy (strtab.buffer + strtab.pos, s, l);
+  unsigned res = strtab.pos;
+  strtab.pos += l;
+  return res;
+}
+
+/* Write qualified name of decl.  INNER >0 if this is a definition, <0
+   if this is a qualifier of an outer name.  */
+
+void
+elf_out::strtab_write (tree decl, int inner)
+{
+  tree ctx = CP_DECL_CONTEXT (decl);
+  if (TYPE_P (ctx))
+    ctx = TYPE_NAME (ctx);
+  if (ctx != global_namespace)
+    strtab_write (ctx, -1);
+
+  tree name = DECL_NAME (decl);
+  if (!name)
+    name = DECL_ASSEMBLER_NAME_RAW (decl);
+  strtab_write (IDENTIFIER_POINTER (name), IDENTIFIER_LENGTH (name));
+
+  if (inner)
+    strtab_write (&"::{}"[inner+1], 2);
+}
+
+/* Map IDENTIFIER IDENT to strtab offset.  Inserts into strtab if not
+   already there.  */
+
+unsigned
+elf_out::name (tree ident)
+{
+  unsigned res = 0;
+  if (ident)
+    {
+      bool existed;
+      int *slot = &identtab.get_or_insert (ident, &existed);
+      if (!existed)
+	*slot = strtab_write (IDENTIFIER_POINTER (ident),
+			      IDENTIFIER_LENGTH (ident) + 1);
+      res = *slot;
+    }
+  return res;
+}
+
+/* Map LITERAL to strtab offset.  Does not detect duplicates and
+   expects LITERAL to remain live until strtab is written out.  */
+
+unsigned
+elf_out::name (const char *literal)
+{
+  return strtab_write (literal, strlen (literal) + 1);
+}
+
+/* Map a DECL's qualified name to strtab offset.  Does not detect
+   duplicates.  */
+
+unsigned
+elf_out::qualified_name (tree decl, bool is_defn)
+{
+  gcc_checking_assert (DECL_P (decl) && decl != global_namespace);
+  unsigned result = strtab.pos;
+
+  strtab_write (decl, is_defn);
+  strtab_write ("", 1);
+
+  return result;
+}
+
+/* Add section to file.  Return section number.  TYPE & NAME identify
+   the section.  OFF and SIZE identify the file location of its
+   data.  FLAGS contains additional info.  */
+
+unsigned
+elf_out::add (unsigned type, unsigned name, unsigned off, unsigned size,
+	      unsigned flags)
+{
+  gcc_checking_assert (!(off & (SECTION_ALIGN - 1)));
+  if (sectab.pos + sizeof (section) > sectab.size)
+    data::simple_memory.grow (sectab, sectab.pos + sizeof (section), false);
+  section *sec = reinterpret_cast<section *> (sectab.buffer + sectab.pos);
+  memset (sec, 0, sizeof (section));
+  sec->type = type;
+  sec->flags = flags;
+  sec->name = name;
+  sec->offset = off;
+  sec->size = size;
+  if (flags & SHF_STRINGS)
+    sec->entsize = 1;
+
+  unsigned res = sectab.pos;
+  sectab.pos += sizeof (section);
+  return res / sizeof (section);
+}
+
+/* Pad to the next alignment boundary, then write BUFFER to disk.
+   Return the position of the start of the write, or zero on failure.   */
+
+unsigned
+elf_out::write (const data &buffer)
+{
+#if MAPPED_WRITING
+  /* HDR is always mapped.  */
+  if (&buffer != &hdr)
+    {
+      bytes_out out (this);
+      grow (out, buffer.pos, true);
+      if (out.buffer)
+	memcpy (out.buffer, buffer.buffer, buffer.pos);
+      shrink (out);
+    }
+  else
+    /* We should have been aligned during the first allocation.  */
+    gcc_checking_assert (!(pos & (SECTION_ALIGN - 1)));
+#else
+  if (::write (fd, buffer.buffer, buffer.pos) != buffer.pos)
+    {
+      set_error (errno);
+      return 0;
+    }
+#endif
+  unsigned res = pos;
+  pos += buffer.pos;
+
+  if (unsigned padding = -pos & (SECTION_ALIGN - 1))
+    {
+#if !MAPPED_WRITING
+      /* Align the section on disk, should help the necessary copies.
+	 fseeking to extend is non-portable.  */
+      static char zero[SECTION_ALIGN];
+      if (::write (fd, &zero, padding) != padding)
+	set_error (errno);
+#endif
+      pos += padding;
+    }
+  return res;
+}
+
+/* Write a streaming buffer.  It must be using us as an allocator.  */
+
+#if MAPPED_WRITING
+unsigned
+elf_out::write (const bytes_out &buf)
+{
+  gcc_checking_assert (buf.memory == this);
+  /* A directly mapped buffer.  */
+  gcc_checking_assert (buf.buffer - hdr.buffer >= 0
+		       && buf.buffer - hdr.buffer + buf.size <= extent);
+  unsigned res = pos;
+  pos += buf.pos;
+
+  /* Align up.  We're not going to advance into the next page. */
+  pos += -pos & (SECTION_ALIGN - 1);
+
+  return res;
+}
+#endif
+
+/* Write data and add section.  STRING_P is true for a string
+   section, false for PROGBITS.  NAME identifies the section (0 is the
+   empty name).  DATA is the contents.  Return section number or 0 on
+   failure (0 is the undef section).  */
+
+unsigned
+elf_out::add (const bytes_out &data, bool string_p, unsigned name)
+{
+  unsigned off = write (data);
+
+  return add (string_p ? SHT_STRTAB : SHT_PROGBITS, name,
+	      off, data.pos, string_p ? SHF_STRINGS : SHF_NONE);
+}
+
+/* Begin writing the file.  Initialize the section table and write an
+   empty header.  Return false on failure.  */
+
+bool
+elf_out::begin ()
+{
+  if (!parent::begin ())
+    return false;
+
+  /* Let the allocators pick a default.  */
+  data::simple_memory.grow (strtab, 0, false);
+  data::simple_memory.grow (sectab, 0, false);
+
+  /* The string table starts with an empty string.  */
+  name ("");
+
+  /* Create the UNDEF section.  */
+  add (SHT_NONE);
+
+#if MAPPED_WRITING
+  /* Start a mapping.  */
+  create_mapping (EXPERIMENT (page_size,
+			      (32767 + page_size) & ~(page_size - 1)));
+  if (!hdr.buffer)
+    return false;
+#endif
+
+  /* Write an empty header.  */
+  grow (hdr, sizeof (header), true);
+  header *h = reinterpret_cast<header *> (hdr.buffer);
+  memset (h, 0, sizeof (header));
+  hdr.pos = hdr.size;
+  write (hdr);
+  return !get_error ();
+}
+
+/* Finish writing the file.  Write out the string & section tables.
+   Fill in the header.  Return true on error.  */
+
+bool
+elf_out::end ()
+{
+  if (fd >= 0)
+    {
+      /* Write the string table.  */
+      unsigned strnam = name (".strtab");
+      unsigned stroff = write (strtab);
+      unsigned strndx = add (SHT_STRTAB, strnam, stroff, strtab.pos,
+			     SHF_STRINGS);
+
+      /* Store escape values in section[0].  */
+      if (strndx >= SHN_LORESERVE)
+	{
+	  reinterpret_cast<section *> (sectab.buffer)->link = strndx;
+	  strndx = SHN_XINDEX;
+	}
+      unsigned shnum = sectab.pos / sizeof (section);
+      if (shnum >= SHN_LORESERVE)
+	{
+	  reinterpret_cast<section *> (sectab.buffer)->size = shnum;
+	  shnum = SHN_XINDEX;
+	}
+
+      unsigned shoff = write (sectab);
+
+#if MAPPED_WRITING
+      if (offset)
+	{
+	  remove_mapping ();
+	  offset = 0;
+	  create_mapping ((sizeof (header) + page_size - 1) & ~(page_size - 1),
+			  false);
+	}
+      unsigned length = pos;
+#else
+      if (lseek (fd, 0, SEEK_SET) < 0)
+	set_error (errno);
+#endif
+      /* Write header.  */
+      if (!get_error ())
+	{
+	  /* Write the correct header now.  */
+	  header *h = reinterpret_cast<header *> (hdr.buffer);
+	  h->ident.magic[0] = 0x7f;
+	  h->ident.magic[1] = 'E';	/* Elrond */
+	  h->ident.magic[2] = 'L';	/* is an */
+	  h->ident.magic[3] = 'F';	/* elf.  */
+	  h->ident.klass = MY_CLASS;
+	  h->ident.data =  MY_ENDIAN;
+	  h->ident.version = EV_CURRENT;
+	  h->ident.osabi = OSABI_NONE;
+	  h->type = ET_NONE;
+	  h->machine = EM_NONE;
+	  h->version = EV_CURRENT;
+	  h->shoff = shoff;
+	  h->ehsize = sizeof (header);
+	  h->shentsize = sizeof (section);
+	  h->shnum = shnum;
+	  h->shstrndx = strndx;
+
+	  pos = 0;
+	  write (hdr);
+	}
+
+#if MAPPED_WRITING
+      remove_mapping ();
+      if (ftruncate (fd, length))
+	set_error (errno);
+#endif
+    }
+
+  data::simple_memory.shrink (sectab);
+  data::simple_memory.shrink (strtab);
+
+  return parent::end ();
+}
+
+/********************************************************************/
+
+/* A dependency set.  This is used during stream out to determine the
+   connectivity of the graph.  Every namespace-scope declaration that
+   needs writing has a depset.  The depset is filled with the (depsets
+   of) declarations within this module that it references.  For a
+   declaration that'll generally be named types.  For definitions
+   it'll also be declarations in the body.
+
+   From that we can convert the graph to a DAG, via determining the
+   Strongly Connected Clusters.  Each cluster is streamed
+   independently, and thus we achieve lazy loading.
+
+   Other decls that get a depset are namespaces themselves and
+   unnameable declarations.   */
+
+class depset {
+private:
+  tree entity;  /* Entity, or containing namespace.  */
+  uintptr_t discriminator;  /* Flags or identifier.  */
+
+public:
+  /* The kinds of entity the depset could describe.  The ordering is
+     significant, see entity_kind_name.  */
+  enum entity_kind
+  {
+    EK_DECL,		/* A decl.  */
+    EK_SPECIALIZATION,  /* A specialization.  */
+    EK_USING,		/* A using declaration (at namespace scope).  */
+    EK_NAMESPACE,	/* A namespace.  */
+    EK_REDIRECT,	/* Redirect to a template_decl.  */
+    EK_EXPLICIT_HWM,  
+    EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded.  */
+    EK_FOR_BINDING,	/* A decl being inserted for a binding.  */
+    EK_INNER_DECL,	/* A decl defined outside of it's imported
+			   context.  */
+
+    EK_BITS = 3		/* Only need to encode below EK_EXPLICIT_HWM.  */
+  };
+
+private:
+  /* Placement of bit fields in discriminator.  */
+  enum disc_bits 
+  {
+    DB_ZERO_BIT, /* Set to disambiguate identifier from flags  */
+    DB_SPECIAL_BIT, /* First dep slot is special.  */
+    DB_KIND_BIT, /* Kind of the entity.  */
+    DB_KIND_BITS = EK_BITS,
+    DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
+    DB_IS_MEMBER_BIT,		/* Is an out-of-class member.  */
+    DB_IS_INTERNAL_BIT,		/* It is an (erroneous)
+				   internal-linkage entity.  */
+    DB_REFS_INTERNAL_BIT,	/* Refers to an internal-linkage
+				   entity. */
+    DB_IMPORTED_BIT,		/* An imported entity.  */
+    DB_UNREACHED_BIT,		/* A yet-to-be reached entity.  */
+    DB_HIDDEN_BIT,		/* A hidden binding.  */
+    /* The following bits are not independent, but enumerating them is
+       awkward.  */
+    DB_ALIAS_TMPL_INST_BIT,	/* An alias template instantiation. */
+    DB_PARTIAL_BIT,		/* A partial instantiation or
+				   specialization.  */
+    DB_ALIAS_SPEC_BIT,		/* Specialization of an alias template
+				   (in both spec tables).  */
+    DB_TYPE_SPEC_BIT,		/* Specialization in the type table.
+				   */
+    DB_FRIEND_SPEC_BIT,		/* An instantiated template friend.  */
+  };
+
+public:
+  /* The first slot is special for EK_SPECIALIZATIONS it is a
+     spec_entry pointer.  It is not relevant for the SCC
+     determination.  */
+  vec<depset *> deps;  /* Depsets we reference.  */
+
+public:
+  unsigned cluster; /* Strongly connected cluster, later entity number  */
+  unsigned section; /* Section written to.  */
+  /* During SCC construction, section is lowlink, until the depset is
+     removed from the stack.  See Tarjan algorithm for details.  */
+
+private:
+  /* Construction via factories.  Destruction via hash traits.  */
+  depset (tree entity);
+  ~depset ();
+
+public:
+  static depset *make_binding (tree, tree);
+  static depset *make_entity (tree, entity_kind, bool = false);
+  /* Late setting a binding name -- /then/ insert into hash!  */
+  inline void set_binding_name (tree name)
+  {
+    gcc_checking_assert (!get_name ());
+    discriminator = reinterpret_cast<uintptr_t> (name);
+  }
+
+private:
+  template<unsigned I> void set_flag_bit ()
+  {
+    gcc_checking_assert (I < 2 || !is_binding ());
+    discriminator |= 1u << I;
+  }
+  template<unsigned I> void clear_flag_bit ()
+  {
+    gcc_checking_assert (I < 2 || !is_binding ());
+    discriminator &= ~(1u << I);
+  }
+  template<unsigned I> bool get_flag_bit () const
+  {
+    gcc_checking_assert (I < 2 || !is_binding ());
+    return bool ((discriminator >> I) & 1);
+  }
+  
+public:
+  bool is_binding () const
+  {
+    return !get_flag_bit<DB_ZERO_BIT> ();
+  }
+  entity_kind get_entity_kind () const
+  {
+    if (is_binding ())
+      return EK_BINDING;
+    return entity_kind ((discriminator >> DB_KIND_BIT) & ((1u << EK_BITS) - 1));
+  }
+  const char *entity_kind_name () const;
+
+public:
+  bool has_defn () const
+  {
+    return get_flag_bit<DB_DEFN_BIT> ();
+  }
+
+public:
+  bool is_member () const
+  {
+    return get_flag_bit<DB_IS_MEMBER_BIT> ();
+  }
+public:
+  bool is_internal () const
+  {
+    return get_flag_bit<DB_IS_INTERNAL_BIT> ();
+  }
+  bool refs_internal () const
+  {
+    return get_flag_bit<DB_REFS_INTERNAL_BIT> ();
+  }
+  bool is_import () const
+  {
+    return get_flag_bit<DB_IMPORTED_BIT> ();
+  }
+  bool is_unreached () const
+  {
+    return get_flag_bit<DB_UNREACHED_BIT> ();
+  }
+  bool is_partial () const
+  {
+    return get_flag_bit<DB_PARTIAL_BIT> ();
+  }
+  bool is_alias_tmpl_inst () const
+  {
+    return get_flag_bit<DB_ALIAS_TMPL_INST_BIT> ();
+  }
+  bool is_alias () const
+  {
+    return get_flag_bit<DB_ALIAS_SPEC_BIT> ();
+  }
+  bool is_hidden () const
+  {
+    return get_flag_bit<DB_HIDDEN_BIT> ();
+  }
+  bool is_type_spec () const
+  {
+    return get_flag_bit<DB_TYPE_SPEC_BIT> ();
+  }
+  bool is_friend_spec () const
+  {
+    return get_flag_bit<DB_FRIEND_SPEC_BIT> ();
+  }
+
+public:
+  /* We set these bit outside of depset.  */
+  void set_hidden_binding ()
+  {
+    set_flag_bit<DB_HIDDEN_BIT> ();
+  }
+  void clear_hidden_binding ()
+  {
+    clear_flag_bit<DB_HIDDEN_BIT> ();
+  }
+
+public:
+  bool is_special () const
+  {
+    return get_flag_bit<DB_SPECIAL_BIT> ();
+  }
+  void set_special ()
+  {
+    set_flag_bit<DB_SPECIAL_BIT> ();
+  }
+
+public:
+  tree get_entity () const
+  {
+    return entity;
+  }
+  tree get_name () const
+  {
+    gcc_checking_assert (is_binding ());
+    return reinterpret_cast <tree> (discriminator);
+  }
+
+public:
+  /* Traits for a hash table of pointers to bindings.  */
+  struct traits {
+    /* Each entry is a pointer to a depset. */
+    typedef depset *value_type;
+    /* We lookup by container:maybe-identifier pair.  */
+    typedef std::pair<tree,tree> compare_type;
+
+    static const bool empty_zero_p = true;
+
+    /* hash and equality for compare_type.  */
+    inline static hashval_t hash (const compare_type &p)
+    {
+      hashval_t h = pointer_hash<tree_node>::hash (p.first);
+      if (p.second)
+	{
+	  hashval_t nh = IDENTIFIER_HASH_VALUE (p.second);
+	  h = iterative_hash_hashval_t (h, nh);
+	}
+      return h;
+    }
+    inline static bool equal (const value_type b, const compare_type &p)
+    {
+      if (b->entity != p.first)
+	return false;
+
+      if (p.second)
+	return b->discriminator == reinterpret_cast<uintptr_t> (p.second);
+      else
+	return !b->is_binding ();
+    }
+
+    /* (re)hasher for a binding itself.  */
+    inline static hashval_t hash (const value_type b)
+    {
+      hashval_t h = pointer_hash<tree_node>::hash (b->entity);
+      if (b->is_binding ())
+	{
+	  hashval_t nh = IDENTIFIER_HASH_VALUE (b->get_name ());
+	  h = iterative_hash_hashval_t (h, nh);
+	}
+      return h;
+    }
+
+    /* Empty via NULL.  */
+    static inline void mark_empty (value_type &p) {p = NULL;}
+    static inline bool is_empty (value_type p) {return !p;}
+
+    /* Nothing is deletable.  Everything is insertable.  */
+    static bool is_deleted (value_type) { return false; }
+    static void mark_deleted (value_type) { gcc_unreachable (); }
+
+    /* We own the entities in the hash table.  */
+    static void remove (value_type p)
+    {
+      delete (p);
+    }
+  };
+
+public:
+  class hash : public hash_table<traits> {
+    typedef traits::compare_type key_t;
+    typedef hash_table<traits> parent;
+
+  public:
+    vec<depset *> worklist;  /* Worklist of decls to walk.  */
+    hash *chain;	     /* Original table.  */
+    depset *current;         /* Current depset being depended.  */
+    unsigned section;	     /* When writing out, the section.  */
+    bool sneakoscope;        /* Detecting dark magic (of a voldemort).  */
+    bool reached_unreached;  /* We reached an unreached entity.  */
+
+  public:
+    hash (size_t size, hash *c = NULL)
+      : parent (size), chain (c), current (NULL), section (0),
+	sneakoscope (false), reached_unreached (false)
+    {
+      worklist.create (size);
+    }
+    ~hash ()
+    {
+      worklist.release ();
+    }
+
+  public:
+    bool is_key_order () const
+    {
+      return chain != NULL;
+    }
+
+  private:
+    depset **entity_slot (tree entity, bool = true);
+    depset **binding_slot (tree ctx, tree name, bool = true);
+    depset *maybe_add_declaration (tree decl);
+
+  public:
+    depset *find_dependency (tree entity);
+    depset *find_binding (tree ctx, tree name);
+    depset *make_dependency (tree decl, entity_kind);
+    void add_dependency (depset *);
+
+  public:
+    void add_mergeable (depset *);
+    depset *add_dependency (tree decl, entity_kind);
+    void add_namespace_context (depset *, tree ns);
+
+  private:
+    depset *add_partial_redirect (depset *partial, depset **slot = nullptr);
+    static bool add_binding_entity (tree, WMB_Flags, void *);
+
+  public:
+    bool add_namespace_entities (tree ns, bitmap partitions);
+    void add_specializations (bool decl_p);
+    void add_class_entities (vec<tree, va_gc> *);
+
+  public:    
+    void find_dependencies ();
+    bool finalize_dependencies ();
+    vec<depset *> connect ();
+  };
+
+public:
+  struct tarjan {
+    vec<depset *> result;
+    vec<depset *> stack;
+    unsigned index;
+
+    tarjan (unsigned size)
+      : index (0)
+    {
+      result.create (size);
+      stack.create (50);
+    }
+    ~tarjan () 
+    {
+      gcc_assert (!stack.length ());
+      stack.release ();
+    }
+
+  public:
+    void connect (depset *);
+  };
+};
+
+inline
+depset::depset (tree entity)
+  :entity (entity), discriminator (0), cluster (0), section (0)
+{
+  deps.create (0);
+}
+
+inline
+depset::~depset ()
+{
+  deps.release ();
+}
+
+const char *
+depset::entity_kind_name () const
+{
+  /* Same order as entity_kind.  */
+  static const char *const names[] = 
+    {"decl", "specialization", "using", "namespace", "redirect", "binding"};
+  entity_kind kind = get_entity_kind ();
+  gcc_checking_assert (kind < sizeof (names) / sizeof(names[0]));
+  return names[kind];
+}
+
+/* Create a depset for a namespace binding NS::NAME.  */
+
+depset *depset::make_binding (tree ns, tree name)
+{
+  depset *binding = new depset (ns);
+
+  binding->discriminator = reinterpret_cast <uintptr_t> (name);
+
+  return binding;
+}
+
+depset *depset::make_entity (tree entity, entity_kind ek, bool is_defn)
+{
+  depset *r = new depset (entity);
+
+  r->discriminator = ((1 << DB_ZERO_BIT)
+		      | (ek << DB_KIND_BIT)
+		      | is_defn << DB_DEFN_BIT);
+
+  return r;
+}
+
+/* Values keyed to some unsigned integer.  This is not GTY'd, so if
+   T is tree they must be reachable via some other path.  */
+
+template<typename T>
+class uintset {
+public:
+  unsigned key;  /* Entity index of the other entity.  */
+
+  /* Payload.  */
+  unsigned allocp2 : 5;  /* log(2) allocated pending  */
+  unsigned num : 27;    /* Number of pending.  */
+
+  /* Trailing array of values.   */
+  T values[1];
+
+public:
+  /* Even with ctors, we're very pod-like.  */
+  uintset (unsigned uid)
+    : key (uid), allocp2 (0), num (0)
+  {
+  }
+  /* Copy constructor, which is exciting because of the trailing
+     array.  */
+  uintset (const uintset *from)
+  {
+    size_t size = (offsetof (uintset, values)
+		   + sizeof (uintset::values) * from->num);
+    memmove (this, from, size);
+    if (from->num)
+      allocp2++;
+  }
+
+public:
+  struct traits : delete_ptr_hash<uintset> {
+    typedef unsigned compare_type;
+    typedef typename delete_ptr_hash<uintset>::value_type value_type;
+
+    /* Hash and equality for compare_type.  */
+    inline static hashval_t hash (const compare_type k)
+    {
+      return hashval_t (k);
+    }
+    inline static hashval_t hash (const value_type v)
+    {
+      return hash (v->key);
+    }
+
+    inline static bool equal (const value_type v, const compare_type k)
+    {
+      return v->key == k;
+    }
+  };
+
+public:
+  class hash : public hash_table<traits> 
+  {
+    typedef typename traits::compare_type key_t;
+    typedef hash_table<traits> parent;
+
+  public:
+    hash (size_t size)
+      : parent (size)
+    {
+    }
+    ~hash ()
+    {
+    }
+
+  private:
+    uintset **find_slot (key_t key, insert_option insert)
+    {
+      return this->find_slot_with_hash (key, traits::hash (key), insert);
+    }
+
+  public:
+    uintset *get (key_t key, bool extract = false);
+    bool add (key_t key, T value);
+    uintset *create (key_t key, unsigned num, T init = 0);
+  };
+};
+
+/* Add VALUE to KEY's uintset, creating it if necessary.  Returns true
+   if we created the uintset.  */
+
+template<typename T>
+bool
+uintset<T>::hash::add (typename uintset<T>::hash::key_t key, T value)
+{
+  uintset **slot = this->find_slot (key, INSERT);
+  uintset *set = *slot;
+  bool is_new = !set;
+
+  if (is_new || set->num == (1u << set->allocp2))
+    {
+      if (set)
+	{
+	  unsigned n = set->num * 2;
+	  size_t new_size = (offsetof (uintset, values)
+			     + sizeof (uintset::values) * n);
+	  uintset *new_set = new (::operator new (new_size)) uintset (set);
+	  delete set;
+	  set = new_set;
+	}
+      else
+	set = new (::operator new (sizeof (*set))) uintset (key);
+      *slot = set;
+    }
+
+  set->values[set->num++] = value;
+
+  return is_new;
+}
+
+template<typename T>
+uintset<T> *
+uintset<T>::hash::create (typename uintset<T>::hash::key_t key, unsigned num,
+			  T init)
+{
+  unsigned p2alloc = 0;
+  for (unsigned v = num; v != 1; v = (v >> 1) | (v & 1))
+    p2alloc++;
+
+  size_t new_size = (offsetof (uintset, values)
+		     + (sizeof (uintset::values) << p2alloc));
+  uintset *set = new (::operator new (new_size)) uintset (key);
+  set->allocp2 = p2alloc;
+  set->num = num;
+  while (num--)
+    set->values[num] = init;
+
+  uintset **slot = this->find_slot (key, INSERT);
+  gcc_checking_assert (!*slot);
+  *slot = set;
+
+  return set;
+}
+
+/* Locate KEY's uintset, potentially removing it from the hash table  */
+
+template<typename T>
+uintset<T> *
+uintset<T>::hash::get (typename uintset<T>::hash::key_t key, bool extract)
+{
+  uintset *res = NULL;
+
+  if (uintset **slot = this->find_slot (key, NO_INSERT))
+    {
+      res = *slot;
+      if (extract)
+	/* We need to remove the pendset without deleting it. */
+	traits::mark_deleted (*slot);
+    }
+
+  return res;
+}
+
+/* Entities keyed to some other entity.  When we load the other
+   entity, we mark it in some way to indicate there are further
+   entities to load when you start looking inside it.  For instance
+   template specializations are keyed to their most general template.
+   When we instantiate that, we need to know all the partial
+   specializations (to pick the right template), and all the known
+   specializations (to avoid reinstantiating it, and/or whether it's
+   extern).  The values split into two ranges.  If !MSB set, indices
+   into the entity array.  If MSB set, an indirection to another
+   pendset.  */
+
+typedef uintset<unsigned> pendset;
+static pendset::hash *pending_table;
+
+/* Some entities are attached to another entitity for ODR purposes.
+   For example, at namespace scope, 'inline auto var = []{};', that
+   lambda is attached to 'var', and follows its ODRness.  */
+typedef uintset<tree> attachset;
+static attachset::hash *attached_table;
+
+/********************************************************************/
+/* Tree streaming.   The tree streaming is very specific to the tree
+   structures themselves.  A tag indicates the kind of tree being
+   streamed.  -ve tags indicate backreferences to already-streamed
+   trees.  Backreferences are auto-numbered.  */
+
+/* Tree tags.  */
+enum tree_tag {
+  tt_null,		/* NULL_TREE.  */
+  tt_fixed,		/* Fixed vector index.  */
+
+  tt_node,		/* By-value node.  */
+  tt_decl,		/* By-value mergeable decl.  */
+  tt_tpl_parm,		/* Template parm.  */
+
+  /* The ordering of the following 4 is relied upon in
+     trees_out::tree_node.  */
+  tt_id,  		/* Identifier node.  */
+  tt_conv_id,		/* Conversion operator name.  */
+  tt_anon_id,		/* Anonymous name.  */
+  tt_lambda_id,		/* Lambda name.  */
+
+  tt_typedef_type,	/* A (possibly implicit) typedefed type.  */
+  tt_derived_type,	/* A type derived from another type.  */
+  tt_variant_type,	/* A variant of another type.  */
+
+  tt_tinfo_var,		/* Typeinfo object. */
+  tt_tinfo_typedef,	/* Typeinfo typedef.  */
+  tt_ptrmem_type,	/* Pointer to member type.  */
+
+  tt_parm,		/* Function parameter or result.  */
+  tt_enum_value,	/* An enum value.  */
+  tt_enum_decl,		/* An enum decl.  */
+  tt_data_member,	/* Data member/using-decl.  */
+
+  tt_binfo,		/* A BINFO.  */
+  tt_vtable,		/* A vtable.  */
+  tt_thunk,		/* A thunk.  */
+  tt_clone_ref,
+
+  tt_entity,		/* A extra-cluster entity.  */
+
+  tt_template,		/* The TEMPLATE_RESULT of a template.  */
+};
+
+enum walk_kind {
+  WK_none,	/* No walk to do (a back- or fixed-ref happened).  */
+  WK_normal,	/* Normal walk (by-name if possible).  */
+
+  WK_value,	/* By-value walk.  */
+};
+
+enum merge_kind
+{
+  MK_unique,	/* Known unique.  */
+  MK_named,	/* Found by CTX, NAME + maybe_arg types etc.  */
+  MK_field,	/* Found by CTX and index on TYPE_FIELDS  */
+  MK_vtable,	/* Found by CTX and index on TYPE_VTABLES  */
+  MK_as_base,	/* Found by CTX.  */
+
+  MK_partial,
+
+  MK_enum,	/* Found by CTX, & 1stMemberNAME.  */
+  MK_attached,  /* Found by attachee & index.  */
+
+  MK_friend_spec,  /* Like named, but has a tmpl & args too.  */
+  MK_local_friend, /* Found by CTX, index.  */
+
+  MK_indirect_lwm = MK_enum,
+  
+  /* Template specialization kinds below. These are all found via
+     primary template and specialization args.  */
+  MK_template_mask = 0x10,  /* A template specialization.  */
+
+  MK_tmpl_decl_mask = 0x8, /* In decl table (not a type specialization).  */
+
+  /* Following bit has meaning dependent on MK_tmpl_decl_mask. */
+  MK_tmpl_alias_mask = 0x2, /* An alias specialization (in both).  */
+  MK_tmpl_partial_mask = 0x2, /* A partial type specialization.  */
+  
+  MK_tmpl_tmpl_mask = 0x1, /* We want TEMPLATE_DECL.  */
+
+  MK_type_spec = MK_template_mask,
+  MK_type_tmpl_spec = MK_type_spec | MK_tmpl_tmpl_mask,
+  MK_type_partial_spec = MK_type_tmpl_spec | MK_tmpl_partial_mask,
+
+  MK_decl_spec = MK_template_mask | MK_tmpl_decl_mask,
+  MK_decl_tmpl_spec = MK_decl_spec | MK_tmpl_tmpl_mask,
+
+  MK_alias_spec = MK_decl_spec | MK_tmpl_alias_mask,
+
+  MK_hwm = 0x20
+};
+/* This is more than a debugging array.  NULLs are used to determine
+   an invalid merge_kind number.  */
+static char const *const merge_kind_name[MK_hwm] =
+  {
+    "unique", "named", "field", "vtable",	/* 0...3  */
+    "asbase", "partial", "enum", "attached",	/* 4...7  */
+
+    "friend spec", "local friend", NULL, NULL,  /* 8...11 */
+    NULL, NULL, NULL, NULL,
+
+    "type spec", "type tmpl spec",	/* 16,17 type (template).  */
+    NULL, "type partial spec",		/* 18,19 partial template. */
+    NULL, NULL, NULL, NULL,
+
+    "decl spec", "decl tmpl spec",	/* 24,25 decl (template).  */
+    "alias spec", NULL,			/* 26,27 alias. */
+    NULL, NULL, NULL, NULL,
+  };
+
+/* Mergeable entity location data.  */
+struct merge_key {
+  cp_ref_qualifier ref_q : 2;
+  unsigned index;
+
+  tree ret;  /* Return type, if appropriate.  */
+  tree args; /* Arg types, if appropriate.  */
+
+  tree constraints;  /* Constraints.  */
+
+  merge_key ()
+    :ref_q (REF_QUAL_NONE), index (0),
+     ret (NULL_TREE), args (NULL_TREE),
+     constraints (NULL_TREE)
+  {
+  }
+};
+
+struct duplicate_hash : nodel_ptr_hash<tree_node>
+{
+  inline static hashval_t hash (value_type decl)
+  {
+    if (TREE_CODE (decl) == TREE_BINFO)
+      decl = TYPE_NAME (BINFO_TYPE (decl));
+    return hashval_t (DECL_UID (decl));
+  }
+};
+
+/* Hashmap of merged duplicates.  Usually decls, but can contain
+   BINFOs.  */
+typedef hash_map<tree,uintptr_t,
+		 simple_hashmap_traits<duplicate_hash,uintptr_t> >
+duplicate_hash_map;
+
+/* Tree stream reader.  Note that reading a stream doesn't mark the
+   read trees with TREE_VISITED.  Thus it's quite safe to have
+   multiple concurrent readers.  Which is good, because lazy
+   loading. */
+class trees_in : public bytes_in {
+  typedef bytes_in parent;
+
+private:
+  module_state *state;		/* Module being imported.  */
+  vec<tree> back_refs;		/* Back references.  */
+  duplicate_hash_map *duplicates;	/* Map from existings to duplicate.  */
+  vec<tree> post_decls;		/* Decls to post process.  */
+  unsigned unused;		/* Inhibit any interior TREE_USED
+				   marking.  */
+
+public:
+  trees_in (module_state *);
+  ~trees_in ();
+
+public:
+  int insert (tree);
+  tree back_ref (int);
+
+private:
+  tree start (unsigned = 0);
+
+public:
+  /* Needed for binfo writing  */
+  bool core_bools (tree);
+
+private:
+  /* Stream tree_core, lang_decl_specific and lang_type_specific
+     bits.  */
+  bool core_vals (tree);
+  bool lang_type_bools (tree);
+  bool lang_type_vals (tree);
+  bool lang_decl_bools (tree);
+  bool lang_decl_vals (tree);
+  bool lang_vals (tree);
+  bool tree_node_bools (tree);
+  bool tree_node_vals (tree);
+  tree tree_value ();
+  tree decl_value ();
+  tree tpl_parm_value ();
+
+private:
+  tree chained_decls ();  /* Follow DECL_CHAIN.  */
+  vec<tree, va_heap> *vec_chained_decls ();
+  vec<tree, va_gc> *tree_vec (); /* vec of tree.  */
+  vec<tree_pair_s, va_gc> *tree_pair_vec (); /* vec of tree_pair.  */
+  tree tree_list (bool has_purpose);
+
+public:
+  /* Read a tree node.  */
+  tree tree_node (bool is_use = false);
+
+private:
+  bool install_entity (tree decl);
+  tree tpl_parms (unsigned &tpl_levels);
+  bool tpl_parms_fini (tree decl, unsigned tpl_levels);
+  bool tpl_header (tree decl, unsigned *tpl_levels);
+  int fn_parms_init (tree);
+  void fn_parms_fini (int tag, tree fn, tree existing, bool has_defn);
+  unsigned add_indirect_tpl_parms (tree);
+public:
+  bool add_indirects (tree);
+
+public:
+  /* Serialize various definitions. */
+  bool read_definition (tree decl);
+  
+private:
+  bool is_matching_decl (tree existing, tree decl);
+  static bool install_implicit_member (tree decl);
+  bool read_function_def (tree decl, tree maybe_template);
+  bool read_var_def (tree decl, tree maybe_template);
+  bool read_class_def (tree decl, tree maybe_template);
+  bool read_enum_def (tree decl, tree maybe_template);
+
+public:
+  tree decl_container ();
+  tree key_mergeable (int tag, merge_kind, tree decl, tree inner, tree type,
+		      tree container, bool is_mod);
+  unsigned binfo_mergeable (tree *);
+
+private:
+  uintptr_t *find_duplicate (tree existing);
+  void register_duplicate (tree decl, tree existing);
+  /* Mark as an already diagnosed bad duplicate.  */
+  void unmatched_duplicate (tree existing)
+  {
+    *find_duplicate (existing) |= 1;
+  }
+
+public:
+  bool is_duplicate (tree decl)
+  {
+    return find_duplicate (decl) != NULL;
+  }
+  tree maybe_duplicate (tree decl)
+  {
+    if (uintptr_t *dup = find_duplicate (decl))
+      return reinterpret_cast<tree> (*dup & ~uintptr_t (1));
+    return decl;
+  }
+  tree odr_duplicate (tree decl, bool has_defn);
+
+public:
+  /* Return the next decl to postprocess, or NULL.  */
+  tree post_process ()
+  {
+    return post_decls.length () ? post_decls.pop () : NULL_TREE;
+  }
+private:
+  /* Register DECL for postprocessing.  */
+  void post_process (tree decl)
+  {
+    post_decls.safe_push (decl);
+  }
+
+private:
+  void assert_definition (tree, bool installing);
+};
+
+trees_in::trees_in (module_state *state)
+  :parent (), state (state), unused (0)
+{
+  duplicates = NULL;
+  back_refs.create (500);
+  post_decls.create (0);
+}
+
+trees_in::~trees_in ()
+{
+  delete (duplicates);
+  back_refs.release ();
+  post_decls.release ();
+}
+
+/* Tree stream writer.  */
+class trees_out : public bytes_out {
+  typedef bytes_out parent;
+
+private:
+  module_state *state;		/* The module we are writing.  */
+  ptr_int_hash_map tree_map; 	/* Trees to references */
+  depset::hash *dep_hash;    	/* Dependency table.  */
+  int ref_num;			/* Back reference number.  */
+  unsigned section;
+#if CHECKING_P
+  int importedness;		/* Checker that imports not occurring
+				   inappropriately.  */
+#endif
+
+public:
+  trees_out (allocator *, module_state *, depset::hash &deps, unsigned sec = 0);
+  ~trees_out ();
+
+private:
+  void mark_trees ();
+  void unmark_trees ();
+
+public:
+  /* Hey, let's ignore the well known STL iterator idiom.  */
+  void begin ();
+  unsigned end (elf_out *sink, unsigned name, unsigned *crc_ptr);
+  void end ();
+
+public:
+  enum tags 
+  {
+    tag_backref = -1,	/* Upper bound on the backrefs.  */
+    tag_value = 0,	/* Write by value.  */
+    tag_fixed		/* Lower bound on the fixed trees.  */
+  };
+
+public:
+  bool is_key_order () const
+  {
+    return dep_hash->is_key_order ();
+  }
+
+public:
+  int insert (tree, walk_kind = WK_normal);
+
+private:
+  void start (tree, bool = false);
+
+private:
+  walk_kind ref_node (tree);
+public:
+  int get_tag (tree);
+  void set_importing (int i ATTRIBUTE_UNUSED)
+  {
+#if CHECKING_P
+    importedness = i;
+#endif
+  }
+
+private:
+  void core_bools (tree);
+  void core_vals (tree);
+  void lang_type_bools (tree);
+  void lang_type_vals (tree);
+  void lang_decl_bools (tree);
+  void lang_decl_vals (tree);
+  void lang_vals (tree);
+  void tree_node_bools (tree);
+  void tree_node_vals (tree);
+
+private:
+  void chained_decls (tree);
+  void vec_chained_decls (tree);
+  void tree_vec (vec<tree, va_gc> *);
+  void tree_pair_vec (vec<tree_pair_s, va_gc> *);
+  void tree_list (tree, bool has_purpose);
+
+public:
+  /* Mark a node for by-value walking.  */
+  void mark_by_value (tree);
+
+public:
+  void tree_node (tree);
+
+private:
+  void install_entity (tree decl, depset *);
+  void tpl_parms (tree parms, unsigned &tpl_levels);
+  void tpl_parms_fini (tree decl, unsigned tpl_levels);
+  void fn_parms_fini (tree) {}
+  unsigned add_indirect_tpl_parms (tree);
+public:
+  void add_indirects (tree);
+  void fn_parms_init (tree);
+  void tpl_header (tree decl, unsigned *tpl_levels);
+
+public:
+  merge_kind get_merge_kind (tree decl, depset *maybe_dep);
+  tree decl_container (tree decl);
+  void key_mergeable (int tag, merge_kind, tree decl, tree inner,
+		      tree container, depset *maybe_dep);
+  void binfo_mergeable (tree binfo);
+
+private:
+  bool decl_node (tree, walk_kind ref);
+  void type_node (tree);
+  void tree_value (tree);
+  void tpl_parm_value (tree);
+
+public:
+  void decl_value (tree, depset *);
+
+public:
+  /* Serialize various definitions. */
+  void write_definition (tree decl);
+  void mark_declaration (tree decl, bool do_defn);
+
+private:
+  void mark_function_def (tree decl);
+  void mark_var_def (tree decl);
+  void mark_class_def (tree decl);
+  void mark_enum_def (tree decl);
+  void mark_class_member (tree decl, bool do_defn = true);
+  void mark_binfos (tree type);
+
+private:
+  void write_var_def (tree decl);
+  void write_function_def (tree decl);
+  void write_class_def (tree decl);
+  void write_enum_def (tree decl);
+
+private:
+  static void assert_definition (tree);
+
+public:
+  static void instrument ();
+
+private:
+  /* Tree instrumentation. */
+  static unsigned tree_val_count;
+  static unsigned decl_val_count;
+  static unsigned back_ref_count;
+  static unsigned null_count;
+};
+
+/* Instrumentation counters.  */
+unsigned trees_out::tree_val_count;
+unsigned trees_out::decl_val_count;
+unsigned trees_out::back_ref_count;
+unsigned trees_out::null_count;
+
+trees_out::trees_out (allocator *mem, module_state *state, depset::hash &deps,
+		      unsigned section)
+  :parent (mem), state (state), tree_map (500),
+   dep_hash (&deps), ref_num (0), section (section)
+{
+#if CHECKING_P
+  importedness = 0;
+#endif
+}
+
+trees_out::~trees_out ()
+{
+}
+
+/********************************************************************/
+/* Location.  We're aware of the line-map concept and reproduce it
+   here.  Each imported module allocates a contiguous span of ordinary
+   maps, and of macro maps.  adhoc maps are serialized by contents,
+   not pre-allocated.   The scattered linemaps of a module are
+   coalesced when writing.  */
+
+
+/* I use half-open [first,second) ranges.  */
+typedef std::pair<unsigned,unsigned> range_t;
+
+/* A range of locations.  */
+typedef std::pair<location_t,location_t> loc_range_t;
+
+/* Spans of the line maps that are occupied by this TU.  I.e. not
+   within imports.  Only extended when in an interface unit.
+   Interval zero corresponds to the forced header linemap(s).  This
+   is a singleton object.  */
+
+class loc_spans {
+public:
+  /* An interval of line maps.  The line maps here represent a contiguous
+     non-imported range.  */
+  struct span {
+    loc_range_t ordinary;	/* Ordinary map location range. */
+    loc_range_t macro;		/* Macro map location range.  */
+    int ordinary_delta;	/* Add to ordinary loc to get serialized loc.  */
+    int macro_delta;	/* Likewise for macro loc.  */
+  };
+
+private:
+  vec<span> spans;
+
+public:
+  loc_spans ()
+  {
+    spans.create (20);
+  }
+  ~loc_spans ()
+  {
+    spans.release ();
+  }
+
+public:
+  span &operator[] (unsigned ix)
+  {
+    return spans[ix];
+  }
+  unsigned length () const
+  {
+    return spans.length ();
+  }
+
+public:
+  bool init_p () const
+  {
+    return spans.length () != 0;
+  }
+  /* Initializer.  */
+  void init (const line_maps *lmaps, const line_map_ordinary *map);
+
+  /* Slightly skewed preprocessed files can cause us to miss an
+     initialization in some places.  Fallback initializer.  */
+  void maybe_init ()
+  {
+    if (!init_p ())
+      init (line_table, nullptr);
+  }
+
+public:
+  enum {
+    SPAN_RESERVED = 0,	/* Reserved (fixed) locations.  */
+    SPAN_FIRST = 1,	/* LWM of locations to stream  */
+    SPAN_MAIN = 2	/* Main file and onwards.  */
+  };
+
+public:
+  location_t main_start () const
+  {
+    return spans[SPAN_MAIN].ordinary.first;
+  }
+
+public:
+  void open (location_t);
+  void close ();
+
+public:
+  /* Propagate imported linemaps to us, if needed.  */
+  bool maybe_propagate (module_state *import, location_t loc);
+
+public:
+  const span *ordinary (location_t);
+  const span *macro (location_t);
+};
+
+static loc_spans spans;
+
+/********************************************************************/
+/* Data needed by a module during the process of loading.  */
+struct GTY(()) slurping {
+
+  /* Remap import's module numbering to our numbering.  Values are
+     shifted by 1.  Bit0 encodes if the import is direct.  */
+  vec<unsigned, va_heap, vl_embed> *
+    GTY((skip)) remap;			/* Module owner remapping.  */
+
+  elf_in *GTY((skip)) from;     	/* The elf loader.  */
+
+  /* This map is only for header imports themselves -- the global
+     headers bitmap hold it for the current TU.  */
+  bitmap headers;	/* Transitive set of direct imports, including
+			   self.  Used for macro visibility and
+			   priority.  */
+
+  /* These objects point into the mmapped area, unless we're not doing
+     that, or we got frozen or closed.  In those cases they point to
+     buffers we own.  */
+  bytes_in macro_defs;	/* Macro definitions.  */
+  bytes_in macro_tbl;	/* Macro table.  */
+
+  /* Location remapping.  first->ordinary, second->macro.  */
+  range_t GTY((skip)) loc_deltas;
+
+  unsigned current;	/* Section currently being loaded.  */
+  unsigned remaining;	/* Number of lazy sections yet to read.  */
+  unsigned lru;		/* An LRU counter.  */
+
+ public:
+  slurping (elf_in *);
+  ~slurping ();
+
+ public:
+  /* Close the ELF file, if it's open.  */
+  void close ()
+  {
+    if (from)
+      {
+	from->end ();
+	delete from;
+	from = NULL;
+      }
+  }
+
+ public:
+  void release_macros ();
+
+ public:
+  void alloc_remap (unsigned size)
+  {
+    gcc_assert (!remap);
+    vec_safe_reserve (remap, size);
+    for (unsigned ix = size; ix--;)
+      remap->quick_push (0);
+  }
+  unsigned remap_module (unsigned owner)
+  {
+    if (owner < remap->length ())
+      return (*remap)[owner] >> 1;
+    return 0;
+  }
+
+ public:
+  /* GC allocation.  But we must explicitly delete it.   */
+  static void *operator new (size_t x)
+  {
+    return ggc_alloc_atomic (x);
+  }
+  static void operator delete (void *p)
+  {
+    ggc_free (p);
+  }
+};
+
+slurping::slurping (elf_in *from)
+  : remap (NULL), from (from),
+    headers (BITMAP_GGC_ALLOC ()), macro_defs (), macro_tbl (),
+    loc_deltas (0, 0),
+    current (~0u), remaining (0), lru (0)
+{
+}
+
+slurping::~slurping ()
+{
+  vec_free (remap);
+  remap = NULL;
+  release_macros ();
+  close ();
+}
+
+void slurping::release_macros ()
+{
+  if (macro_defs.size)
+    elf_in::release (from, macro_defs);
+  if (macro_tbl.size)
+    elf_in::release (from, macro_tbl);
+}
+
+/* Information about location maps used during writing.  */
+
+struct location_map_info {
+  range_t num_maps;
+
+  unsigned max_range;
+};
+
+/* Flage for extensions that end up being streamed.  */
+
+enum streamed_extensions {
+  SE_OPENMP = 1 << 0,
+  SE_BITS = 1
+};
+
+/********************************************************************/
+struct module_state_config;
+
+/* Increasing levels of loadedness.  */
+enum module_loadedness {
+  ML_NONE,		/* Not loaded.  */
+  ML_CONFIG,		/* Config loaed.  */
+  ML_PREPROCESSOR,	/* Preprocessor loaded.  */
+  ML_LANGUAGE,		/* Language loaded.  */
+};
+
+/* Increasing levels of directness (toplevel) of import.  */
+enum module_directness {
+  MD_NONE,  		/* Not direct.  */
+  MD_PARTITION_DIRECT,	/* Direct import of a partition.  */
+  MD_DIRECT,		/* Direct import.  */
+  MD_PURVIEW_DIRECT,	/* direct import in purview.  */
+};
+
+/* State of a particular module. */
+
+class GTY((chain_next ("%h.parent"), for_user)) module_state {
+ public:
+  /* We always import & export ourselves.  */
+  bitmap imports;	/* Transitive modules we're importing.  */
+  bitmap exports;	/* Subset of that, that we're exporting.  */
+
+  module_state *parent;
+  tree name;		/* Name of the module.  */
+
+  slurping *slurp;	/* Data for loading.  */
+
+  const char *flatname;	/* Flatname of module.  */
+  char *filename;	/* CMI Filename */
+
+  /* Indices into the entity_ary.  */
+  unsigned entity_lwm;
+  unsigned entity_num;
+
+  /* Location ranges for this module.  adhoc-locs are decomposed, so
+     don't have a range.  */
+  loc_range_t GTY((skip)) ordinary_locs;
+  loc_range_t GTY((skip)) macro_locs;
+
+  /* LOC is first set too the importing location.  When initially
+     loaded it refers to a module loc whose parent is the importing
+     location.  */
+  location_t loc; 	/* Location referring to module itself.  */
+  unsigned crc;		/* CRC we saw reading it in. */
+
+  unsigned mod;		/* Module owner number.  */
+  unsigned remap;	/* Remapping during writing.  */
+
+  unsigned short subst;	/* Mangle subst if !0.  */
+
+  /* How loaded this module is.  */
+  enum module_loadedness loadedness : 2;
+
+  bool module_p : 1;    /* /The/ module of this TU.  */
+  bool header_p : 1;	/* Is a header unit.  */
+  bool interface_p : 1; /* An interface.  */
+  bool partition_p : 1; /* A partition.  */
+
+  /* How directly this module is imported.  */
+  enum module_directness directness : 2;
+
+  bool exported_p : 1;	/* directness != MD_NONE && exported.  */
+  bool cmi_noted_p : 1; /* We've told the user about the CMI, don't
+			   do it again  */
+  bool call_init_p : 1; /* This module's global initializer needs
+			   calling.  */
+  /* Record extensions emitted or permitted.  */
+  unsigned extensions : SE_BITS;
+  /* 12 bits used, 4 bits remain  */
+
+ public:
+  module_state (tree name, module_state *, bool);
+  ~module_state ();
+
+ public:
+  void release ()
+  {
+    imports = exports = NULL;
+    slurped ();
+  }
+  void slurped ()
+  {
+    delete slurp;
+    slurp = NULL;
+  }
+  elf_in *from () const
+  {
+    return slurp->from;
+  }
+
+ public:
+  /* Kind of this module.  */
+  bool is_module () const
+  {
+    return module_p;
+  }
+  bool is_header () const
+  {
+    return header_p;
+  }
+  bool is_interface () const
+  {
+    return interface_p;
+  }
+  bool is_partition () const
+  {
+    return partition_p;
+  }
+
+  /* How this module is used in the current TU.  */
+  bool is_exported () const
+  {
+    return exported_p;
+  }
+  bool is_direct () const
+  {
+    return directness >= MD_DIRECT;
+  }
+  bool is_purview_direct () const
+  {
+    return directness == MD_PURVIEW_DIRECT;
+  }
+  bool is_partition_direct () const
+  {
+    return directness == MD_PARTITION_DIRECT;
+  }
+
+ public:
+  /* Is this not a real module?  */
+  bool is_rooted () const
+  {
+    return loc != UNKNOWN_LOCATION;
+  }
+
+ public:
+  bool check_not_purview (location_t loc);
+
+ public:
+  void mangle (bool include_partition);
+
+ public:
+  void set_import (module_state const *, bool is_export);
+  void announce (const char *) const;
+
+ public:
+  /* Read and write module.  */
+  void write (elf_out *to, cpp_reader *);
+  bool read_initial (cpp_reader *);
+  bool read_preprocessor (bool);
+  bool read_language (bool);
+
+ public:
+  /* Read a section.  */
+  bool load_section (unsigned snum, mc_slot *mslot);
+  /* Lazily read a section.  */
+  bool lazy_load (unsigned index, mc_slot *mslot);
+
+ public:
+  /* Juggle a limited number of file numbers.  */
+  static void freeze_an_elf ();
+  bool maybe_defrost ();
+
+ public:
+  void maybe_completed_reading ();
+  bool check_read (bool outermost, bool ok);
+
+ private:
+  /* The README, for human consumption.  */
+  void write_readme (elf_out *to, const char *dialect, unsigned extensions);
+  void write_env (elf_out *to);
+
+ private:
+  /* Import tables. */
+  void write_imports (bytes_out &cfg, bool direct);
+  unsigned read_imports (bytes_in &cfg, cpp_reader *, line_maps *maps);
+
+ private:
+  void write_imports (elf_out *to, unsigned *crc_ptr);
+  bool read_imports (cpp_reader *, line_maps *);
+
+ private:
+  void write_partitions (elf_out *to, unsigned, unsigned *crc_ptr);
+  bool read_partitions (unsigned);
+
+ private:
+  void write_config (elf_out *to, struct module_state_config &, unsigned crc);
+  bool read_config (struct module_state_config &);
+  static void write_counts (elf_out *to, unsigned [], unsigned *crc_ptr);
+  bool read_counts (unsigned []);
+
+ public:
+  void note_cmi_name ();
+
+ private:
+  static unsigned write_bindings (elf_out *to, vec<depset *> depsets,
+				  unsigned *crc_ptr);
+  bool read_bindings (unsigned count, unsigned lwm, unsigned hwm);
+
+  static void write_namespace (bytes_out &sec, depset *ns_dep);
+  tree read_namespace (bytes_in &sec);
+
+  void write_namespaces (elf_out *to, vec<depset *> spaces,
+			 unsigned, unsigned *crc_ptr);
+  bool read_namespaces (unsigned);
+
+  unsigned write_cluster (elf_out *to, depset *depsets[], unsigned size,
+			  depset::hash &, unsigned *counts, unsigned *crc_ptr);
+  bool read_cluster (unsigned snum);
+
+ private:
+  unsigned write_inits (elf_out *to, depset::hash &, unsigned *crc_ptr);
+  bool read_inits (unsigned count);
+
+ private:
+  void write_pendings (elf_out *to, vec<depset *> depsets,
+		      depset::hash &, unsigned count, unsigned *crc_ptr);
+  bool read_pendings (unsigned count);
+
+ private:
+  void write_entities (elf_out *to, vec<depset *> depsets,
+		       unsigned count, unsigned *crc_ptr);
+  bool read_entities (unsigned count, unsigned lwm, unsigned hwm);
+
+ private:
+  location_map_info write_prepare_maps (module_state_config *);
+  bool read_prepare_maps (const module_state_config *);
+
+  void write_ordinary_maps (elf_out *to, location_map_info &,
+			    module_state_config *, bool, unsigned *crc_ptr);
+  bool read_ordinary_maps ();
+  void write_macro_maps (elf_out *to, location_map_info &,
+			 module_state_config *, unsigned *crc_ptr);
+  bool read_macro_maps ();
+
+ private:
+  void write_define (bytes_out &, const cpp_macro *, bool located = true);
+  cpp_macro *read_define (bytes_in &, cpp_reader *, bool located = true) const;
+  unsigned write_macros (elf_out *to, cpp_reader *, unsigned *crc_ptr);
+  bool read_macros ();
+  void install_macros ();
+
+ public:
+  void import_macros ();
+
+ public:
+  static void undef_macro (cpp_reader *, location_t, cpp_hashnode *);
+  static cpp_macro *deferred_macro (cpp_reader *, location_t, cpp_hashnode *);
+
+ public:
+  void write_location (bytes_out &, location_t);
+  location_t read_location (bytes_in &) const;
+
+ public:
+  void set_flatname ();
+  const char *get_flatname () const
+  {
+    return flatname;
+  }
+  location_t imported_from () const;
+
+ public:
+  void set_filename (const Cody::Packet &);
+  bool do_import (cpp_reader *, bool outermost);
+};
+
+/* Hash module state by name.  This cannot be a member of
+   module_state, because of GTY restrictions.  We never delete from
+   the hash table, but ggc_ptr_hash doesn't support that
+   simplification.  */
+
+struct module_state_hash : ggc_ptr_hash<module_state> {
+  typedef std::pair<tree,uintptr_t> compare_type; /* {name,parent} */
+
+  static inline hashval_t hash (const value_type m);
+  static inline hashval_t hash (const compare_type &n);
+  static inline bool equal (const value_type existing,
+			    const compare_type &candidate);
+};
+
+module_state::module_state (tree name, module_state *parent, bool partition)
+  : imports (BITMAP_GGC_ALLOC ()), exports (BITMAP_GGC_ALLOC ()),
+    parent (parent), name (name), slurp (NULL),
+    flatname (NULL), filename (NULL),
+    entity_lwm (~0u >> 1), entity_num (0),
+    ordinary_locs (0, 0), macro_locs (0, 0),
+    loc (UNKNOWN_LOCATION),
+    crc (0), mod (MODULE_UNKNOWN), remap (0), subst (0)
+{
+  loadedness = ML_NONE;
+
+  module_p = header_p = interface_p = partition_p = false;
+
+  directness = MD_NONE;
+  exported_p = false;
+
+  cmi_noted_p = false;
+  call_init_p = false;
+
+  partition_p = partition;
+
+  extensions = 0;
+  if (name && TREE_CODE (name) == STRING_CST)
+    {
+      header_p = true;
+
+      const char *string = TREE_STRING_POINTER (name);
+      gcc_checking_assert (string[0] == '.'
+			   ? IS_DIR_SEPARATOR (string[1])
+			   : IS_ABSOLUTE_PATH (string));
+    }
+
+  gcc_checking_assert (!(parent && header_p));
+}
+
+module_state::~module_state ()
+{
+  release ();
+}
+
+/* Hash module state.  */
+static hashval_t
+module_name_hash (const_tree name)
+{
+  if (TREE_CODE (name) == STRING_CST)
+    return htab_hash_string (TREE_STRING_POINTER (name));
+  else
+    return IDENTIFIER_HASH_VALUE (name);
+}
+
+hashval_t
+module_state_hash::hash (const value_type m)
+{
+  hashval_t ph = pointer_hash<void>::hash
+    (reinterpret_cast<void *> (reinterpret_cast<uintptr_t> (m->parent)
+			       | m->is_partition ()));
+  hashval_t nh = module_name_hash (m->name);
+  return iterative_hash_hashval_t (ph, nh);
+}
+
+/* Hash a name.  */
+hashval_t
+module_state_hash::hash (const compare_type &c)
+{
+  hashval_t ph = pointer_hash<void>::hash (reinterpret_cast<void *> (c.second));
+  hashval_t nh = module_name_hash (c.first);
+
+  return iterative_hash_hashval_t (ph, nh);
+}
+
+bool
+module_state_hash::equal (const value_type existing,
+			  const compare_type &candidate)
+{
+  uintptr_t ep = (reinterpret_cast<uintptr_t> (existing->parent)
+		  | existing->is_partition ());
+  if (ep != candidate.second)
+    return false;
+
+  /* Identifier comparison is by pointer.  If the string_csts happen
+     to be the same object, then they're equal too.  */
+  if (existing->name == candidate.first)
+    return true;
+
+  /* If neither are string csts, they can't be equal.  */
+  if (TREE_CODE (candidate.first) != STRING_CST
+      || TREE_CODE (existing->name) != STRING_CST)
+    return false;
+
+  /* String equality.  */
+  if (TREE_STRING_LENGTH (existing->name)
+      == TREE_STRING_LENGTH (candidate.first)
+      && !memcmp (TREE_STRING_POINTER (existing->name),
+		  TREE_STRING_POINTER (candidate.first),
+		  TREE_STRING_LENGTH (existing->name)))
+    return true;
+
+  return false;
+}
+
+/********************************************************************/
+/* Global state */
+
+/* Mapper name.  */
+static const char *module_mapper_name;
+
+/* CMI repository path and workspace.  */
+static char *cmi_repo;
+static size_t cmi_repo_length;
+static char *cmi_path;
+static size_t cmi_path_alloc;
+
+/* Count of available and loaded clusters.  */
+static unsigned available_clusters;
+static unsigned loaded_clusters;
+
+/* What the current TU is.  */
+unsigned module_kind;
+
+/* Number of global init calls needed.  */
+unsigned num_init_calls_needed = 0;
+
+/* Global trees.  */
+static const std::pair<tree *, unsigned> global_tree_arys[] =
+  {
+    std::pair<tree *, unsigned> (sizetype_tab, stk_type_kind_last),
+    std::pair<tree *, unsigned> (integer_types, itk_none),
+    std::pair<tree *, unsigned> (global_trees, TI_MODULE_HWM),
+    std::pair<tree *, unsigned> (c_global_trees, CTI_MODULE_HWM),
+    std::pair<tree *, unsigned> (cp_global_trees, CPTI_MODULE_HWM),
+    std::pair<tree *, unsigned> (NULL, 0)
+  };
+static GTY(()) vec<tree, va_gc> *fixed_trees;
+static unsigned global_crc;
+
+/* Lazy loading can open many files concurrently, there are
+   per-process limits on that.  We pay attention to the process limit,
+   and attempt to increase it when we run out.  Otherwise we use an
+   LRU scheme to figure out who to flush.  Note that if the import
+   graph /depth/ exceeds lazy_limit, we'll exceed the limit.  */
+static unsigned lazy_lru;  /* LRU counter.  */
+static unsigned lazy_open; /* Number of open modules */
+static unsigned lazy_limit; /* Current limit of open modules.  */
+static unsigned lazy_hard_limit; /* Hard limit on open modules.  */
+/* Account for source, assembler and dump files & directory searches.
+   We don't keep the source file's open, so we don't have to account
+   for #include depth.  I think dump files are opened and closed per
+   pass, but ICBW.  */
+#define LAZY_HEADROOM 15 /* File descriptor headroom.  */
+
+/* Vector of module state.  Indexed by OWNER.  Has at least 2 slots.  */
+static GTY(()) vec<module_state *, va_gc> *modules;
+
+/* Hash of module state, findable by {name, parent}. */
+static GTY(()) hash_table<module_state_hash> *modules_hash;
+
+/* Map of imported entities.  We map DECL_UID to index of entity
+   vector.  */
+typedef hash_map<unsigned/*UID*/, unsigned/*index*/,
+		 simple_hashmap_traits<int_hash<unsigned,0>, unsigned>
+		 > entity_map_t;
+static entity_map_t *entity_map;
+/* Doesn't need GTYing, because any tree referenced here is also
+   findable by, symbol table, specialization table, return type of
+   reachable function.  */
+static vec<mc_slot, va_heap, vl_embed> *entity_ary;
+
+/* Members entities of imported classes that are defined in this TU.
+   These are where the entity's context is not from the current TU.
+   We need to emit the definition (but not the enclosing class).
+
+   We could find these by walking ALL the imported classes that we
+   could provide a member definition.  But that's expensive,
+   especially when you consider lazy implicit member declarations,
+   which could be ANY imported class.
+
+   Template instantiations are in the template table, so we don't
+   need to record those here.  */
+static GTY(()) vec<tree, va_gc> *class_members;
+
+/********************************************************************/
+
+/* Our module mapper (created lazily).  */
+module_client *mapper;
+
+static module_client *make_mapper (location_t loc);
+inline module_client *get_mapper (location_t loc)
+{
+  auto *res = mapper;
+  if (!res)
+    res = make_mapper (loc);
+  return res;
+}
+
+/********************************************************************/
+static tree
+get_clone_target (tree decl)
+{
+  tree target;
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      tree res_orig = DECL_CLONED_FUNCTION (DECL_TEMPLATE_RESULT (decl));
+      
+      target = DECL_TI_TEMPLATE (res_orig);
+    }
+  else
+    target = DECL_CLONED_FUNCTION (decl);
+
+  gcc_checking_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (target));
+
+  return target;
+}
+
+/* Like FOR_EACH_CLONE, but will walk cloned templates.  */
+#define FOR_EVERY_CLONE(CLONE, FN)			\
+  if (!DECL_MAYBE_IN_CHARGE_CDTOR_P (FN));		\
+  else							\
+    for (CLONE = DECL_CHAIN (FN);			\
+	 CLONE && DECL_CLONED_FUNCTION_P (CLONE);	\
+	 CLONE = DECL_CHAIN (CLONE))
+
+/* It'd be nice if USE_TEMPLATE was a field of template_info
+   (a) it'd solve the enum case dealt with below,
+   (b) both class templates and decl templates would store this in the
+   same place
+   (c) this function wouldn't need the by-ref arg, which is annoying.  */
+
+static tree
+node_template_info (tree decl, int &use)
+{
+  tree ti = NULL_TREE;
+  int use_tpl = -1;
+  if (DECL_IMPLICIT_TYPEDEF_P (decl))
+    {
+      tree type = TREE_TYPE (decl);
+
+      ti = TYPE_TEMPLATE_INFO (type);
+      if (ti)
+	{
+	  if (TYPE_LANG_SPECIFIC (type))
+	    use_tpl = CLASSTYPE_USE_TEMPLATE (type);
+	  else
+	    {
+	      /* An enum, where we don't explicitly encode use_tpl.
+		 If the containing context (a type or a function), is
+		 an ({im,ex}plicit) instantiation, then this is too.
+		 If it's a partial or explicit specialization, then
+		 this is not!.  */
+	      tree ctx = CP_DECL_CONTEXT (decl);
+	      if (TYPE_P (ctx))
+		ctx = TYPE_NAME (ctx);
+	      node_template_info (ctx, use);
+	      use_tpl = use != 2 ? use : 0;
+	    }
+	}
+    }
+  else if (DECL_LANG_SPECIFIC (decl)
+	   && (TREE_CODE (decl) == VAR_DECL
+	       || TREE_CODE (decl) == TYPE_DECL
+	       || TREE_CODE (decl) == FUNCTION_DECL
+	       || TREE_CODE (decl) == FIELD_DECL
+	       || TREE_CODE (decl) == TEMPLATE_DECL))
+    {
+      use_tpl = DECL_USE_TEMPLATE (decl);
+      ti = DECL_TEMPLATE_INFO (decl);
+    }
+
+  use = use_tpl;
+  return ti;
+}
+
+/* Find the index in entity_ary for an imported DECL.  It should
+   always be there, but bugs can cause it to be missing, and that can
+   crash the crash reporting -- let's not do that!  When streaming
+   out we place entities from this module there too -- with negated
+   indices.  */
+
+static unsigned
+import_entity_index (tree decl, bool null_ok = false)
+{
+  if (unsigned *slot = entity_map->get (DECL_UID (decl)))
+    return *slot;
+
+  gcc_checking_assert (null_ok);
+  return ~(~0u >> 1);
+}
+
+/* Find the module for an imported entity at INDEX in the entity ary.
+   There must be one.  */
+
+static module_state *
+import_entity_module (unsigned index)
+{
+  if (index > ~(~0u >> 1))
+    /* This is an index for an exported entity.  */
+    return (*modules)[0];
+
+  unsigned pos = 1;
+  unsigned len = modules->length () - pos;
+  while (len)
+    {
+      unsigned half = len / 2;
+      module_state *probe = (*modules)[pos + half];
+      if (index < probe->entity_lwm)
+	len = half;
+      else if (index < probe->entity_lwm + probe->entity_num)
+	return probe;
+      else
+	{
+	  pos += half + 1;
+	  len = len - (half + 1);
+	}
+    }
+  gcc_unreachable ();
+}
+
+
+/********************************************************************/
+/* A dumping machinery.  */
+
+class dumper {
+public:
+  enum {
+    LOCATION = TDF_LINENO,  /* -lineno:Source location streaming.  */
+    DEPEND = TDF_GRAPH,	/* -graph:Dependency graph construction.  */
+    CLUSTER = TDF_BLOCKS,   /* -blocks:Clusters.  */
+    TREE = TDF_UID, 	/* -uid:Tree streaming.  */
+    MERGE = TDF_ALIAS,	/* -alias:Mergeable Entities.  */
+    ELF = TDF_ASMNAME,	/* -asmname:Elf data.  */
+    MACRO = TDF_VOPS	/* -vops:Macros.  */
+  };
+
+private:
+  struct impl {
+    typedef vec<module_state *, va_heap, vl_embed> stack_t;
+
+    FILE *stream;	/* Dump stream.  */
+    unsigned indent; 	/* Local indentation.  */
+    bool bol; 		/* Beginning of line.  */
+    stack_t stack;	/* Trailing array of module_state.  */
+
+    bool nested_name (tree);  /* Dump a name following DECL_CONTEXT.  */
+  };
+
+public:
+  /* The dumper.  */
+  impl *dumps;
+  dump_flags_t flags;
+
+public:
+  /* Push/pop module state dumping.  */
+  unsigned push (module_state *);
+  void pop (unsigned);
+
+public:
+  /* Change local indentation.  */
+  void indent ()
+  {
+    if (dumps)
+      dumps->indent++;
+  }
+  void outdent ()
+  {
+    if (dumps)
+      {
+	gcc_checking_assert (dumps->indent);
+	dumps->indent--;
+      }
+  }
+
+public:
+  /* Is dump enabled?.  */
+  bool operator () (int mask = 0)
+  {
+    if (!dumps || !dumps->stream)
+      return false;
+    if (mask && !(mask & flags))
+      return false;
+    return true;
+  }
+  /* Dump some information.  */
+  bool operator () (const char *, ...);
+};
+
+/* The dumper.  */
+static dumper dump = {0, dump_flags_t (0)};
+
+/* Push to dumping M.  Return previous indentation level.  */
+
+unsigned
+dumper::push (module_state *m)
+{
+  FILE *stream = NULL;
+  if (!dumps || !dumps->stack.length ())
+    {
+      stream = dump_begin (module_dump_id, &flags);
+      if (!stream)
+	return 0;
+    }
+
+  if (!dumps || !dumps->stack.space (1))
+    {
+      /* Create or extend the dump implementor.  */
+      unsigned current = dumps ? dumps->stack.length () : 0;
+      unsigned count = current ? current * 2 : EXPERIMENT (1, 20);
+      size_t alloc = (offsetof (impl, impl::stack)
+		      + impl::stack_t::embedded_size (count));
+      dumps = XRESIZEVAR (impl, dumps, alloc);
+      dumps->stack.embedded_init (count, current);
+    }
+  if (stream)
+    dumps->stream = stream;
+
+  unsigned n = dumps->indent;
+  dumps->indent = 0;
+  dumps->bol = true;
+  dumps->stack.quick_push (m);
+  if (m)
+    {
+      module_state *from = NULL;
+
+      if (dumps->stack.length () > 1)
+	from = dumps->stack[dumps->stack.length () - 2];
+      else
+	dump ("");
+      dump (from ? "Starting module %M (from %M)"
+	    : "Starting module %M", m, from);
+    }
+
+  return n;
+}
+
+/* Pop from dumping.  Restore indentation to N.  */
+
+void dumper::pop (unsigned n)
+{
+  if (!dumps)
+    return;
+
+  gcc_checking_assert (dump () && !dumps->indent);
+  if (module_state *m = dumps->stack[dumps->stack.length () - 1])
+    {
+      module_state *from = (dumps->stack.length () > 1
+			    ? dumps->stack[dumps->stack.length () - 2] : NULL);
+      dump (from ? "Finishing module %M (returning to %M)"
+	    : "Finishing module %M", m, from);
+    }
+  dumps->stack.pop ();
+  dumps->indent = n;
+  if (!dumps->stack.length ())
+    {
+      dump_end (module_dump_id, dumps->stream);
+      dumps->stream = NULL;
+    }
+}
+
+/* Dump a nested name for arbitrary tree T.  Sometimes it won't have a
+   name.  */
+
+bool
+dumper::impl::nested_name (tree t)
+{
+  tree ti = NULL_TREE;
+  int origin = -1;
+  tree name = NULL_TREE;
+
+  if (t && TREE_CODE (t) == TREE_BINFO)
+    t = BINFO_TYPE (t);
+
+  if (t && TYPE_P (t))
+    t = TYPE_NAME (t);
+
+  if (t && DECL_P (t))
+    {
+      if (t == global_namespace || DECL_TEMPLATE_PARM_P (t))
+	;
+      else if (tree ctx = DECL_CONTEXT (t))
+	if (TREE_CODE (ctx) == TRANSLATION_UNIT_DECL
+	    || nested_name (ctx))
+	  fputs ("::", stream);
+
+      int use_tpl;
+      ti = node_template_info (t, use_tpl);
+      if (ti && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL
+	  && (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == t))
+	t = TI_TEMPLATE (ti);
+      if (TREE_CODE (t) == TEMPLATE_DECL)
+	fputs ("template ", stream);
+
+      if (DECL_LANG_SPECIFIC (t) && DECL_MODULE_IMPORT_P (t))
+	{
+	  /* We need to be careful here, so as to not explode on
+	     inconsistent data -- we're probably debugging, because
+	     Something Is Wrong.  */
+	  unsigned index = import_entity_index (t, true);
+	  if (!(index & ~(~0u >> 1)))
+	    origin = import_entity_module (index)->mod;
+	  else if (index > ~(~0u >> 1))
+	    /* An imported partition member that we're emitting.  */
+	    origin = 0;
+	  else
+	    origin = -2;
+	}
+
+      name = DECL_NAME (t) ? DECL_NAME (t)
+	: HAS_DECL_ASSEMBLER_NAME_P (t) ? DECL_ASSEMBLER_NAME_RAW (t)
+	: NULL_TREE;
+    }
+  else
+    name = t;
+
+  if (name)
+    switch (TREE_CODE (name))
+      {
+      default:
+	fputs ("#unnamed#", stream);
+	break;
+
+      case IDENTIFIER_NODE:
+	fwrite (IDENTIFIER_POINTER (name), 1, IDENTIFIER_LENGTH (name), stream);
+	break;
+
+      case INTEGER_CST:
+	print_hex (wi::to_wide (name), stream);
+	break;
+
+      case STRING_CST:
+	/* If TREE_TYPE is NULL, this is a raw string.  */
+	fwrite (TREE_STRING_POINTER (name), 1,
+		TREE_STRING_LENGTH (name) - (TREE_TYPE (name) != NULL_TREE),
+		stream);
+	break;
+      }
+  else
+    fputs ("#null#", stream);
+
+  if (origin >= 0)
+    {
+      const module_state *module = (*modules)[origin];
+      fprintf (stream, "@%s:%d", !module ? "" : !module->name ? "(unnamed)"
+	       : module->get_flatname (), origin);
+    }
+  else if (origin == -2)
+    fprintf (stream, "@???");
+
+  if (ti)
+    {
+      tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti));
+      fputs ("<", stream);
+      if (args)
+	for (int ix = 0; ix != TREE_VEC_LENGTH (args); ix++)
+	  {
+	    if (ix)
+	      fputs (",", stream);
+	    nested_name (TREE_VEC_ELT (args, ix));
+	  }
+      fputs (">", stream);
+    }
+
+  return true;
+}
+
+/* Formatted dumping.  FORMAT begins with '+' do not emit a trailing
+   new line.  (Normally it is appended.)
+   Escapes:
+      %C - tree_code
+      %I - identifier
+      %M - module_state
+      %N - name -- DECL_NAME
+      %P - context:name pair
+      %R - unsigned:unsigned ratio
+      %S - symbol -- DECL_ASSEMBLER_NAME
+      %U - long unsigned
+      %V - version
+      --- the following are printf-like, but without its flexibility
+      %d - decimal int
+      %p - pointer
+      %s - string
+      %u - unsigned int
+      %x - hex int
+
+  We do not implement the printf modifiers.  */
+
+bool
+dumper::operator () (const char *format, ...)
+{
+  if (!(*this) ())
+    return false;
+
+  bool no_nl = format[0] == '+';
+  format += no_nl;
+
+  if (dumps->bol)
+    {
+      /* Module import indent.  */
+      if (unsigned depth = dumps->stack.length () - 1)
+	{
+	  const char *prefix = ">>>>";
+	  fprintf (dumps->stream, (depth <= strlen (prefix)
+				   ? &prefix[strlen (prefix) - depth]
+				   : ">.%d.>"), depth);
+	}
+
+      /* Local indent.  */
+      if (unsigned indent = dumps->indent)
+	{
+	  const char *prefix = "      ";
+	  fprintf (dumps->stream, (indent <= strlen (prefix)
+				   ? &prefix[strlen (prefix) - indent]
+				   : "  .%d.  "), indent);
+	}
+      dumps->bol = false;
+    }
+
+  va_list args;
+  va_start (args, format);
+  while (const char *esc = strchr (format, '%'))
+    {
+      fwrite (format, 1, (size_t)(esc - format), dumps->stream);
+      format = ++esc;
+      switch (*format++)
+	{
+	default:
+	  gcc_unreachable ();
+
+	case '%':
+	  fputc ('%', dumps->stream);
+	  break;
+
+	case 'C': /* Code */
+	  {
+	    tree_code code = (tree_code)va_arg (args, unsigned);
+	    fputs (get_tree_code_name (code), dumps->stream);
+	  }
+	  break;
+
+	case 'I': /* Identifier.  */
+	  {
+	    tree t = va_arg (args, tree);
+	    dumps->nested_name (t);
+	  }
+	  break;
+
+	case 'M': /* Module. */
+	  {
+	    const char *str = "(none)";
+	    if (module_state *m = va_arg (args, module_state *))
+	      {
+		if (!m->is_rooted ())
+		  str = "(detached)";
+		else
+		  str = m->get_flatname ();
+	      }
+	    fputs (str, dumps->stream);
+	  }
+	  break;
+
+	case 'N': /* Name.  */
+	  {
+	    tree t = va_arg (args, tree);
+	    if (t && TREE_CODE (t) == OVERLOAD)
+	      t = OVL_FIRST (t);
+	    fputc ('\'', dumps->stream);
+	    dumps->nested_name (t);
+	    fputc ('\'', dumps->stream);
+	  }
+	  break;
+
+	case 'P': /* Pair.  */
+	  {
+	    tree ctx = va_arg (args, tree);
+	    tree name = va_arg (args, tree);
+	    fputc ('\'', dumps->stream);
+	    dumps->nested_name (ctx);
+	    if (ctx && ctx != global_namespace)
+	      fputs ("::", dumps->stream);
+	    dumps->nested_name (name);
+	    fputc ('\'', dumps->stream);
+	  }
+	  break;
+
+	case 'R': /* Ratio */
+	  {
+	    unsigned a = va_arg (args, unsigned);
+	    unsigned b = va_arg (args, unsigned);
+	    fprintf (dumps->stream, "%.1f", (float) a / (b + !b));
+	  }
+	  break;
+
+	case 'S': /* Symbol name */
+	  {
+	    tree t = va_arg (args, tree);
+	    if (t && TYPE_P (t))
+	      t = TYPE_NAME (t);
+	    if (t && HAS_DECL_ASSEMBLER_NAME_P (t)
+		&& DECL_ASSEMBLER_NAME_SET_P (t))
+	      {
+		fputc ('(', dumps->stream);
+		fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (t)),
+		       dumps->stream);
+		fputc (')', dumps->stream);
+	      }
+	  }
+	  break;
+
+	case 'U': /* long unsigned.  */
+	  {
+	    unsigned long u = va_arg (args, unsigned long);
+	    fprintf (dumps->stream, "%lu", u);
+	  }
+	  break;
+
+	case 'V': /* Verson.  */
+	  {
+	    unsigned v = va_arg (args, unsigned);
+	    verstr_t string;
+
+	    version2string (v, string);
+	    fputs (string, dumps->stream);
+	  }
+	  break;
+
+	case 'c': /* Character.  */
+	  {
+	    int c = va_arg (args, int);
+	    fputc (c, dumps->stream);
+	  }
+	  break;
+
+	case 'd': /* Decimal Int.  */
+	  {
+	    int d = va_arg (args, int);
+	    fprintf (dumps->stream, "%d", d);
+	  }
+	  break;
+
+	case 'p': /* Pointer. */
+	  {
+	    void *p = va_arg (args, void *);
+	    fprintf (dumps->stream, "%p", p);
+	  }
+	  break;
+
+	case 's': /* String. */
+	  {
+	    const char *s = va_arg (args, char *);
+	    gcc_checking_assert (s);
+	    fputs (s, dumps->stream);
+	  }
+	  break;
+
+	case 'u': /* Unsigned.  */
+	  {
+	    unsigned u = va_arg (args, unsigned);
+	    fprintf (dumps->stream, "%u", u);
+	  }
+	  break;
+
+	case 'x': /* Hex. */
+	  {
+	    unsigned x = va_arg (args, unsigned);
+	    fprintf (dumps->stream, "%x", x);
+	  }
+	  break;
+	}
+    }
+  fputs (format, dumps->stream);
+  va_end (args);
+  if (!no_nl)
+    {
+      dumps->bol = true;
+      fputc ('\n', dumps->stream);
+    }
+  return true;
+}
+
+struct note_def_cache_hasher : ggc_cache_ptr_hash<tree_node>
+{
+  static int keep_cache_entry (tree t)
+  {
+    if (!CHECKING_P)
+      /* GTY is unfortunately not clever enough to conditionalize
+	 this.  */
+      gcc_unreachable ();
+
+    if (ggc_marked_p (t))
+      return -1;
+
+    unsigned n = dump.push (NULL);
+    /* This might or might not be an error.  We should note its
+       dropping whichever.  */
+    dump () && dump ("Dropping %N from note_defs table", t);
+    dump.pop (n);
+
+    return 0;
+  }
+};
+
+/* We should stream each definition at most once.
+   This needs to be a cache because there are cases where a definition
+   ends up being not retained, and we need to drop those so we don't
+   get confused if memory is reallocated.  */
+typedef hash_table<note_def_cache_hasher> note_defs_table_t;
+static GTY((cache)) note_defs_table_t *note_defs;
+
+void
+trees_in::assert_definition (tree decl ATTRIBUTE_UNUSED,
+			     bool installing ATTRIBUTE_UNUSED)
+{
+#if CHECKING_P
+  tree *slot = note_defs->find_slot (decl, installing ? INSERT : NO_INSERT);
+  if (installing)
+    {
+      /* We must be inserting for the first time.  */
+      gcc_assert (!*slot);
+      *slot = decl;
+    }
+  else
+    /* If this is not the mergeable entity, it should not be in the
+       table.  If it is a non-global-module mergeable entity, it
+       should be in the table.  Global module entities could have been
+       defined textually in the current TU and so might or might not
+       be present.  */
+    gcc_assert (!is_duplicate (decl)
+		? !slot
+		: (slot
+		   || !DECL_LANG_SPECIFIC (decl)
+		   || !DECL_MODULE_PURVIEW_P (decl)
+		   || (!DECL_MODULE_IMPORT_P (decl)
+		       && header_module_p ())));
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    gcc_assert (!note_defs->find_slot (DECL_TEMPLATE_RESULT (decl), NO_INSERT));
+#endif
+}
+
+void
+trees_out::assert_definition (tree decl ATTRIBUTE_UNUSED)
+{
+#if CHECKING_P
+  tree *slot = note_defs->find_slot (decl, INSERT);
+  gcc_assert (!*slot);
+  *slot = decl;
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    gcc_assert (!note_defs->find_slot (DECL_TEMPLATE_RESULT (decl), NO_INSERT));
+#endif
+}
+
+/********************************************************************/
+static bool
+noisy_p ()
+{
+  if (quiet_flag)
+    return false;
+
+  pp_needs_newline (global_dc->printer) = true;
+  diagnostic_set_last_function (global_dc, (diagnostic_info *) NULL);
+
+  return true;
+}
+
+/* Set the cmi repo.  Strip trailing '/', '.' becomes NULL.  */
+
+static void
+set_cmi_repo (const char *r)
+{
+  XDELETEVEC (cmi_repo);
+  XDELETEVEC (cmi_path);
+  cmi_path_alloc = 0;
+
+  cmi_repo = NULL;
+  cmi_repo_length = 0;
+
+  if (!r || !r[0])
+    return;
+
+  size_t len = strlen (r);
+  cmi_repo = XNEWVEC (char, len + 1);
+  memcpy (cmi_repo, r, len + 1);
+  
+  if (len > 1 && IS_DIR_SEPARATOR (cmi_repo[len-1]))
+    len--;
+  if (len == 1 && cmi_repo[0] == '.')
+    len--;
+  cmi_repo[len] = 0;
+  cmi_repo_length = len;
+}
+
+/* TO is a repo-relative name.  Provide one that we may use from where
+   we are.  */
+
+static const char *
+maybe_add_cmi_prefix (const char *to, size_t *len_p = NULL)
+{
+  size_t len = len_p || cmi_repo_length ? strlen (to) : 0;
+
+  if (cmi_repo_length && !IS_ABSOLUTE_PATH (to))
+    {
+      if (cmi_path_alloc < cmi_repo_length + len + 2)
+	{
+	  XDELETEVEC (cmi_path);
+	  cmi_path_alloc = cmi_repo_length + len * 2 + 2;
+	  cmi_path = XNEWVEC (char, cmi_path_alloc);
+
+	  memcpy (cmi_path, cmi_repo, cmi_repo_length);
+	  cmi_path[cmi_repo_length] = DIR_SEPARATOR;
+	}
+
+      memcpy (&cmi_path[cmi_repo_length + 1], to, len + 1);
+      len += cmi_repo_length + 1;
+      to = cmi_path;
+    }
+
+  if (len_p)
+    *len_p = len;
+
+  return to;
+}
+
+/* Try and create the directories of PATH.  */
+
+static void
+create_dirs (char *path)
+{
+  /* Try and create the missing directories.  */
+  for (char *base = path; *base; base++)
+    if (IS_DIR_SEPARATOR (*base))
+      {
+	char sep = *base;
+	*base = 0;
+	int failed = mkdir (path, S_IRWXU | S_IRWXG | S_IRWXO);
+	*base = sep;
+	if (failed
+	    /* Maybe racing with another creator (of a *different*
+	       module).  */
+	    && errno != EEXIST)
+	  break;
+      }
+}
+
+/* Given a CLASSTYPE_DECL_LIST VALUE get the the template friend decl,
+   if that's what this is.  */
+
+static tree
+friend_from_decl_list (tree frnd)
+{
+  tree res = frnd;
+
+  if (TREE_CODE (frnd) != TEMPLATE_DECL)
+    {
+      tree tmpl = NULL_TREE;
+      if (TYPE_P (frnd))
+	{
+	  res = TYPE_NAME (frnd);
+	  if (CLASSTYPE_TEMPLATE_INFO (frnd))
+	    tmpl = CLASSTYPE_TI_TEMPLATE (frnd);
+	}
+      else if (DECL_TEMPLATE_INFO (frnd))
+	{
+	  tmpl = DECL_TI_TEMPLATE (frnd);
+	  if (TREE_CODE (tmpl) != TEMPLATE_DECL)
+	    tmpl = NULL_TREE;
+	}
+
+      if (tmpl && DECL_TEMPLATE_RESULT (tmpl) == res)
+	res = tmpl;
+    }
+
+  return res;
+}
+
+static tree
+find_enum_member (tree ctx, tree name)
+{
+  for (tree values = TYPE_VALUES (ctx);
+       values; values = TREE_CHAIN (values))
+    if (DECL_NAME (TREE_VALUE (values)) == name)
+      return TREE_VALUE (values);
+
+  return NULL_TREE;
+}
+
+/********************************************************************/
+/* Instrumentation gathered writing bytes.  */
+
+void
+bytes_out::instrument ()
+{
+  dump ("Wrote %u bytes in %u blocks", lengths[3], spans[3]);
+  dump ("Wrote %u bits in %u bytes", lengths[0] + lengths[1], lengths[2]);
+  for (unsigned ix = 0; ix < 2; ix++)
+    dump ("  %u %s spans of %R bits", spans[ix],
+	  ix ? "one" : "zero", lengths[ix], spans[ix]);
+  dump ("  %u blocks with %R bits padding", spans[2],
+	lengths[2] * 8 - (lengths[0] + lengths[1]), spans[2]);
+}
+
+/* Instrumentation gathered writing trees.  */
+void
+trees_out::instrument ()
+{
+  if (dump (""))
+    {
+      bytes_out::instrument ();
+      dump ("Wrote:");
+      dump ("  %u decl trees", decl_val_count);
+      dump ("  %u other trees", tree_val_count);
+      dump ("  %u back references", back_ref_count);
+      dump ("  %u null trees", null_count);
+    }
+}
+
+/* Setup and teardown for a tree walk.  */
+
+void
+trees_out::begin ()
+{
+  gcc_assert (!streaming_p () || !tree_map.elements ());
+
+  mark_trees ();
+  if (streaming_p ())
+    parent::begin ();
+}
+
+unsigned
+trees_out::end (elf_out *sink, unsigned name, unsigned *crc_ptr)
+{
+  gcc_checking_assert (streaming_p ());
+
+  unmark_trees ();
+  return parent::end (sink, name, crc_ptr);
+}
+
+void
+trees_out::end ()
+{
+  gcc_assert (!streaming_p ());
+
+  unmark_trees ();
+  /* Do not parent::end -- we weren't streaming.  */
+}
+
+void
+trees_out::mark_trees ()
+{
+  if (size_t size = tree_map.elements ())
+    {
+      /* This isn't our first rodeo, destroy and recreate the
+	 tree_map.  I'm a bad bad man.  Use the previous size as a
+	 guess for the next one (so not all bad).  */
+      tree_map.~ptr_int_hash_map ();
+      new (&tree_map) ptr_int_hash_map (size);
+    }
+
+  /* Install the fixed trees, with +ve references.  */
+  unsigned limit = fixed_trees->length ();
+  for (unsigned ix = 0; ix != limit; ix++)
+    {
+      tree val = (*fixed_trees)[ix];
+      bool existed = tree_map.put (val, ix + tag_fixed);
+      gcc_checking_assert (!TREE_VISITED (val) && !existed);
+      TREE_VISITED (val) = true;
+    }
+
+  ref_num = 0;
+}
+
+/* Unmark the trees we encountered  */
+
+void
+trees_out::unmark_trees ()
+{
+  ptr_int_hash_map::iterator end (tree_map.end ());
+  for (ptr_int_hash_map::iterator iter (tree_map.begin ()); iter != end; ++iter)
+    {
+      tree node = reinterpret_cast<tree> ((*iter).first);
+      int ref = (*iter).second;
+      /* We should have visited the node, and converted its mergeable
+	 reference to a regular reference.  */
+      gcc_checking_assert (TREE_VISITED (node)
+			   && (ref <= tag_backref || ref >= tag_fixed));
+      TREE_VISITED (node) = false;
+    }
+}
+
+/* Mark DECL for by-value walking.  We do this by inserting it into
+   the tree map with a reference of zero.  May be called multiple
+   times on the same node.  */
+
+void
+trees_out::mark_by_value (tree decl)
+{
+  gcc_checking_assert (DECL_P (decl)
+		       /* Enum consts are INTEGER_CSTS.  */
+		       || TREE_CODE (decl) == INTEGER_CST
+		       || TREE_CODE (decl) == TREE_BINFO);
+
+  if (TREE_VISITED (decl))
+    /* Must already be forced or fixed.  */
+    gcc_checking_assert (*tree_map.get (decl) >= tag_value);
+  else
+    {
+      bool existed = tree_map.put (decl, tag_value);
+      gcc_checking_assert (!existed);
+      TREE_VISITED (decl) = true;
+    }
+}
+
+int
+trees_out::get_tag (tree t)
+{
+  gcc_checking_assert (TREE_VISITED (t));
+  return *tree_map.get (t);
+}
+
+/* Insert T into the map, return its tag number.    */
+
+int
+trees_out::insert (tree t, walk_kind walk)
+{
+  gcc_checking_assert (walk != WK_normal || !TREE_VISITED (t));
+  int tag = --ref_num;
+  bool existed;
+  int &slot = tree_map.get_or_insert (t, &existed);
+  gcc_checking_assert (TREE_VISITED (t) == existed
+		       && (!existed
+			   || (walk == WK_value && slot == tag_value)));
+  TREE_VISITED (t) = true;
+  slot = tag;
+
+  return tag;
+}
+
+/* Insert T into the backreference array.  Return its back reference
+   number.  */
+
+int
+trees_in::insert (tree t)
+{
+  gcc_checking_assert (t || get_overrun ());
+  back_refs.safe_push (t);
+  return -(int)back_refs.length ();
+}
+
+/* A chained set of decls.  */
+
+void
+trees_out::chained_decls (tree decls)
+{
+  for (; decls; decls = DECL_CHAIN (decls))
+    {
+      if (VAR_OR_FUNCTION_DECL_P (decls)
+	  && DECL_LOCAL_DECL_P (decls))
+	{
+	  /* Make sure this is the first encounter, and mark for
+	     walk-by-value.  */
+	  gcc_checking_assert (!TREE_VISITED (decls)
+			       && !DECL_TEMPLATE_INFO (decls));
+	  mark_by_value (decls);
+	}
+      tree_node (decls);
+    }
+  tree_node (NULL_TREE);
+}
+
+tree
+trees_in::chained_decls ()
+{
+  tree decls = NULL_TREE;
+  for (tree *chain = &decls;;)
+    if (tree decl = tree_node ())
+      {
+	if (!DECL_P (decl) || DECL_CHAIN (decl))
+	  {
+	    set_overrun ();
+	    break;
+	  }
+	*chain = decl;
+	chain = &DECL_CHAIN (decl);
+      }
+    else
+      break;
+
+  return decls;
+}
+
+/* A vector of decls following DECL_CHAIN.  */
+
+void
+trees_out::vec_chained_decls (tree decls)
+{
+  if (streaming_p ())
+    {
+      unsigned len = 0;
+
+      for (tree decl = decls; decl; decl = DECL_CHAIN (decl))
+	len++;
+      u (len);
+    }
+
+  for (tree decl = decls; decl; decl = DECL_CHAIN (decl))
+    {
+      if (DECL_IMPLICIT_TYPEDEF_P (decl)
+	  && TYPE_NAME (TREE_TYPE (decl)) != decl)
+	/* An anonynmous struct with a typedef name.  An odd thing to
+	   write.  */
+	tree_node (NULL_TREE);
+      else
+	tree_node (decl);
+    }
+}
+
+vec<tree, va_heap> *
+trees_in::vec_chained_decls ()
+{
+  vec<tree, va_heap> *v = NULL;
+
+  if (unsigned len = u ())
+    {
+      vec_alloc (v, len);
+
+      for (unsigned ix = 0; ix < len; ix++)
+	{
+	  tree decl = tree_node ();
+	  if (decl && !DECL_P (decl))
+	    {
+	      set_overrun ();
+	      break;
+	    }
+	  v->quick_push (decl);
+	}
+
+      if (get_overrun ())
+	{
+	  vec_free (v);
+	  v = NULL;
+	}
+    }
+
+  return v;
+}
+
+/* A vector of trees.  */
+
+void
+trees_out::tree_vec (vec<tree, va_gc> *v)
+{
+  unsigned len = vec_safe_length (v);
+  if (streaming_p ())
+    u (len);
+  for (unsigned ix = 0; ix != len; ix++)
+    tree_node ((*v)[ix]);
+}
+
+vec<tree, va_gc> *
+trees_in::tree_vec ()
+{
+  vec<tree, va_gc> *v = NULL;
+  if (unsigned len = u ())
+    {
+      vec_alloc (v, len);
+      for (unsigned ix = 0; ix != len; ix++)
+	v->quick_push (tree_node ());
+    }
+  return v;
+}
+
+/* A vector of tree pairs.  */
+
+void
+trees_out::tree_pair_vec (vec<tree_pair_s, va_gc> *v)
+{
+  unsigned len = vec_safe_length (v);
+  if (streaming_p ())
+    u (len);
+  if (len)
+    for (unsigned ix = 0; ix != len; ix++)
+      {
+	tree_pair_s const &s = (*v)[ix];
+	tree_node (s.purpose);
+	tree_node (s.value);
+      }
+}
+
+vec<tree_pair_s, va_gc> *
+trees_in::tree_pair_vec ()
+{
+  vec<tree_pair_s, va_gc> *v = NULL;
+  if (unsigned len = u ())
+    {
+      vec_alloc (v, len);
+      for (unsigned ix = 0; ix != len; ix++)
+	{
+	  tree_pair_s s;
+	  s.purpose = tree_node ();
+	  s.value = tree_node ();
+	  v->quick_push (s);
+      }
+    }
+  return v;
+}
+
+void
+trees_out::tree_list (tree list, bool has_purpose)
+{
+  for (; list; list = TREE_CHAIN (list))
+    {
+      gcc_checking_assert (TREE_VALUE (list));
+      tree_node (TREE_VALUE (list));
+      if (has_purpose)
+	tree_node (TREE_PURPOSE (list));
+    }
+  tree_node (NULL_TREE);
+}
+
+tree
+trees_in::tree_list (bool has_purpose)
+{
+  tree res = NULL_TREE;
+
+  for (tree *chain = &res; tree value = tree_node ();
+       chain = &TREE_CHAIN (*chain))
+    {
+      tree purpose = has_purpose ? tree_node () : NULL_TREE;
+      *chain = build_tree_list (purpose, value);
+    }
+
+  return res;
+}
+/* Start tree write.  Write information to allocate the receiving
+   node.  */
+
+void
+trees_out::start (tree t, bool code_streamed)
+{
+  if (TYPE_P (t))
+    {
+      enum tree_code code = TREE_CODE (t);
+      gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t);
+      /* All these types are TYPE_NON_COMMON.  */
+      gcc_checking_assert (code == RECORD_TYPE
+			   || code == UNION_TYPE
+			   || code == ENUMERAL_TYPE
+			   || code == TEMPLATE_TYPE_PARM
+			   || code == TEMPLATE_TEMPLATE_PARM
+			   || code == BOUND_TEMPLATE_TEMPLATE_PARM);
+    }
+
+  if (!code_streamed)
+    u (TREE_CODE (t));
+
+  switch (TREE_CODE (t))
+    {
+    default:
+      if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_vl_exp)
+	u (VL_EXP_OPERAND_LENGTH (t));
+      break;
+
+    case INTEGER_CST:
+      u (TREE_INT_CST_NUNITS (t));
+      u (TREE_INT_CST_EXT_NUNITS (t));
+      u (TREE_INT_CST_OFFSET_NUNITS (t));
+      break;
+
+    case OMP_CLAUSE:
+      state->extensions |= SE_OPENMP;
+      u (OMP_CLAUSE_CODE (t));
+      break;
+
+    case STRING_CST:
+      str (TREE_STRING_POINTER (t), TREE_STRING_LENGTH (t));
+      break;
+
+    case VECTOR_CST:
+      u (VECTOR_CST_LOG2_NPATTERNS (t));
+      u (VECTOR_CST_NELTS_PER_PATTERN (t));
+      break;
+
+    case TREE_BINFO:
+      u (BINFO_N_BASE_BINFOS (t));
+      break;
+
+    case TREE_VEC:
+      u (TREE_VEC_LENGTH (t));
+      break;
+
+    case FIXED_CST:
+    case POLY_INT_CST:
+      gcc_unreachable (); /* Not supported in C++.  */
+      break;
+
+    case IDENTIFIER_NODE:
+    case SSA_NAME:
+    case TARGET_MEM_REF:
+    case TRANSLATION_UNIT_DECL:
+      /* We shouldn't meet these.  */
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Start tree read.  Allocate the receiving node.  */
+
+tree
+trees_in::start (unsigned code)
+{
+  tree t = NULL_TREE;
+
+  if (!code)
+    code = u ();
+
+  switch (code)
+    {
+    default:
+      if (code >= MAX_TREE_CODES)
+	{
+	fail:
+	  set_overrun ();
+	  return NULL_TREE;
+	}
+      else if (TREE_CODE_CLASS (code) == tcc_vl_exp)
+	{
+	  unsigned ops = u ();
+	  t = build_vl_exp (tree_code (code), ops);
+	}
+      else
+	t = make_node (tree_code (code));
+      break;
+
+    case INTEGER_CST:
+      {
+	unsigned n = u ();
+	unsigned e = u ();
+	t = make_int_cst (n, e);
+	TREE_INT_CST_OFFSET_NUNITS(t) = u ();
+      }
+      break;
+
+    case OMP_CLAUSE:
+      {
+	if (!(state->extensions & SE_OPENMP))
+	  goto fail;
+
+	unsigned omp_code = u ();
+	t = build_omp_clause (UNKNOWN_LOCATION, omp_clause_code (omp_code));
+      }
+      break;
+
+    case STRING_CST:
+      {
+	size_t l;
+	const char *chars = str (&l);
+	t = build_string (l, chars);
+      }
+      break;
+
+    case VECTOR_CST:
+      {
+	unsigned log2_npats = u ();
+	unsigned elts_per = u ();
+	t = make_vector (log2_npats, elts_per);
+      }
+      break;
+
+    case TREE_BINFO:
+      t = make_tree_binfo (u ());
+      break;
+
+    case TREE_VEC:
+      t = make_tree_vec (u ());
+      break;
+
+    case FIXED_CST:
+    case IDENTIFIER_NODE:
+    case POLY_INT_CST:
+    case SSA_NAME:
+    case TARGET_MEM_REF:
+    case TRANSLATION_UNIT_DECL:
+      goto fail;
+    }
+
+  return t;
+}
+
+/* The structure streamers access the raw fields, because the
+   alternative, of using the accessor macros can require using
+   different accessors for the same underlying field, depending on the
+   tree code.  That's both confusing and annoying.  */
+
+/* Read & write the core boolean flags.  */
+
+void
+trees_out::core_bools (tree t)
+{
+#define WB(X) (b (X))
+  tree_code code = TREE_CODE (t);
+
+  WB (t->base.side_effects_flag);
+  WB (t->base.constant_flag);
+  WB (t->base.addressable_flag);
+  WB (t->base.volatile_flag);
+  WB (t->base.readonly_flag);
+  /* base.asm_written_flag is a property of the current TU's use of
+     this decl.  */
+  WB (t->base.nowarning_flag);
+  /* base.visited read as zero (it's set for writer, because that's
+     how we mark nodes).  */
+  /* base.used_flag is not streamed.  Readers may set TREE_USED of
+     decls they use.  */
+  WB (t->base.nothrow_flag);
+  WB (t->base.static_flag);
+  if (TREE_CODE_CLASS (code) != tcc_type)
+    /* This is TYPE_CACHED_VALUES_P for types.  */
+    WB (t->base.public_flag);
+  WB (t->base.private_flag);
+  WB (t->base.protected_flag);
+  WB (t->base.deprecated_flag);
+  WB (t->base.default_def_flag);
+
+  switch (code)
+    {
+    case CALL_EXPR:
+    case INTEGER_CST:
+    case SSA_NAME:
+    case TARGET_MEM_REF:
+    case TREE_VEC:
+      /* These use different base.u fields.  */
+      break;
+
+    default:
+      WB (t->base.u.bits.lang_flag_0);
+      bool flag_1 = t->base.u.bits.lang_flag_1;
+      if (!flag_1)
+	;
+      else if (code == TEMPLATE_INFO)
+	/* This is TI_PENDING_TEMPLATE_FLAG, not relevant to reader.  */
+	flag_1 = false;
+      else if (code == VAR_DECL)
+	{
+	  /* This is DECL_INITIALIZED_P.  */
+	  if (DECL_CONTEXT (t)
+	      && TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL)
+	    /* We'll set this when reading the definition.  */
+	    flag_1 = false;
+	}
+      WB (flag_1);
+      WB (t->base.u.bits.lang_flag_2);
+      WB (t->base.u.bits.lang_flag_3);
+      WB (t->base.u.bits.lang_flag_4);
+      WB (t->base.u.bits.lang_flag_5);
+      WB (t->base.u.bits.lang_flag_6);
+      WB (t->base.u.bits.saturating_flag);
+      WB (t->base.u.bits.unsigned_flag);
+      WB (t->base.u.bits.packed_flag);
+      WB (t->base.u.bits.user_align);
+      WB (t->base.u.bits.nameless_flag);
+      WB (t->base.u.bits.atomic_flag);
+      break;
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
+    {
+      WB (t->type_common.no_force_blk_flag);
+      WB (t->type_common.needs_constructing_flag);
+      WB (t->type_common.transparent_aggr_flag);
+      WB (t->type_common.restrict_flag);
+      WB (t->type_common.string_flag);
+      WB (t->type_common.lang_flag_0);
+      WB (t->type_common.lang_flag_1);
+      WB (t->type_common.lang_flag_2);
+      WB (t->type_common.lang_flag_3);
+      WB (t->type_common.lang_flag_4);
+      WB (t->type_common.lang_flag_5);
+      WB (t->type_common.lang_flag_6);
+      WB (t->type_common.typeless_storage);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON))
+    {
+      WB (t->decl_common.nonlocal_flag);
+      WB (t->decl_common.virtual_flag);
+      WB (t->decl_common.ignored_flag);
+      WB (t->decl_common.abstract_flag);
+      WB (t->decl_common.artificial_flag);
+      WB (t->decl_common.preserve_flag);
+      WB (t->decl_common.debug_expr_is_from);
+      WB (t->decl_common.lang_flag_0);
+      WB (t->decl_common.lang_flag_1);
+      WB (t->decl_common.lang_flag_2);
+      WB (t->decl_common.lang_flag_3);
+      WB (t->decl_common.lang_flag_4);
+      WB (t->decl_common.lang_flag_5);
+      WB (t->decl_common.lang_flag_6);
+      WB (t->decl_common.lang_flag_7);
+      WB (t->decl_common.lang_flag_8);
+      WB (t->decl_common.decl_flag_0);
+
+      {
+	/* DECL_EXTERNAL -> decl_flag_1
+	     == it is defined elsewhere
+	   DECL_NOT_REALLY_EXTERN -> base.not_really_extern
+	     == that was a lie, it is here  */
+
+	bool is_external = t->decl_common.decl_flag_1;
+	if (!is_external)
+	  /* decl_flag_1 is DECL_EXTERNAL. Things we emit here, might
+	     well be external from the POV of an importer.  */
+	  // FIXME: Do we need to know if this is a TEMPLATE_RESULT --
+	  // a flag from the caller?
+	  switch (code)
+	    {
+	    default:
+	      break;
+
+	    case VAR_DECL:
+	      if (TREE_PUBLIC (t)
+		  && !DECL_VAR_DECLARED_INLINE_P (t))
+		is_external = true;
+	      break;
+
+	    case FUNCTION_DECL:
+	      if (TREE_PUBLIC (t)
+		  && !DECL_DECLARED_INLINE_P (t))
+		is_external = true;
+	      break;
+	    }
+	WB (is_external);
+      }
+
+      WB (t->decl_common.decl_flag_2);
+      WB (t->decl_common.decl_flag_3);
+      WB (t->decl_common.not_gimple_reg_flag);
+      WB (t->decl_common.decl_by_reference_flag);
+      WB (t->decl_common.decl_read_flag);
+      WB (t->decl_common.decl_nonshareable_flag);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
+    {
+      WB (t->decl_with_vis.defer_output);
+      WB (t->decl_with_vis.hard_register);
+      WB (t->decl_with_vis.common_flag);
+      WB (t->decl_with_vis.in_text_section);
+      WB (t->decl_with_vis.in_constant_pool);
+      WB (t->decl_with_vis.dllimport_flag);
+      WB (t->decl_with_vis.weak_flag);
+      WB (t->decl_with_vis.seen_in_bind_expr);
+      WB (t->decl_with_vis.comdat_flag);
+      WB (t->decl_with_vis.visibility_specified);
+      WB (t->decl_with_vis.init_priority_p);
+      WB (t->decl_with_vis.shadowed_for_var_p);
+      WB (t->decl_with_vis.cxx_constructor);
+      WB (t->decl_with_vis.cxx_destructor);
+      WB (t->decl_with_vis.final);
+      WB (t->decl_with_vis.regdecl_flag);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
+    {
+      WB (t->function_decl.static_ctor_flag);
+      WB (t->function_decl.static_dtor_flag);
+      WB (t->function_decl.uninlinable);
+      WB (t->function_decl.possibly_inlined);
+      WB (t->function_decl.novops_flag);
+      WB (t->function_decl.returns_twice_flag);
+      WB (t->function_decl.malloc_flag);
+      WB (t->function_decl.declared_inline_flag);
+      WB (t->function_decl.no_inline_warning_flag);
+      WB (t->function_decl.no_instrument_function_entry_exit);
+      WB (t->function_decl.no_limit_stack);
+      WB (t->function_decl.disregard_inline_limits);
+      WB (t->function_decl.pure_flag);
+      WB (t->function_decl.looping_const_or_pure_flag);
+
+      WB (t->function_decl.has_debug_args_flag);
+      WB (t->function_decl.versioned_function);
+
+      /* decl_type is a (misnamed) 2 bit discriminator.	 */
+      unsigned kind = t->function_decl.decl_type;
+      WB ((kind >> 0) & 1);
+      WB ((kind >> 1) & 1);
+    }
+#undef WB
+}
+
+bool
+trees_in::core_bools (tree t)
+{
+#define RB(X) ((X) = b ())
+  tree_code code = TREE_CODE (t);
+
+  RB (t->base.side_effects_flag);
+  RB (t->base.constant_flag);
+  RB (t->base.addressable_flag);
+  RB (t->base.volatile_flag);
+  RB (t->base.readonly_flag);
+  /* base.asm_written_flag is not streamed.  */
+  RB (t->base.nowarning_flag);
+  /* base.visited is not streamed.  */
+  /* base.used_flag is not streamed.  */
+  RB (t->base.nothrow_flag);
+  RB (t->base.static_flag);
+  if (TREE_CODE_CLASS (code) != tcc_type)
+    RB (t->base.public_flag);
+  RB (t->base.private_flag);
+  RB (t->base.protected_flag);
+  RB (t->base.deprecated_flag);
+  RB (t->base.default_def_flag);
+
+  switch (code)
+    {
+    case CALL_EXPR:
+    case INTEGER_CST:
+    case SSA_NAME:
+    case TARGET_MEM_REF:
+    case TREE_VEC:
+      /* These use different base.u fields.  */
+      break;
+
+    default:
+      RB (t->base.u.bits.lang_flag_0);
+      RB (t->base.u.bits.lang_flag_1);
+      RB (t->base.u.bits.lang_flag_2);
+      RB (t->base.u.bits.lang_flag_3);
+      RB (t->base.u.bits.lang_flag_4);
+      RB (t->base.u.bits.lang_flag_5);
+      RB (t->base.u.bits.lang_flag_6);
+      RB (t->base.u.bits.saturating_flag);
+      RB (t->base.u.bits.unsigned_flag);
+      RB (t->base.u.bits.packed_flag);
+      RB (t->base.u.bits.user_align);
+      RB (t->base.u.bits.nameless_flag);
+      RB (t->base.u.bits.atomic_flag);
+      break;
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
+    {
+      RB (t->type_common.no_force_blk_flag);
+      RB (t->type_common.needs_constructing_flag);
+      RB (t->type_common.transparent_aggr_flag);
+      RB (t->type_common.restrict_flag);
+      RB (t->type_common.string_flag);
+      RB (t->type_common.lang_flag_0);
+      RB (t->type_common.lang_flag_1);
+      RB (t->type_common.lang_flag_2);
+      RB (t->type_common.lang_flag_3);
+      RB (t->type_common.lang_flag_4);
+      RB (t->type_common.lang_flag_5);
+      RB (t->type_common.lang_flag_6);
+      RB (t->type_common.typeless_storage);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON))
+    {
+      RB (t->decl_common.nonlocal_flag);
+      RB (t->decl_common.virtual_flag);
+      RB (t->decl_common.ignored_flag);
+      RB (t->decl_common.abstract_flag);
+      RB (t->decl_common.artificial_flag);
+      RB (t->decl_common.preserve_flag);
+      RB (t->decl_common.debug_expr_is_from);
+      RB (t->decl_common.lang_flag_0);
+      RB (t->decl_common.lang_flag_1);
+      RB (t->decl_common.lang_flag_2);
+      RB (t->decl_common.lang_flag_3);
+      RB (t->decl_common.lang_flag_4);
+      RB (t->decl_common.lang_flag_5);
+      RB (t->decl_common.lang_flag_6);
+      RB (t->decl_common.lang_flag_7);
+      RB (t->decl_common.lang_flag_8);
+      RB (t->decl_common.decl_flag_0);
+      RB (t->decl_common.decl_flag_1);
+      RB (t->decl_common.decl_flag_2);
+      RB (t->decl_common.decl_flag_3);
+      RB (t->decl_common.not_gimple_reg_flag);
+      RB (t->decl_common.decl_by_reference_flag);
+      RB (t->decl_common.decl_read_flag);
+      RB (t->decl_common.decl_nonshareable_flag);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
+    {
+      RB (t->decl_with_vis.defer_output);
+      RB (t->decl_with_vis.hard_register);
+      RB (t->decl_with_vis.common_flag);
+      RB (t->decl_with_vis.in_text_section);
+      RB (t->decl_with_vis.in_constant_pool);
+      RB (t->decl_with_vis.dllimport_flag);
+      RB (t->decl_with_vis.weak_flag);
+      RB (t->decl_with_vis.seen_in_bind_expr);
+      RB (t->decl_with_vis.comdat_flag);
+      RB (t->decl_with_vis.visibility_specified);
+      RB (t->decl_with_vis.init_priority_p);
+      RB (t->decl_with_vis.shadowed_for_var_p);
+      RB (t->decl_with_vis.cxx_constructor);
+      RB (t->decl_with_vis.cxx_destructor);
+      RB (t->decl_with_vis.final);
+      RB (t->decl_with_vis.regdecl_flag);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
+    {
+      RB (t->function_decl.static_ctor_flag);
+      RB (t->function_decl.static_dtor_flag);
+      RB (t->function_decl.uninlinable);
+      RB (t->function_decl.possibly_inlined);
+      RB (t->function_decl.novops_flag);
+      RB (t->function_decl.returns_twice_flag);
+      RB (t->function_decl.malloc_flag);
+      RB (t->function_decl.declared_inline_flag);
+      RB (t->function_decl.no_inline_warning_flag);
+      RB (t->function_decl.no_instrument_function_entry_exit);
+      RB (t->function_decl.no_limit_stack);
+      RB (t->function_decl.disregard_inline_limits);
+      RB (t->function_decl.pure_flag);
+      RB (t->function_decl.looping_const_or_pure_flag);
+      
+      RB (t->function_decl.has_debug_args_flag);
+      RB (t->function_decl.versioned_function);
+
+      /* decl_type is a (misnamed) 2 bit discriminator.	 */
+      unsigned kind = 0;
+      kind |= unsigned (b ()) << 0;
+      kind |= unsigned (b ()) << 1;
+      t->function_decl.decl_type = function_decl_type (kind);
+    }
+#undef RB
+  return !get_overrun ();
+}
+
+void
+trees_out::lang_decl_bools (tree t)
+{
+#define WB(X) (b (X))
+  const struct lang_decl *lang = DECL_LANG_SPECIFIC (t);
+
+  WB (lang->u.base.language == lang_cplusplus);
+  WB ((lang->u.base.use_template >> 0) & 1);
+  WB ((lang->u.base.use_template >> 1) & 1);
+  /* Do not write lang->u.base.not_really_extern, importer will set
+     when reading the definition (if any).  */
+  WB (lang->u.base.initialized_in_class);
+  WB (lang->u.base.threadprivate_or_deleted_p);
+  /* Do not write lang->u.base.anticipated_p, it is a property of the
+     current TU.  */
+  WB (lang->u.base.friend_or_tls);
+  WB (lang->u.base.unknown_bound_p);
+  /* Do not write lang->u.base.odr_used, importer will recalculate if
+     they do ODR use this decl.  */
+  WB (lang->u.base.concept_p);
+  WB (lang->u.base.var_declared_inline_p);
+  WB (lang->u.base.dependent_init_p);
+  WB (lang->u.base.module_purview_p);
+  WB (lang->u.base.attached_decls_p);
+  switch (lang->u.base.selector)
+    {
+    default:
+      gcc_unreachable ();
+
+    case lds_fn:  /* lang_decl_fn.  */
+      WB (lang->u.fn.global_ctor_p);
+      WB (lang->u.fn.global_dtor_p);
+      WB (lang->u.fn.static_function);
+      WB (lang->u.fn.pure_virtual);
+      WB (lang->u.fn.defaulted_p);
+      WB (lang->u.fn.has_in_charge_parm_p);
+      WB (lang->u.fn.has_vtt_parm_p);
+      /* There shouldn't be a pending inline at this point.  */
+      gcc_assert (!lang->u.fn.pending_inline_p);
+      WB (lang->u.fn.nonconverting);
+      WB (lang->u.fn.thunk_p);
+      WB (lang->u.fn.this_thunk_p);
+      /* Do not stream lang->u.hidden_friend_p, it is a property of
+	 the TU.  */
+      WB (lang->u.fn.omp_declare_reduction_p);
+      WB (lang->u.fn.has_dependent_explicit_spec_p);
+      WB (lang->u.fn.immediate_fn_p);
+      WB (lang->u.fn.maybe_deleted);
+      goto lds_min;
+
+    case lds_decomp:  /* lang_decl_decomp.  */
+      /* No bools.  */
+      goto lds_min;
+
+    case lds_min:  /* lang_decl_min.  */
+    lds_min:
+      /* No bools.  */
+      break;
+
+    case lds_ns:  /* lang_decl_ns.  */
+      /* No bools.  */
+      break;
+
+    case lds_parm:  /* lang_decl_parm.  */
+      /* No bools.  */
+      break;
+    }
+#undef WB
+}
+
+bool
+trees_in::lang_decl_bools (tree t)
+{
+#define RB(X) ((X) = b ())
+  struct lang_decl *lang = DECL_LANG_SPECIFIC (t);
+
+  lang->u.base.language = b () ? lang_cplusplus : lang_c;
+  unsigned v;
+  v = b () << 0;
+  v |= b () << 1;
+  lang->u.base.use_template = v;
+  /* lang->u.base.not_really_extern is not streamed.  */
+  RB (lang->u.base.initialized_in_class);
+  RB (lang->u.base.threadprivate_or_deleted_p);
+  /* lang->u.base.anticipated_p is not streamed.  */
+  RB (lang->u.base.friend_or_tls);
+  RB (lang->u.base.unknown_bound_p);
+  /* lang->u.base.odr_used is not streamed.  */
+  RB (lang->u.base.concept_p);
+  RB (lang->u.base.var_declared_inline_p);
+  RB (lang->u.base.dependent_init_p);
+  RB (lang->u.base.module_purview_p);
+  RB (lang->u.base.attached_decls_p);
+  switch (lang->u.base.selector)
+    {
+    default:
+      gcc_unreachable ();
+
+    case lds_fn:  /* lang_decl_fn.  */
+      RB (lang->u.fn.global_ctor_p);
+      RB (lang->u.fn.global_dtor_p);
+      RB (lang->u.fn.static_function);
+      RB (lang->u.fn.pure_virtual);
+      RB (lang->u.fn.defaulted_p);
+      RB (lang->u.fn.has_in_charge_parm_p);
+      RB (lang->u.fn.has_vtt_parm_p);
+      RB (lang->u.fn.nonconverting);
+      RB (lang->u.fn.thunk_p);
+      RB (lang->u.fn.this_thunk_p);
+      /* lang->u.fn.hidden_friend_p is not streamed.  */
+      RB (lang->u.fn.omp_declare_reduction_p);
+      RB (lang->u.fn.has_dependent_explicit_spec_p);
+      RB (lang->u.fn.immediate_fn_p);
+      RB (lang->u.fn.maybe_deleted);
+      goto lds_min;
+
+    case lds_decomp:  /* lang_decl_decomp.  */
+      /* No bools.  */
+      goto lds_min;
+
+    case lds_min:  /* lang_decl_min.  */
+    lds_min:
+      /* No bools.  */
+      break;
+
+    case lds_ns:  /* lang_decl_ns.  */
+      /* No bools.  */
+      break;
+
+    case lds_parm:  /* lang_decl_parm.  */
+      /* No bools.  */
+      break;
+    }
+#undef RB
+  return !get_overrun ();
+}
+
+void
+trees_out::lang_type_bools (tree t)
+{
+#define WB(X) (b (X))
+  const struct lang_type *lang = TYPE_LANG_SPECIFIC (t);
+
+  WB (lang->has_type_conversion);
+  WB (lang->has_copy_ctor);
+  WB (lang->has_default_ctor);
+  WB (lang->const_needs_init);
+  WB (lang->ref_needs_init);
+  WB (lang->has_const_copy_assign);
+  WB ((lang->use_template >> 0) & 1);
+  WB ((lang->use_template >> 1) & 1);
+
+  WB (lang->has_mutable);
+  WB (lang->com_interface);
+  WB (lang->non_pod_class);
+  WB (lang->nearly_empty_p);
+  WB (lang->user_align);
+  WB (lang->has_copy_assign);
+  WB (lang->has_new);
+  WB (lang->has_array_new);
+
+  WB ((lang->gets_delete >> 0) & 1);
+  WB ((lang->gets_delete >> 1) & 1);
+  // Interfaceness is recalculated upon reading.  May have to revisit?
+  // How do dllexport and dllimport interact across a module?
+  // lang->interface_only
+  // lang->interface_unknown
+  WB (lang->contains_empty_class_p);
+  WB (lang->anon_aggr);
+  WB (lang->non_zero_init);
+  WB (lang->empty_p);
+
+  WB (lang->vec_new_uses_cookie);
+  WB (lang->declared_class);
+  WB (lang->diamond_shaped);
+  WB (lang->repeated_base);
+  gcc_assert (!lang->being_defined);
+  // lang->debug_requested
+  WB (lang->fields_readonly);
+  WB (lang->ptrmemfunc_flag);
+
+  WB (lang->lazy_default_ctor);
+  WB (lang->lazy_copy_ctor);
+  WB (lang->lazy_copy_assign);
+  WB (lang->lazy_destructor);
+  WB (lang->has_const_copy_ctor);
+  WB (lang->has_complex_copy_ctor);
+  WB (lang->has_complex_copy_assign);
+  WB (lang->non_aggregate);
+
+  WB (lang->has_complex_dflt);
+  WB (lang->has_list_ctor);
+  WB (lang->non_std_layout);
+  WB (lang->is_literal);
+  WB (lang->lazy_move_ctor);
+  WB (lang->lazy_move_assign);
+  WB (lang->has_complex_move_ctor);
+  WB (lang->has_complex_move_assign);
+
+  WB (lang->has_constexpr_ctor);
+  WB (lang->unique_obj_representations);
+  WB (lang->unique_obj_representations_set);
+#undef WB
+}
+
+bool
+trees_in::lang_type_bools (tree t)
+{
+#define RB(X) ((X) = b ())
+  struct lang_type *lang = TYPE_LANG_SPECIFIC (t);
+
+  RB (lang->has_type_conversion);
+  RB (lang->has_copy_ctor);
+  RB (lang->has_default_ctor);
+  RB (lang->const_needs_init);
+  RB (lang->ref_needs_init);
+  RB (lang->has_const_copy_assign);
+  unsigned v;
+  v = b () << 0;
+  v |= b () << 1;
+  lang->use_template = v;
+
+  RB (lang->has_mutable);
+  RB (lang->com_interface);
+  RB (lang->non_pod_class);
+  RB (lang->nearly_empty_p);
+  RB (lang->user_align);
+  RB (lang->has_copy_assign);
+  RB (lang->has_new);
+  RB (lang->has_array_new);
+
+  v = b () << 0;
+  v |= b () << 1;
+  lang->gets_delete = v;
+  // lang->interface_only
+  // lang->interface_unknown
+  lang->interface_unknown = true; // Redetermine interface
+  RB (lang->contains_empty_class_p);
+  RB (lang->anon_aggr);
+  RB (lang->non_zero_init);
+  RB (lang->empty_p);
+
+  RB (lang->vec_new_uses_cookie);
+  RB (lang->declared_class);
+  RB (lang->diamond_shaped);
+  RB (lang->repeated_base);
+  gcc_assert (!lang->being_defined);
+  gcc_assert (!lang->debug_requested);
+  RB (lang->fields_readonly);
+  RB (lang->ptrmemfunc_flag);
+
+  RB (lang->lazy_default_ctor);
+  RB (lang->lazy_copy_ctor);
+  RB (lang->lazy_copy_assign);
+  RB (lang->lazy_destructor);
+  RB (lang->has_const_copy_ctor);
+  RB (lang->has_complex_copy_ctor);
+  RB (lang->has_complex_copy_assign);
+  RB (lang->non_aggregate);
+
+  RB (lang->has_complex_dflt);
+  RB (lang->has_list_ctor);
+  RB (lang->non_std_layout);
+  RB (lang->is_literal);
+  RB (lang->lazy_move_ctor);
+  RB (lang->lazy_move_assign);
+  RB (lang->has_complex_move_ctor);
+  RB (lang->has_complex_move_assign);
+
+  RB (lang->has_constexpr_ctor);
+  RB (lang->unique_obj_representations);
+  RB (lang->unique_obj_representations_set);
+#undef RB
+  return !get_overrun ();
+}
+
+/* Read & write the core values and pointers.  */
+
+void
+trees_out::core_vals (tree t)
+{
+#define WU(X) (u (X))
+#define WT(X) (tree_node (X))
+  tree_code code = TREE_CODE (t);
+
+  /* First by shape of the tree.  */
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL))
+    {
+      /* Write this early, for better log information.  */
+      WT (t->decl_minimal.name);
+      if (!DECL_TEMPLATE_PARM_P (t))
+	WT (t->decl_minimal.context);
+
+      state->write_location (*this, t->decl_minimal.locus);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
+    {
+      gcc_checking_assert (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON));
+
+      /* We only stream the main variant.  */
+      gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t);
+
+      /* Stream the name & context first, for better log information  */
+      WT (t->type_common.name);
+      WT (t->type_common.context);
+
+      /* By construction we want to make sure we have the canonical
+	 and main variants already in the type table, so emit them
+	 now.  */
+      WT (t->type_common.main_variant);
+
+      tree canonical = t->type_common.canonical;
+      if (canonical && DECL_TEMPLATE_PARM_P (TYPE_NAME (t)))
+	/* We do not want to wander into different templates.
+	   Reconstructed on stream in.  */
+	canonical = t;
+      WT (canonical);
+
+      /* type_common.next_variant is internally manipulated.  */
+      /* type_common.pointer_to, type_common.reference_to.  */
+
+      if (streaming_p ())
+	{
+	  WU (t->type_common.precision);
+	  WU (t->type_common.contains_placeholder_bits);
+	  WU (t->type_common.mode);
+	  WU (t->type_common.align);
+	}
+
+      if (!RECORD_OR_UNION_CODE_P (code))
+	{
+	  WT (t->type_common.size);
+	  WT (t->type_common.size_unit);
+	}
+      WT (t->type_common.attributes);
+
+      WT (t->type_common.common.chain); /* TYPE_STUB_DECL.  */
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON))
+    {
+      if (streaming_p ())
+	{
+	  WU (t->decl_common.mode);
+	  WU (t->decl_common.off_align);
+	  WU (t->decl_common.align);
+	}
+
+      /* For templates these hold instantiation (partial and/or
+	 specialization) information.  */
+      if (code != TEMPLATE_DECL)
+	{
+	  WT (t->decl_common.size);
+	  WT (t->decl_common.size_unit);
+	}
+
+      WT (t->decl_common.attributes);
+      // FIXME: Does this introduce cross-decl links?  For instance
+      // from instantiation to the template.  If so, we'll need more
+      // deduplication logic
+      WT (t->decl_common.abstract_origin);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
+    {
+      WT (t->decl_with_vis.assembler_name);
+      if (streaming_p ())
+	WU (t->decl_with_vis.visibility);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
+    {
+      /* Records and unions hold FIELDS, VFIELD & BINFO on these
+	 things.  */
+      if (!RECORD_OR_UNION_CODE_P (code) && code != ENUMERAL_TYPE)
+	{
+	  /* Don't write the cached values vector.  */
+	  WT (TYPE_CACHED_VALUES_P (t) ? NULL_TREE : t->type_non_common.values);
+	  WT (t->type_non_common.maxval);
+	  WT (t->type_non_common.minval);
+	}
+
+      WT (t->type_non_common.lang_1);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_EXP))
+    {
+      state->write_location (*this, t->exp.locus);
+
+      /* Walk in forward order, as (for instance) REQUIRES_EXPR has a
+         bunch of unscoped parms on its first operand.  It's safer to
+         create those in order.  */
+      bool vl = TREE_CODE_CLASS (code) == tcc_vl_exp;
+      for (unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t)
+			     : TREE_OPERAND_LENGTH (t)),
+	     ix = unsigned (vl); ix != limit; ix++)
+	WT (TREE_OPERAND (t, ix));
+    }
+  else
+    /* The CODE_CONTAINS tables were inaccurate when I started.  */
+    gcc_checking_assert (TREE_CODE_CLASS (code) != tcc_expression
+			 && TREE_CODE_CLASS (code) != tcc_binary
+			 && TREE_CODE_CLASS (code) != tcc_unary
+			 && TREE_CODE_CLASS (code) != tcc_reference
+			 && TREE_CODE_CLASS (code) != tcc_comparison
+			 && TREE_CODE_CLASS (code) != tcc_statement
+			 && TREE_CODE_CLASS (code) != tcc_vl_exp);
+
+  /* Then by CODE.  Special cases and/or 1:1 tree shape
+     correspondance. */
+  switch (code)
+    {
+    default:
+      break;
+
+    case ARGUMENT_PACK_SELECT:  /* Transient during instantiation.  */
+    case DEFERRED_PARSE:	/* Expanded upon completion of
+				   outermost class.  */
+    case IDENTIFIER_NODE:	/* Streamed specially.  */
+    case MODULE_VECTOR:		/* Only in namespace-scope symbol
+				   table.  */
+    case SSA_NAME:
+    case TRANSLATION_UNIT_DECL: /* There is only one, it is a
+				   global_tree.  */
+    case USERDEF_LITERAL:  	/* Expanded during parsing.  */
+      gcc_unreachable (); /* Should never meet.  */
+
+      /* Constants.  */
+    case COMPLEX_CST:
+      WT (TREE_REALPART (t));
+      WT (TREE_IMAGPART (t));
+      break;
+
+    case FIXED_CST:
+      gcc_unreachable (); /* Not supported in C++.  */
+
+    case INTEGER_CST:
+      if (streaming_p ())
+	{
+	  unsigned num = TREE_INT_CST_EXT_NUNITS (t);
+	  for (unsigned ix = 0; ix != num; ix++)
+	    wu (TREE_INT_CST_ELT (t, ix));
+	}
+      break;
+
+    case POLY_INT_CST:
+      gcc_unreachable (); /* Not supported in C++.  */
+
+    case REAL_CST:
+      if (streaming_p ())
+	buf (TREE_REAL_CST_PTR (t), sizeof (real_value));
+      break;
+
+    case STRING_CST:
+      /* Streamed during start.  */
+      break;
+
+    case VECTOR_CST:
+      for (unsigned ix = vector_cst_encoded_nelts (t); ix--;)
+	WT (VECTOR_CST_ENCODED_ELT (t, ix));
+      break;
+
+      /* Decls.  */
+    case VAR_DECL:
+      if (DECL_CONTEXT (t)
+	  && TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL)
+	break;
+      /* FALLTHROUGH  */
+
+    case RESULT_DECL:
+    case PARM_DECL:
+      if (DECL_HAS_VALUE_EXPR_P (t))
+	WT (DECL_VALUE_EXPR (t));
+      /* FALLTHROUGH  */
+
+    case CONST_DECL:
+    case IMPORTED_DECL:
+      WT (t->decl_common.initial);
+      break;
+
+    case FIELD_DECL:
+      WT (t->field_decl.offset);
+      WT (t->field_decl.bit_field_type);
+      WT (t->field_decl.qualifier); /* bitfield unit.  */
+      WT (t->field_decl.bit_offset);
+      WT (t->field_decl.fcontext);
+      WT (t->decl_common.initial);
+      break;
+
+    case LABEL_DECL:
+      if (streaming_p ())
+	{
+	  WU (t->label_decl.label_decl_uid);
+	  WU (t->label_decl.eh_landing_pad_nr);
+	}
+      break;
+
+    case FUNCTION_DECL:
+      if (streaming_p ())
+	{
+	  /* Builtins can be streamed by value when a header declares
+	     them.  */
+	  WU (DECL_BUILT_IN_CLASS (t));
+	  if (DECL_BUILT_IN_CLASS (t) != NOT_BUILT_IN)
+	    WU (DECL_UNCHECKED_FUNCTION_CODE (t));
+	}
+
+      WT (t->function_decl.personality);
+      WT (t->function_decl.function_specific_target);
+      WT (t->function_decl.function_specific_optimization);
+      WT (t->function_decl.vindex);
+      break;
+
+    case USING_DECL:
+      /* USING_DECL_DECLS  */
+      WT (t->decl_common.initial);
+      /* FALLTHROUGH  */
+
+    case TYPE_DECL:
+      /* USING_DECL: USING_DECL_SCOPE  */
+      /* TYPE_DECL: DECL_ORIGINAL_TYPE */
+      WT (t->decl_non_common.result);
+      break;
+
+      /* Miscellaneous common nodes.  */
+    case BLOCK:
+      state->write_location (*this, t->block.locus);
+      state->write_location (*this, t->block.end_locus);
+      
+      /* DECL_LOCAL_DECL_P decls are first encountered here and
+         streamed by value.  */
+      chained_decls (t->block.vars);
+      /* nonlocalized_vars is a middle-end thing.  */
+      WT (t->block.subblocks);
+      WT (t->block.supercontext);
+      // FIXME: As for decl's abstract_origin, does this introduce crosslinks?
+      WT (t->block.abstract_origin);
+      /* fragment_origin, fragment_chain are middle-end things.  */
+      WT (t->block.chain);
+      /* nonlocalized_vars, block_num & die are middle endy/debug
+	 things.  */
+      break;
+
+    case CALL_EXPR:
+      if (streaming_p ())
+	WU (t->base.u.ifn);
+      break;
+
+    case CONSTRUCTOR:
+      {
+	unsigned len = vec_safe_length (t->constructor.elts);
+	if (streaming_p ())
+	  WU (len);
+	if (len)
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      const constructor_elt &elt = (*t->constructor.elts)[ix];
+
+	      WT (elt.index);
+	      WT (elt.value);
+	    }
+      }
+      break;
+
+    case OMP_CLAUSE:
+      {
+	/* The ompcode is serialized in start.  */
+	if (streaming_p ())
+	  WU (t->omp_clause.subcode.map_kind);
+	state->write_location (*this, t->omp_clause.locus);
+
+	unsigned len = omp_clause_num_ops[OMP_CLAUSE_CODE (t)];
+	for (unsigned ix = 0; ix != len; ix++)
+	  WT (t->omp_clause.ops[ix]);
+      }
+      break;
+
+    case STATEMENT_LIST:
+      for (tree_stmt_iterator iter = tsi_start (t);
+	   !tsi_end_p (iter); tsi_next (&iter))
+	if (tree stmt = tsi_stmt (iter))
+	  WT (stmt);
+      WT (NULL_TREE);
+      break;
+
+    case OPTIMIZATION_NODE:
+    case TARGET_OPTION_NODE:
+      // FIXME: Our representation for these two nodes is a cache of
+      // the resulting set of options.  Not a record of the options
+      // that got changed by a particular attribute or pragma.  Should
+      // we record that, or should we record the diff from the command
+      // line options?  The latter seems the right behaviour, but is
+      // (a) harder, and I guess could introduce strangeness if the
+      // importer has set some incompatible set of optimization flags?
+      gcc_unreachable ();
+      break;
+
+    case TREE_BINFO:
+      {
+	WT (t->binfo.common.chain);
+	WT (t->binfo.offset);
+	WT (t->binfo.inheritance);
+	WT (t->binfo.vptr_field);
+
+	WT (t->binfo.vtable);
+	WT (t->binfo.virtuals);
+	WT (t->binfo.vtt_subvtt);
+	WT (t->binfo.vtt_vptr);
+
+	tree_vec (BINFO_BASE_ACCESSES (t));
+	unsigned num = vec_safe_length (BINFO_BASE_ACCESSES (t));
+	for (unsigned ix = 0; ix != num; ix++)
+	  WT (BINFO_BASE_BINFO (t, ix));
+      }
+      break;
+
+    case TREE_LIST:
+      WT (t->list.purpose);
+      WT (t->list.value);
+      WT (t->list.common.chain);
+      break;
+
+    case TREE_VEC:
+      for (unsigned ix = TREE_VEC_LENGTH (t); ix--;)
+	WT (TREE_VEC_ELT (t, ix));
+      /* We stash NON_DEFAULT_TEMPLATE_ARGS_COUNT on TREE_CHAIN!  */
+      gcc_checking_assert (!t->type_common.common.chain
+			   || (TREE_CODE (t->type_common.common.chain)
+			       == INTEGER_CST));
+      WT (t->type_common.common.chain);
+      break;
+
+      /* C++-specific nodes ...  */
+    case BASELINK:
+      WT (((lang_tree_node *)t)->baselink.binfo);
+      WT (((lang_tree_node *)t)->baselink.functions);
+      WT (((lang_tree_node *)t)->baselink.access_binfo);
+      break;
+
+    case CONSTRAINT_INFO:
+      WT (((lang_tree_node *)t)->constraint_info.template_reqs);
+      WT (((lang_tree_node *)t)->constraint_info.declarator_reqs);
+      WT (((lang_tree_node *)t)->constraint_info.associated_constr);
+      break;
+
+    case DEFERRED_NOEXCEPT:
+      WT (((lang_tree_node *)t)->deferred_noexcept.pattern);
+      WT (((lang_tree_node *)t)->deferred_noexcept.args);
+      break;
+
+    case LAMBDA_EXPR:
+      WT (((lang_tree_node *)t)->lambda_expression.capture_list);
+      WT (((lang_tree_node *)t)->lambda_expression.this_capture);
+      WT (((lang_tree_node *)t)->lambda_expression.extra_scope);
+      /* pending_proxies is a parse-time thing.  */
+      gcc_assert (!((lang_tree_node *)t)->lambda_expression.pending_proxies);
+      state->write_location
+	(*this, ((lang_tree_node *)t)->lambda_expression.locus);
+      if (streaming_p ())
+	{
+	  WU (((lang_tree_node *)t)->lambda_expression.default_capture_mode);
+	  WU (((lang_tree_node *)t)->lambda_expression.discriminator);
+	}
+      break;
+
+    case OVERLOAD:
+      WT (((lang_tree_node *)t)->overload.function);
+      WT (t->common.chain);
+      break;
+      
+    case PTRMEM_CST:
+      WT (((lang_tree_node *)t)->ptrmem.member);
+      break;
+
+    case STATIC_ASSERT:
+      WT (((lang_tree_node *)t)->static_assertion.condition);
+      WT (((lang_tree_node *)t)->static_assertion.message);
+      state->write_location
+	(*this, ((lang_tree_node *)t)->static_assertion.location);
+      break;
+
+    case TEMPLATE_DECL:
+      /* Streamed with the template_decl node itself.  */
+      gcc_checking_assert
+      	(TREE_VISITED (((lang_tree_node *)t)->template_decl.arguments));
+      gcc_checking_assert
+	(TREE_VISITED (((lang_tree_node *)t)->template_decl.result)
+	 || dep_hash->find_dependency (t)->is_alias_tmpl_inst ());
+      if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (t))
+	WT (DECL_CHAIN (t));
+      break;
+
+    case TEMPLATE_INFO:
+      {
+	WT (((lang_tree_node *)t)->template_info.tmpl);
+	WT (((lang_tree_node *)t)->template_info.args);
+
+	const auto *ac = (((lang_tree_node *)t)
+			  ->template_info.deferred_access_checks);
+	unsigned len = vec_safe_length (ac);
+	if (streaming_p ())
+	  u (len);
+	if (len)
+	  {
+	    for (unsigned ix = 0; ix != len; ix++)
+	      {
+		const auto &m = (*ac)[ix];
+		WT (m.binfo);
+		WT (m.decl);
+		WT (m.diag_decl);
+		state->write_location (*this, m.loc);
+	      }
+	  }
+      }
+      break;
+
+    case TEMPLATE_PARM_INDEX:
+      if (streaming_p ())
+	{
+	  WU (((lang_tree_node *)t)->tpi.index);
+	  WU (((lang_tree_node *)t)->tpi.level);
+	  WU (((lang_tree_node *)t)->tpi.orig_level);
+	}
+      WT (((lang_tree_node *)t)->tpi.decl);
+      /* TEMPLATE_PARM_DESCENDANTS (AKA TREE_CHAIN) is an internal
+	 cache, do not stream.  */
+      break;
+      
+    case TRAIT_EXPR:
+      WT (((lang_tree_node *)t)->trait_expression.type1);
+      WT (((lang_tree_node *)t)->trait_expression.type2);
+      if (streaming_p ())
+	WU (((lang_tree_node *)t)->trait_expression.kind);
+      break;
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPED))
+    {
+      /* We want to stream the type of a expression-like nodes /after/
+         we've streamed the operands.  The type often contains (bits
+         of the) types of the operands, and with things like decltype
+         and noexcept in play, we really want to stream the decls
+         defining the type before we try and stream the type on its
+         own.  Otherwise we can find ourselves trying to read in a
+         decl, when we're already partially reading in a component of
+         its type.  And that's bad.  */
+      tree type = t->typed.type;
+      unsigned prec = 0;
+
+      switch (code)
+	{
+	default:
+	  break;
+
+	case TEMPLATE_DECL:
+	  /* We fill in the template's type separately.  */
+	  type = NULL_TREE;
+	  break;
+
+	case TYPE_DECL:
+	  if (DECL_ORIGINAL_TYPE (t) && t == TYPE_NAME (type))
+	    /* This is a typedef.  We set its type separately.  */
+	    type = NULL_TREE;
+	  break;
+
+	case ENUMERAL_TYPE:
+	  if (type && !ENUM_FIXED_UNDERLYING_TYPE_P (t))
+	    {
+	      /* Type is a restricted range integer type derived from the
+		 integer_types.  Find the right one.  */
+	      prec = TYPE_PRECISION (type);
+	      tree name = DECL_NAME (TYPE_NAME (type));
+
+	      for (unsigned itk = itk_none; itk--;)
+		if (integer_types[itk]
+		    && DECL_NAME (TYPE_NAME (integer_types[itk])) == name)
+		  {
+		    type = integer_types[itk];
+		    break;
+		  }
+	      gcc_assert (type != t->typed.type);
+	    }
+	  break;
+	}
+
+      WT (type);
+      if (prec && streaming_p ())
+	WU (prec);
+    }
+
+#undef WT
+#undef WU
+}
+
+// Streaming in a reference to a decl can cause that decl to be
+// TREE_USED, which is the mark_used behaviour we need most of the
+// time.  The trees_in::unused can be incremented to inhibit this,
+// which is at least needed for vtables.
+
+bool
+trees_in::core_vals (tree t)
+{
+#define RU(X) ((X) = u ())
+#define RUC(T,X) ((X) = T (u ()))
+#define RT(X) ((X) = tree_node ())
+#define RTU(X) ((X) = tree_node (true))
+  tree_code code = TREE_CODE (t);
+
+  /* First by tree shape.  */
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL))
+    {
+      RT (t->decl_minimal.name);
+      if (!DECL_TEMPLATE_PARM_P (t))
+	RT (t->decl_minimal.context);
+
+      /* Don't zap the locus just yet, we don't record it correctly
+	 and thus lose all location information.  */
+      t->decl_minimal.locus = state->read_location (*this);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON))
+    {
+      RT (t->type_common.name);
+      RT (t->type_common.context);
+
+      RT (t->type_common.main_variant);
+      RT (t->type_common.canonical);
+
+      /* type_common.next_variant is internally manipulated.  */
+      /* type_common.pointer_to, type_common.reference_to.  */
+
+      RU (t->type_common.precision);
+      RU (t->type_common.contains_placeholder_bits);
+      RUC (machine_mode, t->type_common.mode);
+      RU (t->type_common.align);
+
+      if (!RECORD_OR_UNION_CODE_P (code))
+	{
+	  RT (t->type_common.size);
+	  RT (t->type_common.size_unit);
+	}
+      RT (t->type_common.attributes);
+
+      RT (t->type_common.common.chain); /* TYPE_STUB_DECL.  */
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON))
+    {
+      RUC (machine_mode, t->decl_common.mode);
+      RU (t->decl_common.off_align);
+      RU (t->decl_common.align);
+
+      if (code != TEMPLATE_DECL)
+	{
+	  RT (t->decl_common.size);
+	  RT (t->decl_common.size_unit);
+	}
+
+      RT (t->decl_common.attributes);
+      RT (t->decl_common.abstract_origin);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
+    {
+      RT (t->decl_with_vis.assembler_name);
+      RUC (symbol_visibility, t->decl_with_vis.visibility);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON))
+    {
+      /* Records and unions hold FIELDS, VFIELD & BINFO on these
+	 things.  */
+      if (!RECORD_OR_UNION_CODE_P (code) && code != ENUMERAL_TYPE)
+	{
+	  /* This is not clobbering TYPE_CACHED_VALUES, because this
+	     is a new type being read in, so there aren't any.  */
+	  gcc_checking_assert (!TYPE_CACHED_VALUES_P (t));
+	  RT (t->type_non_common.values);
+	  RT (t->type_non_common.maxval);
+	  RT (t->type_non_common.minval);
+	}
+
+      RT (t->type_non_common.lang_1);
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_EXP))
+    {
+      t->exp.locus = state->read_location (*this);
+
+      bool vl = TREE_CODE_CLASS (code) == tcc_vl_exp;
+      for (unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t)
+			     : TREE_OPERAND_LENGTH (t)),
+	     ix = unsigned (vl); ix != limit; ix++)
+	RTU (TREE_OPERAND (t, ix));
+    }
+
+  /* Then by CODE.  Special cases and/or 1:1 tree shape
+     correspondance. */
+  switch (code)
+    {
+    default:
+      break;
+
+    case ARGUMENT_PACK_SELECT:
+    case DEFERRED_PARSE:
+    case IDENTIFIER_NODE:
+    case MODULE_VECTOR:
+    case SSA_NAME:
+    case TRANSLATION_UNIT_DECL:
+    case USERDEF_LITERAL:
+      return false; /* Should never meet.  */
+
+      /* Constants.  */
+    case COMPLEX_CST:
+      RT (TREE_REALPART (t));
+      RT (TREE_IMAGPART (t));
+      break;
+
+    case FIXED_CST:
+      /* Not suported in C++.  */
+      return false;
+
+    case INTEGER_CST:
+      {
+	unsigned num = TREE_INT_CST_EXT_NUNITS (t);
+	for (unsigned ix = 0; ix != num; ix++)
+	  TREE_INT_CST_ELT (t, ix) = wu ();
+      }
+      break;
+
+    case POLY_INT_CST:
+      /* Not suported in C++.  */
+      return false;
+
+    case REAL_CST:
+      if (const void *bytes = buf (sizeof (real_value)))
+	TREE_REAL_CST_PTR (t)
+	  = reinterpret_cast<real_value *> (memcpy (ggc_alloc<real_value> (),
+						    bytes, sizeof (real_value)));
+      break;
+
+    case STRING_CST:
+      /* Streamed during start.  */
+      break;
+
+    case VECTOR_CST:
+      for (unsigned ix = vector_cst_encoded_nelts (t); ix--;)
+	RT (VECTOR_CST_ENCODED_ELT (t, ix));
+      break;
+
+      /* Decls.  */
+    case VAR_DECL:
+      if (DECL_CONTEXT (t)
+	  && TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL)
+	break;
+      /* FALLTHROUGH  */
+
+    case RESULT_DECL:
+    case PARM_DECL:
+      if (DECL_HAS_VALUE_EXPR_P (t))
+	{
+	  /* The DECL_VALUE hash table is a cache, thus if we're
+	     reading a duplicate (which we end up discarding), the
+	     value expr will also be cleaned up at the next gc.  */
+	  tree val = tree_node ();
+	  SET_DECL_VALUE_EXPR (t, val);
+	}
+      /* FALLTHROUGH  */
+
+    case CONST_DECL:
+    case IMPORTED_DECL:
+      RT (t->decl_common.initial);
+      break;
+
+    case FIELD_DECL:
+      RT (t->field_decl.offset);
+      RT (t->field_decl.bit_field_type);
+      RT (t->field_decl.qualifier);
+      RT (t->field_decl.bit_offset);
+      RT (t->field_decl.fcontext);
+      RT (t->decl_common.initial);
+      break;
+
+    case LABEL_DECL:
+      RU (t->label_decl.label_decl_uid);
+      RU (t->label_decl.eh_landing_pad_nr);
+      break;
+  
+    case FUNCTION_DECL:
+      {
+	unsigned bltin = u ();
+	t->function_decl.built_in_class = built_in_class (bltin);
+	if (bltin != NOT_BUILT_IN)
+	  {
+	    bltin = u ();
+	    DECL_UNCHECKED_FUNCTION_CODE (t) = built_in_function (bltin);
+	  }
+
+	RT (t->function_decl.personality);
+	RT (t->function_decl.function_specific_target);
+	RT (t->function_decl.function_specific_optimization);
+	RT (t->function_decl.vindex);
+      }
+      break;
+
+    case USING_DECL:
+      /* USING_DECL_DECLS  */
+      RT (t->decl_common.initial);
+      /* FALLTHROUGH  */
+
+    case TYPE_DECL:
+      /* USING_DECL: USING_DECL_SCOPE  */
+      /* TYPE_DECL: DECL_ORIGINAL_TYPE */
+      RT (t->decl_non_common.result);
+      break;
+
+      /* Miscellaneous common nodes.  */
+    case BLOCK:
+      t->block.locus = state->read_location (*this);
+      t->block.end_locus = state->read_location (*this);
+      t->block.vars = chained_decls ();
+      /* nonlocalized_vars is middle-end.  */
+      RT (t->block.subblocks);
+      RT (t->block.supercontext);
+      RT (t->block.abstract_origin);
+      /* fragment_origin, fragment_chain are middle-end.  */
+      RT (t->block.chain);
+      /* nonlocalized_vars, block_num, die are middle endy/debug
+	 things.  */
+      break;
+
+    case CALL_EXPR:
+      RUC (internal_fn, t->base.u.ifn);
+      break;
+
+    case CONSTRUCTOR:
+      if (unsigned len = u ())
+	{
+	  vec_alloc (t->constructor.elts, len);
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      constructor_elt elt;
+
+	      RT (elt.index);
+	      RTU (elt.value);
+	      t->constructor.elts->quick_push (elt);
+	    }
+	}
+      break;
+
+    case OMP_CLAUSE:
+      {
+	RU (t->omp_clause.subcode.map_kind);
+	t->omp_clause.locus = state->read_location (*this);
+
+	unsigned len = omp_clause_num_ops[OMP_CLAUSE_CODE (t)];
+	for (unsigned ix = 0; ix != len; ix++)
+	  RT (t->omp_clause.ops[ix]);
+      }
+      break;
+
+    case STATEMENT_LIST:
+      {
+	tree_stmt_iterator iter = tsi_start (t);
+	for (tree stmt; RT (stmt);)
+	  tsi_link_after (&iter, stmt, TSI_CONTINUE_LINKING);
+      }
+      break;
+
+    case OPTIMIZATION_NODE:
+    case TARGET_OPTION_NODE:
+      /* Not yet implemented, see trees_out::core_vals.  */
+      gcc_unreachable ();
+      break;
+
+    case TREE_BINFO:
+      RT (t->binfo.common.chain);
+      RT (t->binfo.offset);
+      RT (t->binfo.inheritance);
+      RT (t->binfo.vptr_field);
+
+      /* Do not mark the vtables as USED in the address expressions
+	 here.  */
+      unused++;
+      RT (t->binfo.vtable);
+      RT (t->binfo.virtuals);
+      RT (t->binfo.vtt_subvtt);
+      RT (t->binfo.vtt_vptr);
+      unused--;
+
+      BINFO_BASE_ACCESSES (t) = tree_vec ();
+      if (!get_overrun ())
+	{
+	  unsigned num = vec_safe_length (BINFO_BASE_ACCESSES (t));
+	  for (unsigned ix = 0; ix != num; ix++)
+	    BINFO_BASE_APPEND (t, tree_node ());
+	}
+      break;
+
+    case TREE_LIST:
+      RT (t->list.purpose);
+      RT (t->list.value);
+      RT (t->list.common.chain);
+      break;
+
+    case TREE_VEC:
+      for (unsigned ix = TREE_VEC_LENGTH (t); ix--;)
+	RT (TREE_VEC_ELT (t, ix));
+      RT (t->type_common.common.chain);
+      break;
+
+      /* C++-specific nodes ...  */
+    case BASELINK:
+      RT (((lang_tree_node *)t)->baselink.binfo);
+      RTU (((lang_tree_node *)t)->baselink.functions);
+      RT (((lang_tree_node *)t)->baselink.access_binfo);
+      break;
+
+    case CONSTRAINT_INFO:
+      RT (((lang_tree_node *)t)->constraint_info.template_reqs);
+      RT (((lang_tree_node *)t)->constraint_info.declarator_reqs);
+      RT (((lang_tree_node *)t)->constraint_info.associated_constr);
+      break;
+
+    case DEFERRED_NOEXCEPT:
+      RT (((lang_tree_node *)t)->deferred_noexcept.pattern);
+      RT (((lang_tree_node *)t)->deferred_noexcept.args);
+      break;
+
+    case LAMBDA_EXPR:
+      RT (((lang_tree_node *)t)->lambda_expression.capture_list);
+      RT (((lang_tree_node *)t)->lambda_expression.this_capture);
+      RT (((lang_tree_node *)t)->lambda_expression.extra_scope);
+      /* lambda_expression.pending_proxies is NULL  */
+      ((lang_tree_node *)t)->lambda_expression.locus
+	= state->read_location (*this);
+      RUC (cp_lambda_default_capture_mode_type,
+	   ((lang_tree_node *)t)->lambda_expression.default_capture_mode);
+      RU (((lang_tree_node *)t)->lambda_expression.discriminator);
+      break;
+
+    case OVERLOAD:
+      RT (((lang_tree_node *)t)->overload.function);
+      RT (t->common.chain);
+      break;
+
+    case PTRMEM_CST:
+      RT (((lang_tree_node *)t)->ptrmem.member);
+      break;
+
+    case STATIC_ASSERT:
+      RT (((lang_tree_node *)t)->static_assertion.condition);
+      RT (((lang_tree_node *)t)->static_assertion.message);
+      ((lang_tree_node *)t)->static_assertion.location
+	= state->read_location (*this);
+      break;
+
+    case TEMPLATE_DECL:
+      /* Streamed when reading the raw template decl itself.  */
+      gcc_assert (((lang_tree_node *)t)->template_decl.arguments);
+      gcc_assert (((lang_tree_node *)t)->template_decl.result);
+      if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (t))
+	RT (DECL_CHAIN (t));
+      break;
+
+    case TEMPLATE_INFO:
+      RT (((lang_tree_node *)t)->template_info.tmpl);
+      RT (((lang_tree_node *)t)->template_info.args);
+      if (unsigned len = u ())
+	{
+	  auto &ac = (((lang_tree_node *)t)
+		      ->template_info.deferred_access_checks);
+	  vec_alloc (ac, len);
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      deferred_access_check m;
+
+	      RT (m.binfo);
+	      RT (m.decl);
+	      RT (m.diag_decl);
+	      m.loc = state->read_location (*this);
+	      ac->quick_push (m);
+	    }
+	}
+      break;
+
+    case TEMPLATE_PARM_INDEX:
+      RU (((lang_tree_node *)t)->tpi.index);
+      RU (((lang_tree_node *)t)->tpi.level);
+      RU (((lang_tree_node *)t)->tpi.orig_level);
+      RT (((lang_tree_node *)t)->tpi.decl);
+      break;
+
+    case TRAIT_EXPR:
+      RT (((lang_tree_node *)t)->trait_expression.type1);
+      RT (((lang_tree_node *)t)->trait_expression.type2);
+      RUC (cp_trait_kind, ((lang_tree_node *)t)->trait_expression.kind);
+      break;
+    }
+
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPED))
+    {
+      tree type = tree_node ();
+
+      if (type && code == ENUMERAL_TYPE && !ENUM_FIXED_UNDERLYING_TYPE_P (t))
+	{
+	  unsigned precision = u ();
+
+	  type = build_distinct_type_copy (type);
+	  TYPE_PRECISION (type) = precision;
+	  set_min_and_max_values_for_integral_type (type, precision,
+						    TYPE_SIGN (type));
+	}
+
+      if (code != TEMPLATE_DECL)
+	t->typed.type = type;
+    }
+
+#undef RT
+#undef RM
+#undef RU
+  return !get_overrun ();
+}
+
+void
+trees_out::lang_decl_vals (tree t)
+{
+  const struct lang_decl *lang = DECL_LANG_SPECIFIC (t);
+#define WU(X) (u (X))
+#define WT(X) (tree_node (X))
+  /* Module index already written.  */
+  switch (lang->u.base.selector)
+    {
+    default:
+      gcc_unreachable ();
+
+    case lds_fn:  /* lang_decl_fn.  */
+      if (streaming_p ())
+	{
+	  if (DECL_NAME (t) && IDENTIFIER_OVL_OP_P (DECL_NAME (t)))
+	    WU (lang->u.fn.ovl_op_code);
+	}
+
+      if (DECL_CLASS_SCOPE_P (t))
+	WT (lang->u.fn.context);
+
+      if (lang->u.fn.thunk_p)
+	{
+	  /* The thunked-to function.  */
+	  WT (lang->u.fn.befriending_classes);
+	  if (streaming_p ())
+	    wi (lang->u.fn.u5.fixed_offset);
+	}
+      else
+	WT (lang->u.fn.u5.cloned_function);
+
+      if (FNDECL_USED_AUTO (t))
+	WT (lang->u.fn.u.saved_auto_return_type);
+
+      goto lds_min;
+
+    case lds_decomp:  /* lang_decl_decomp.  */
+      WT (lang->u.decomp.base);
+      goto lds_min;
+
+    case lds_min:  /* lang_decl_min.  */
+    lds_min:
+      WT (lang->u.min.template_info);
+      {
+	tree access = lang->u.min.access;
+
+	/* DECL_ACCESS needs to be maintained by the definition of the
+	   (derived) class that changes the access.  The other users
+	   of DECL_ACCESS need to write it here.  */
+	if (!DECL_THUNK_P (t)
+	    && (DECL_CONTEXT (t) && TYPE_P (DECL_CONTEXT (t))))
+	  access = NULL_TREE;
+
+	WT (access);
+      }
+      break;
+
+    case lds_ns:  /* lang_decl_ns.  */
+      break;
+
+    case lds_parm:  /* lang_decl_parm.  */
+      if (streaming_p ())
+	{
+	  WU (lang->u.parm.level);
+	  WU (lang->u.parm.index);
+	}
+      break;
+    }
+#undef WU
+#undef WT
+}
+
+bool
+trees_in::lang_decl_vals (tree t)
+{
+  struct lang_decl *lang = DECL_LANG_SPECIFIC (t);
+#define RU(X) ((X) = u ())
+#define RT(X) ((X) = tree_node ())
+
+  /* Module index already read.  */
+  switch (lang->u.base.selector)
+    {
+    default:
+      gcc_unreachable ();
+
+    case lds_fn:  /* lang_decl_fn.  */
+      if (DECL_NAME (t) && IDENTIFIER_OVL_OP_P (DECL_NAME (t)))
+	{
+	  unsigned code = u ();
+
+	  /* Check consistency.  */
+	  if (code >= OVL_OP_MAX
+	      || (ovl_op_info[IDENTIFIER_ASSIGN_OP_P (DECL_NAME (t))][code]
+		  .ovl_op_code) == OVL_OP_ERROR_MARK)
+	    set_overrun ();
+	  else
+	    lang->u.fn.ovl_op_code = code;
+	}
+
+      if (DECL_CLASS_SCOPE_P (t))
+	RT (lang->u.fn.context);
+
+      if (lang->u.fn.thunk_p)
+	{
+	  RT (lang->u.fn.befriending_classes);
+	  lang->u.fn.u5.fixed_offset = wi ();
+	}
+      else
+	RT (lang->u.fn.u5.cloned_function);
+
+      if (FNDECL_USED_AUTO (t))
+	RT (lang->u.fn.u.saved_auto_return_type);
+      goto lds_min;
+
+    case lds_decomp:  /* lang_decl_decomp.  */
+      RT (lang->u.decomp.base);
+      goto lds_min;
+
+    case lds_min:  /* lang_decl_min.  */
+    lds_min:
+      RT (lang->u.min.template_info);
+      RT (lang->u.min.access);
+      break;
+
+    case lds_ns:  /* lang_decl_ns.  */
+      break;
+
+    case lds_parm:  /* lang_decl_parm.  */
+      RU (lang->u.parm.level);
+      RU (lang->u.parm.index);
+      break;
+    }
+#undef RU
+#undef RT
+  return !get_overrun ();
+}
+
+/* Most of the value contents of lang_type is streamed in
+   define_class.  */
+
+void
+trees_out::lang_type_vals (tree t)
+{
+  const struct lang_type *lang = TYPE_LANG_SPECIFIC (t);
+#define WU(X) (u (X))
+#define WT(X) (tree_node (X))
+  if (streaming_p ())
+    WU (lang->align);
+#undef WU
+#undef WT
+}
+
+bool
+trees_in::lang_type_vals (tree t)
+{
+  struct lang_type *lang = TYPE_LANG_SPECIFIC (t);
+#define RU(X) ((X) = u ())
+#define RT(X) ((X) = tree_node ())
+  RU (lang->align);
+#undef RU
+#undef RT
+  return !get_overrun ();
+}
+
+/* Write out the bools of T, including information about any
+   LANG_SPECIFIC information.  Including allocation of any lang
+   specific object.  */
+
+void
+trees_out::tree_node_bools (tree t)
+{
+  gcc_checking_assert (streaming_p ());
+
+  /* We should never stream a namespace.  */
+  gcc_checking_assert (TREE_CODE (t) != NAMESPACE_DECL
+		       || DECL_NAMESPACE_ALIAS (t));
+
+  core_bools (t);
+
+  switch (TREE_CODE_CLASS (TREE_CODE (t)))
+    {
+    case tcc_declaration:
+      {
+	bool specific = DECL_LANG_SPECIFIC (t) != NULL;
+	b (specific);
+	if (specific && VAR_P (t))
+	  b (DECL_DECOMPOSITION_P (t));
+	if (specific)
+	  lang_decl_bools (t);
+      }
+      break;
+
+    case tcc_type:
+      {
+	bool specific = (TYPE_MAIN_VARIANT (t) == t
+			 && TYPE_LANG_SPECIFIC (t) != NULL);
+	gcc_assert (TYPE_LANG_SPECIFIC (t)
+		    == TYPE_LANG_SPECIFIC (TYPE_MAIN_VARIANT (t)));
+
+	b (specific);
+	if (specific)
+	  lang_type_bools (t);
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  bflush ();
+}
+
+bool
+trees_in::tree_node_bools (tree t)
+{
+  bool ok = core_bools (t);
+
+  if (ok)
+    switch (TREE_CODE_CLASS (TREE_CODE (t)))
+      {
+      case tcc_declaration:
+	if (b ())
+	  {
+	    bool decomp = VAR_P (t) && b ();
+
+	    ok = maybe_add_lang_decl_raw (t, decomp);
+	    if (ok)
+	      ok = lang_decl_bools (t);
+	}
+	break;
+
+      case tcc_type:
+	if (b ())
+	  {
+	    ok = maybe_add_lang_type_raw (t);
+	    if (ok)
+	      ok = lang_type_bools (t);
+	  }
+	break;
+
+      default:
+	break;
+      }
+
+  bflush ();
+  if (!ok || get_overrun ())
+    return false;
+
+  return true;
+}
+
+
+/* Write out the lang-specifc vals of node T.  */
+
+void
+trees_out::lang_vals (tree t)
+{
+  switch (TREE_CODE_CLASS (TREE_CODE (t)))
+    {
+    case tcc_declaration:
+      if (DECL_LANG_SPECIFIC (t))
+	lang_decl_vals (t);
+      break;
+
+    case tcc_type:
+      if (TYPE_MAIN_VARIANT (t) == t && TYPE_LANG_SPECIFIC (t))
+	lang_type_vals (t);
+      break;
+
+    default:
+      break;
+    }
+}
+
+bool
+trees_in::lang_vals (tree t)
+{
+  bool ok = true;
+
+  switch (TREE_CODE_CLASS (TREE_CODE (t)))
+    {
+    case tcc_declaration:
+      if (DECL_LANG_SPECIFIC (t))
+	ok = lang_decl_vals (t);
+      break;
+
+    case tcc_type:
+      if (TYPE_LANG_SPECIFIC (t))
+	ok = lang_type_vals (t);
+      else
+	TYPE_LANG_SPECIFIC (t) = TYPE_LANG_SPECIFIC (TYPE_MAIN_VARIANT (t));
+      break;
+
+    default:
+      break;
+    }
+
+  return ok;
+}
+
+/* Write out the value fields of node T.  */
+
+void
+trees_out::tree_node_vals (tree t)
+{
+  core_vals (t);
+  lang_vals (t);
+}
+
+bool
+trees_in::tree_node_vals (tree t)
+{
+  bool ok = core_vals (t);
+  if (ok)
+    ok = lang_vals (t);
+
+  return ok;
+}
+
+
+/* If T is a back reference, fixed reference or NULL, write out it's
+   code and return WK_none.  Otherwise return WK_value if we must write
+   by value, or WK_normal otherwise.  */
+
+walk_kind
+trees_out::ref_node (tree t)
+{
+  if (!t)
+    {
+      if (streaming_p ())
+	{
+	  /* NULL_TREE -> tt_null.  */
+	  null_count++;
+	  i (tt_null);
+	}
+      return WK_none;
+    }
+
+  if (!TREE_VISITED (t))
+    return WK_normal;
+
+  /* An already-visited tree.  It must be in the map.  */
+  int val = get_tag (t);
+
+  if (val == tag_value)
+    /* An entry we should walk into.  */
+    return WK_value;
+
+  const char *kind;
+
+  if (val <= tag_backref)
+    {
+      /* Back reference -> -ve number  */
+      if (streaming_p ())
+	i (val);
+      kind = "backref";
+    }
+  else if (val >= tag_fixed)
+    {
+      /* Fixed reference -> tt_fixed */
+      val -= tag_fixed;
+      if (streaming_p ())
+	i (tt_fixed), u (val);
+      kind = "fixed";
+    }
+
+  if (streaming_p ())
+    {
+      back_ref_count++;
+      dump (dumper::TREE)
+	&& dump ("Wrote %s:%d %C:%N%S", kind, val, TREE_CODE (t), t, t);
+    }
+  return WK_none;
+}
+
+tree
+trees_in::back_ref (int tag)
+{
+  tree res = NULL_TREE;
+
+  if (tag < 0 && unsigned (~tag) < back_refs.length ())
+    res = back_refs[~tag];
+
+  if (!res
+      /* Checking TREE_CODE is a dereference, so we know this is not a
+	 wild pointer.  Checking the code provides evidence we've not
+	 corrupted something.  */
+      || TREE_CODE (res) >= MAX_TREE_CODES)
+    set_overrun ();
+  else
+    dump (dumper::TREE) && dump ("Read backref:%d found %C:%N%S", tag,
+				 TREE_CODE (res), res, res);
+  return res;
+}
+
+unsigned
+trees_out::add_indirect_tpl_parms (tree parms)
+{
+  unsigned len = 0;
+  for (; parms; parms = TREE_CHAIN (parms), len++)
+    {
+      if (TREE_VISITED (parms))
+	break;
+
+      int tag = insert (parms);
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Indirect:%d template's parameter %u %C:%N",
+		   tag, len, TREE_CODE (parms), parms);
+    }
+
+  if (streaming_p ())
+    u (len);
+
+  return len;
+}
+
+unsigned
+trees_in::add_indirect_tpl_parms (tree parms)
+{
+  unsigned len = u ();
+  for (unsigned ix = 0; ix != len; parms = TREE_CHAIN (parms), ix++)
+    {
+      int tag = insert (parms);
+      dump (dumper::TREE)
+	&& dump ("Indirect:%d template's parameter %u %C:%N",
+		 tag, ix, TREE_CODE (parms), parms);
+    }
+
+  return len;
+}
+
+/* We've just found DECL by name.  Insert nodes that come with it, but
+   cannot be found by name, so we'll not accidentally walk into them.  */
+
+void
+trees_out::add_indirects (tree decl)
+{
+  unsigned count = 0;
+
+  // FIXME:OPTIMIZATION We'll eventually want default fn parms of
+  // templates and perhaps default template parms too.  The former can
+  // be referenced from instantiations (as they are lazily
+  // instantiated).  Also (deferred?) exception specifications of
+  // templates.  See the note about PARM_DECLs in trees_out::decl_node.
+  tree inner = decl;
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      count += add_indirect_tpl_parms (DECL_TEMPLATE_PARMS (decl));
+
+      inner = DECL_TEMPLATE_RESULT (decl);
+      int tag = insert (inner);
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Indirect:%d template's result %C:%N",
+		   tag, TREE_CODE (inner), inner);
+      count++;
+    }
+
+  if (TREE_CODE (inner) == TYPE_DECL)
+    {
+      /* Make sure the type is in the map too.  Otherwise we get
+	 different RECORD_TYPEs for the same type, and things go
+	 south.  */
+      tree type = TREE_TYPE (inner);
+      gcc_checking_assert (DECL_ORIGINAL_TYPE (inner)
+			   || TYPE_NAME (type) == inner);
+      int tag = insert (type);
+      if (streaming_p ())
+	dump (dumper::TREE) && dump ("Indirect:%d decl's type %C:%N", tag,
+				     TREE_CODE (type), type);
+      count++;
+    }
+
+  if (streaming_p ())
+    {
+      u (count);
+      dump (dumper::TREE) && dump ("Inserted %u indirects", count);
+    }
+}
+
+bool
+trees_in::add_indirects (tree decl)
+{
+  unsigned count = 0;
+	    
+  tree inner = decl;
+  if (TREE_CODE (inner) == TEMPLATE_DECL)
+    {
+      count += add_indirect_tpl_parms (DECL_TEMPLATE_PARMS (decl));
+
+      inner = DECL_TEMPLATE_RESULT (decl);
+      int tag = insert (inner);
+      dump (dumper::TREE)
+	&& dump ("Indirect:%d templates's result %C:%N", tag,
+		 TREE_CODE (inner), inner);
+      count++;
+    }
+
+  if (TREE_CODE (inner) == TYPE_DECL)
+    {
+      tree type = TREE_TYPE (inner);
+      gcc_checking_assert (DECL_ORIGINAL_TYPE (inner)
+			   || TYPE_NAME (type) == inner);
+      int tag = insert (type);
+      dump (dumper::TREE)
+	&& dump ("Indirect:%d decl's type %C:%N", tag, TREE_CODE (type), type);
+      count++;
+    }
+
+  dump (dumper::TREE) && dump ("Inserted %u indirects", count);
+  return count == u ();
+}
+
+/* Stream a template parameter.  There are 4.5 kinds of parameter:
+   a) Template - TEMPLATE_DECL->TYPE_DECL->TEMPLATE_TEMPLATE_PARM
+   	TEMPLATE_TYPE_PARM_INDEX TPI
+   b) Type - TYPE_DECL->TEMPLATE_TYPE_PARM TEMPLATE_TYPE_PARM_INDEX TPI
+   c.1) NonTYPE - PARM_DECL DECL_INITIAL TPI We meet this first
+   c.2) NonTYPE - CONST_DECL DECL_INITIAL Same TPI
+   d) BoundTemplate - TYPE_DECL->BOUND_TEMPLATE_TEMPLATE_PARM
+       TEMPLATE_TYPE_PARM_INDEX->TPI
+       TEMPLATE_TEMPLATE_PARM_INFO->TEMPLATE_INFO
+
+   All of these point to a TEMPLATE_PARM_INDEX, and #B also has a TEMPLATE_INFO
+*/
+
+void
+trees_out::tpl_parm_value (tree parm)
+{
+  gcc_checking_assert (DECL_P (parm) && DECL_TEMPLATE_PARM_P (parm));
+
+  int parm_tag = insert (parm);
+  if (streaming_p ())
+    {
+      i (tt_tpl_parm);
+      dump (dumper::TREE) && dump ("Writing template parm:%d %C:%N",
+				   parm_tag, TREE_CODE (parm), parm);
+      start (parm);
+      tree_node_bools (parm);
+    }
+
+  tree inner = parm;
+  if (TREE_CODE (inner) == TEMPLATE_DECL)
+    {
+      inner = DECL_TEMPLATE_RESULT (inner);
+      int inner_tag = insert (inner);
+      if (streaming_p ())
+	{
+	  dump (dumper::TREE) && dump ("Writing inner template parm:%d %C:%N",
+				       inner_tag, TREE_CODE (inner), inner);
+	  start (inner);
+	  tree_node_bools (inner);
+	}
+    }
+
+  tree type = NULL_TREE;
+  if (TREE_CODE (inner) == TYPE_DECL)
+    {
+      type = TREE_TYPE (inner);
+      int type_tag = insert (type);
+      if (streaming_p ())
+	{
+	  dump (dumper::TREE) && dump ("Writing template parm type:%d %C:%N",
+				       type_tag, TREE_CODE (type), type);
+	  start (type);
+	  tree_node_bools (type);
+	}
+    }
+
+  if (inner != parm)
+    {
+      /* This is a template-template parameter.  */
+      unsigned tpl_levels = 0;
+      tpl_header (parm, &tpl_levels);
+      tpl_parms_fini (parm, tpl_levels);
+    }
+
+  tree_node_vals (parm);
+  if (inner != parm)
+    tree_node_vals (inner);
+  if (type)
+    {
+      tree_node_vals (type);
+      if (DECL_NAME (inner) == auto_identifier
+	  || DECL_NAME (inner) == decltype_auto_identifier)
+	{
+	  /* Placeholder auto.  */
+	  tree_node (DECL_INITIAL (inner));
+	  tree_node (DECL_SIZE_UNIT (inner));
+	}
+    }
+
+  if (streaming_p ())
+    dump (dumper::TREE) && dump ("Wrote template parm:%d %C:%N",
+				 parm_tag, TREE_CODE (parm), parm);
+}
+
+tree
+trees_in::tpl_parm_value ()
+{
+  tree parm = start ();
+  if (!parm || !tree_node_bools (parm))
+    return NULL_TREE;
+
+  int parm_tag = insert (parm);
+  dump (dumper::TREE) && dump ("Reading template parm:%d %C:%N",
+			       parm_tag, TREE_CODE (parm), parm);
+
+  tree inner = parm;
+  if (TREE_CODE (inner) == TEMPLATE_DECL)
+    {
+      inner = start ();
+      if (!inner || !tree_node_bools (inner))
+	return NULL_TREE;
+      int inner_tag = insert (inner);
+      dump (dumper::TREE) && dump ("Reading inner template parm:%d %C:%N",
+				   inner_tag, TREE_CODE (inner), inner);
+      DECL_TEMPLATE_RESULT (parm) = inner;
+    }
+
+  tree type = NULL_TREE;
+  if (TREE_CODE (inner) == TYPE_DECL)
+    {
+      type = start ();
+      if (!type || !tree_node_bools (type))
+	return NULL_TREE;
+      int type_tag = insert (type);
+      dump (dumper::TREE) && dump ("Reading template parm type:%d %C:%N",
+				   type_tag, TREE_CODE (type), type);
+
+      TREE_TYPE (inner) = TREE_TYPE (parm) = type;
+      TYPE_NAME (type) = parm;
+    }
+
+  if (inner != parm)
+    {
+      /* A template template parameter.  */
+      unsigned tpl_levels = 0;
+      tpl_header (parm, &tpl_levels);
+      tpl_parms_fini (parm, tpl_levels);
+    }
+
+  tree_node_vals (parm);
+  if (inner != parm)
+    tree_node_vals (inner);
+  if (type)
+    {
+      tree_node_vals (type);
+      if (DECL_NAME (inner) == auto_identifier
+	  || DECL_NAME (inner) == decltype_auto_identifier)
+	{
+	  /* Placeholder auto.  */
+	  DECL_INITIAL (inner) = tree_node ();
+	  DECL_SIZE_UNIT (inner) = tree_node ();
+	}
+      if (TYPE_CANONICAL (type))
+	{
+	  gcc_checking_assert (TYPE_CANONICAL (type) == type);
+	  TYPE_CANONICAL (type) = canonical_type_parameter (type);
+	}
+    }
+
+  dump (dumper::TREE) && dump ("Read template parm:%d %C:%N",
+			       parm_tag, TREE_CODE (parm), parm);
+
+  return parm;
+}
+
+void
+trees_out::install_entity (tree decl, depset *dep)
+{
+  gcc_checking_assert (streaming_p ());
+  
+  /* Write the entity index, so we can insert it as soon as we
+     know this is new.  */
+  u (dep ? dep->cluster + 1 : 0);
+  if (CHECKING_P && dep)
+    {
+      /* Add it to the entity map, such that we can tell it is
+	 part of us.  */
+      bool existed;
+      unsigned *slot = &entity_map->get_or_insert
+	(DECL_UID (decl), &existed);
+      if (existed)
+	/* If it existed, it should match.  */
+	gcc_checking_assert (decl == (*entity_ary)[*slot]);
+      *slot = ~dep->cluster;
+    }
+}
+
+bool
+trees_in::install_entity (tree decl)
+{
+  unsigned entity_index = u ();
+  if (!entity_index)
+    return false;
+
+  if (entity_index > state->entity_num)
+    {
+      set_overrun ();
+      return false;
+    }
+
+  /* Insert the real decl into the entity ary.  */
+  unsigned ident = state->entity_lwm + entity_index - 1;
+  mc_slot &elt = (*entity_ary)[ident];
+
+  /* See module_state::read_pendings for how this got set.  */
+  int pending = elt.get_lazy () & 3;
+
+  elt = decl;
+
+  /* And into the entity map, if it's not already there.  */
+  if (!DECL_LANG_SPECIFIC (decl)
+      || !DECL_MODULE_ENTITY_P (decl))
+    {
+      retrofit_lang_decl (decl);
+      DECL_MODULE_ENTITY_P (decl) = true;
+
+      /* Insert into the entity hash (it cannot already be there).  */
+      bool existed;
+      unsigned &slot = entity_map->get_or_insert (DECL_UID (decl), &existed);
+      gcc_checking_assert (!existed);
+      slot = ident;
+    }
+  else if (pending != 0)
+    {
+      unsigned key_ident = import_entity_index (decl);
+      if (pending & 1)
+	if (!pending_table->add (key_ident, ~ident))
+	  pending &= ~1;
+
+      if (pending & 2)
+	if (!pending_table->add (~key_ident, ~ident))
+	  pending &= ~2;
+    }
+
+  if (pending & 1)
+    DECL_MODULE_PENDING_SPECIALIZATIONS_P (decl) = true;
+
+  if (pending & 2)
+    {
+      DECL_MODULE_PENDING_MEMBERS_P (decl) = true;
+      gcc_checking_assert (TREE_CODE (decl) != TEMPLATE_DECL);
+    }
+
+  return true;
+}
+
+static bool has_definition (tree decl);
+
+/* DECL is a decl node that must be written by value.  DEP is the
+   decl's depset.  */
+
+void
+trees_out::decl_value (tree decl, depset *dep)
+{
+  /* We should not be writing clones or template parms.  */
+  gcc_checking_assert (DECL_P (decl)
+		       && !DECL_CLONED_FUNCTION_P (decl)
+		       && !DECL_TEMPLATE_PARM_P (decl));
+
+  /* We should never be writing non-typedef ptrmemfuncs by value.  */
+  gcc_checking_assert (TREE_CODE (decl) != TYPE_DECL
+		       || DECL_ORIGINAL_TYPE (decl)
+		       || !TYPE_PTRMEMFUNC_P (TREE_TYPE (decl)));
+
+  merge_kind mk = get_merge_kind (decl, dep);
+
+  if (CHECKING_P)
+    {
+      /* Never start in the middle of a template.  */
+      int use_tpl = -1;
+      if (tree ti = node_template_info (decl, use_tpl))
+	gcc_checking_assert (TREE_CODE (TI_TEMPLATE (ti)) == OVERLOAD
+			     || TREE_CODE (TI_TEMPLATE (ti)) == FIELD_DECL
+			     || (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti))
+				 != decl));
+    }
+
+  if (streaming_p ())
+    {
+      /* A new node -> tt_decl.  */
+      decl_val_count++;
+      i (tt_decl);
+      u (mk);
+      start (decl);
+
+      if (mk != MK_unique)
+	{
+	  if (!(mk & MK_template_mask) && !state->is_header ())
+	    {
+	      /* Tell the importer whether this is a global module entity,
+		 or a module entity.  This bool merges into the next block
+		 of bools.  Sneaky.  */
+	      tree o = get_originating_module_decl (decl);
+	      bool is_mod = false;
+
+	      if (dep && dep->is_alias_tmpl_inst ())
+		/* Alias template instantiations are templatey, but
+		   found by name.  */
+		is_mod = false;
+	      else if (DECL_LANG_SPECIFIC (o) && DECL_MODULE_PURVIEW_P (o))
+		is_mod = true;
+	      b (is_mod);
+	    }
+	  b (dep && dep->has_defn ());
+	}
+      tree_node_bools (decl);
+    }
+
+  int tag = insert (decl, WK_value);
+  if (streaming_p ())
+    dump (dumper::TREE)
+      && dump ("Writing %s:%d %C:%N%S", merge_kind_name[mk], tag,
+	       TREE_CODE (decl), decl, decl);
+
+  tree inner = decl;
+  int inner_tag = 0;
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      if (dep && dep->is_alias_tmpl_inst ())
+	inner = NULL_TREE;
+      else
+	{
+	  inner = DECL_TEMPLATE_RESULT (decl);
+	  inner_tag = insert (inner, WK_value);
+	}
+
+      if (streaming_p ())
+	{
+	  int code = inner ? TREE_CODE (inner) : 0;
+	  u (code);
+	  if (inner)
+	    {
+	      start (inner, true);
+	      tree_node_bools (inner);
+	      dump (dumper::TREE)
+		&& dump ("Writing %s:%d %C:%N%S", merge_kind_name[mk], inner_tag,
+			 TREE_CODE (inner), inner, inner);
+	    }
+	}
+    }
+
+  tree type = NULL_TREE;
+  int type_tag = 0;
+  tree stub_decl = NULL_TREE;
+  int stub_tag = 0;
+  if (inner && TREE_CODE (inner) == TYPE_DECL)
+    {
+      type = TREE_TYPE (inner);
+      bool has_type = (type == TYPE_MAIN_VARIANT (type)
+		       && TYPE_NAME (type) == inner);
+
+      if (streaming_p ())
+	u (has_type ? TREE_CODE (type) : 0);
+
+      if (has_type)
+	{
+	  type_tag = insert (type, WK_value);
+	  if (streaming_p ())
+	    {
+	      start (type, true);
+	      tree_node_bools (type);
+	      dump (dumper::TREE)
+		&& dump ("Writing type:%d %C:%N", type_tag,
+			 TREE_CODE (type), type);
+	    }
+
+	  stub_decl = TYPE_STUB_DECL (type);
+	  bool has_stub = inner != stub_decl;
+	  if (streaming_p ())
+	    u (has_stub ? TREE_CODE (stub_decl) : 0);
+	  if (has_stub)
+	    {
+	      stub_tag = insert (stub_decl);
+	      if (streaming_p ())
+		{
+		  start (stub_decl, true);
+		  tree_node_bools (stub_decl);
+		  dump (dumper::TREE)
+		    && dump ("Writing stub_decl:%d %C:%N", stub_tag,
+			     TREE_CODE (stub_decl), stub_decl);
+		}
+	    }
+	  else
+	    stub_decl = NULL_TREE;
+	}
+      else
+	/* Regular typedef.  */
+	type = NULL_TREE;
+    }
+
+  /* Stream the container, we want it correctly canonicalized before
+     we start emitting keys for this decl.  */
+  tree container = decl_container (decl);
+
+  unsigned tpl_levels = 0;
+  if (decl != inner)
+    tpl_header (decl, &tpl_levels);
+  if (inner && TREE_CODE (inner) == FUNCTION_DECL)
+    fn_parms_init (inner);
+
+  /* Now write out the merging information, and then really
+     install the tag values.  */
+  key_mergeable (tag, mk, decl, inner, container, dep);
+
+  if (streaming_p ())
+    dump (dumper::MERGE)
+      && dump ("Wrote:%d's %s merge key %C:%N", tag,
+	       merge_kind_name[mk], TREE_CODE (decl), decl);
+
+  if (inner && TREE_CODE (inner) == FUNCTION_DECL)
+    fn_parms_fini (inner);
+
+  if (!is_key_order ())
+    tree_node_vals (decl);
+
+  if (inner_tag)
+    {
+      if (!is_key_order ())
+	tree_node_vals (inner);
+      tpl_parms_fini (decl, tpl_levels);
+    }
+  else if (!inner)
+    {
+      /* A template alias instantiation.  */
+      inner = DECL_TEMPLATE_RESULT (decl);
+      if (!is_key_order ())
+	tree_node (inner);
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Wrote(%d) alias template %C:%N",
+		   get_tag (inner), TREE_CODE (inner), inner);
+      inner = NULL_TREE;
+    }
+
+  if (type && !is_key_order ())
+    {
+      tree_node_vals (type);
+      if (stub_decl)
+	tree_node_vals (stub_decl);
+    }
+
+  if (!is_key_order ())
+    tree_node (get_constraints (decl));
+
+  if (streaming_p ())
+    {
+      /* Do not stray outside this section.  */
+      gcc_checking_assert (!dep || dep->section == dep_hash->section);
+
+      /* Write the entity index, so we can insert it as soon as we
+	 know this is new.  */
+      install_entity (decl, dep);
+    }
+
+  if (inner && DECL_LANG_SPECIFIC (inner) && DECL_ATTACHED_DECLS_P (inner)
+      && !is_key_order ())
+    {
+      /* Stream the attached entities.  */
+      attachset *set = attached_table->get (DECL_UID (inner));
+      unsigned num = set->num;
+      if (streaming_p ())
+	u (num);
+      for (unsigned ix = 0; ix != num; ix++)
+	{
+	  tree attached = set->values[ix];
+	  tree_node (attached);
+	  if (streaming_p ())
+	    dump (dumper::MERGE)
+	      && dump ("Written %d[%u] attached decl %N", tag, ix, attached);
+	}
+    }
+
+  bool is_typedef = (!type && inner
+		     && TREE_CODE (inner) == TYPE_DECL
+		     && DECL_ORIGINAL_TYPE (inner)
+		     && TYPE_NAME (TREE_TYPE (inner)) == inner);
+  if (is_typedef)
+    {
+      /* A typedef type.  */
+      int type_tag = insert (TREE_TYPE (inner));
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Cloned:%d typedef %C:%N", type_tag,
+		   TREE_CODE (TREE_TYPE (inner)), TREE_TYPE (inner));
+    }
+
+  if (streaming_p () && DECL_MAYBE_IN_CHARGE_CDTOR_P (decl))
+    {
+      bool cloned_p
+	= (DECL_CHAIN (decl) && DECL_CLONED_FUNCTION_P (DECL_CHAIN (decl)));
+      bool needs_vtt_parm_p
+	= (cloned_p && CLASSTYPE_VBASECLASSES (DECL_CONTEXT (decl)));
+      bool omit_inherited_parms_p
+	= (cloned_p && DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl)
+	   && base_ctor_omit_inherited_parms (decl));
+      unsigned flags = (int (cloned_p) << 0
+			| int (needs_vtt_parm_p) << 1
+			| int (omit_inherited_parms_p) << 2);
+      u (flags);
+      dump (dumper::TREE) && dump ("CDTOR %N is %scloned",
+				   decl, cloned_p ? "" : "not ");
+    }
+
+  if (streaming_p ())
+    dump (dumper::TREE) && dump ("Written decl:%d %C:%N", tag,
+				 TREE_CODE (decl), decl);
+
+  if (!inner || NAMESPACE_SCOPE_P (inner))
+    gcc_checking_assert (!inner
+			 || !dep == (VAR_OR_FUNCTION_DECL_P (inner)
+				     && DECL_LOCAL_DECL_P (inner)));
+  else if ((TREE_CODE (inner) == TYPE_DECL
+	    && TYPE_NAME (TREE_TYPE (inner)) == inner
+	    && !is_typedef)
+	   || TREE_CODE (inner) == FUNCTION_DECL)
+    {
+      bool write_defn = !dep && has_definition (decl);
+      if (streaming_p ())
+	u (write_defn);
+      if (write_defn)
+	write_definition (decl);
+    }
+}
+
+tree
+trees_in::decl_value ()
+{
+  int tag = 0;
+  bool is_mod = false;
+  bool has_defn = false;
+  unsigned mk_u = u ();
+  if (mk_u >= MK_hwm || !merge_kind_name[mk_u])
+    {
+      set_overrun ();
+      return NULL_TREE;
+    }
+
+  unsigned saved_unused = unused;
+  unused = 0;
+  
+  merge_kind mk = merge_kind (mk_u);
+
+  tree decl = start ();
+  if (decl)
+    {
+      if (mk != MK_unique)
+	{
+	  if (!(mk & MK_template_mask) && !state->is_header ())
+	    /* See note in trees_out about where this bool is sequenced.  */
+	    is_mod = b ();
+
+	  has_defn = b ();
+	}
+
+      if (!tree_node_bools (decl))
+	decl = NULL_TREE;
+    }
+  
+  /* Insert into map.  */
+  tag = insert (decl);
+  if (decl)
+    dump (dumper::TREE)
+      && dump ("Reading:%d %C", tag, TREE_CODE (decl));
+
+  tree inner = decl;
+  int inner_tag = 0;
+  if (decl && TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      int code = u ();
+      if (!code)
+	{
+	  inner = NULL_TREE;
+	  DECL_TEMPLATE_RESULT (decl) = error_mark_node;
+	}
+      else
+	{
+	  inner = start (code);
+	  if (inner && tree_node_bools (inner))
+	    DECL_TEMPLATE_RESULT (decl) = inner;
+	  else
+	    decl = NULL_TREE;
+
+	  inner_tag = insert (inner);
+	  if (decl)
+	    dump (dumper::TREE)
+	      && dump ("Reading:%d %C", inner_tag, TREE_CODE (inner));
+	}
+    }
+
+  tree type = NULL_TREE;
+  int type_tag = 0;
+  tree stub_decl = NULL_TREE;
+  int stub_tag = 0;
+  if (decl && inner && TREE_CODE (inner) == TYPE_DECL)
+    {
+      if (unsigned type_code = u ())
+	{
+	  type = start (type_code);
+	  if (type && tree_node_bools (type))
+	    {
+	      TREE_TYPE (inner) = type;
+	      TYPE_NAME (type) = inner;
+	    }
+	  else
+	    decl = NULL_TREE;
+
+	  type_tag = insert (type);
+	  if (decl)
+	    dump (dumper::TREE)
+	      && dump ("Reading type:%d %C", type_tag, TREE_CODE (type));
+
+	  if (unsigned stub_code = u ())
+	    {
+	      stub_decl = start (stub_code);
+	      if (stub_decl && tree_node_bools (stub_decl))
+		{
+		  TREE_TYPE (stub_decl) = type;
+		  TYPE_STUB_DECL (type) = stub_decl;
+		}
+	      else
+		decl = NULL_TREE;
+
+	      stub_tag = insert (stub_decl);
+	      if (decl)
+		dump (dumper::TREE)
+		  && dump ("Reading stub_decl:%d %C", stub_tag,
+			   TREE_CODE (stub_decl));
+	    }
+	}
+    }
+
+  if (!decl)
+    {
+    bail:
+      if (inner_tag != 0)
+	back_refs[~inner_tag] = NULL_TREE;
+      if (type_tag != 0)
+	back_refs[~type_tag] = NULL_TREE;
+      if (stub_tag != 0)
+	back_refs[~stub_tag] = NULL_TREE;
+      if (tag != 0)
+	back_refs[~tag] = NULL_TREE;
+      set_overrun ();
+      /* Bail.  */
+      unused = saved_unused;
+      return NULL_TREE;
+    }
+
+  /* Read the container, to ensure it's already been streamed in.  */
+  tree container = decl_container ();
+  unsigned tpl_levels = 0;
+
+  /* Figure out if this decl is already known about.  */
+  int parm_tag = 0;
+
+  if (decl != inner)
+    if (!tpl_header (decl, &tpl_levels))
+      goto bail;
+  if (inner && TREE_CODE (inner) == FUNCTION_DECL)
+    parm_tag = fn_parms_init (inner);
+
+  tree existing = key_mergeable (tag, mk, decl, inner, type, container, is_mod);
+  tree existing_inner = existing;
+  if (existing)
+    {
+      if (existing == error_mark_node)
+	goto bail;
+
+      if (TREE_CODE (STRIP_TEMPLATE (existing)) == TYPE_DECL)
+	{
+	  tree etype = TREE_TYPE (existing);
+	  if (TYPE_LANG_SPECIFIC (etype)
+	      && COMPLETE_TYPE_P (etype)
+	      && !CLASSTYPE_MEMBER_VEC (etype))
+	    /* Give it a member vec, we're likely gonna be looking
+	       inside it.  */
+	    set_class_bindings (etype, -1);
+	}
+
+      /* Install the existing decl into the back ref array.  */
+      register_duplicate (decl, existing);
+      back_refs[~tag] = existing;
+      if (inner_tag != 0)
+	{
+	  existing_inner = DECL_TEMPLATE_RESULT (existing);
+	  back_refs[~inner_tag] = existing_inner;
+	}
+
+      if (type_tag != 0)
+	{
+	  tree existing_type = TREE_TYPE (existing);
+	  back_refs[~type_tag] = existing_type;
+	  if (stub_tag != 0)
+	    back_refs[~stub_tag] = TYPE_STUB_DECL (existing_type);
+	}
+    }
+
+  if (parm_tag)
+    fn_parms_fini (parm_tag, inner, existing_inner, has_defn);
+
+  if (!tree_node_vals (decl))
+    goto bail;
+
+  if (inner_tag)
+    {
+      gcc_checking_assert (DECL_TEMPLATE_RESULT (decl) == inner);
+
+      if (!tree_node_vals (inner))
+	goto bail;
+
+      if (!tpl_parms_fini (decl, tpl_levels))
+	goto bail;
+    }
+  else if (!inner)
+    {
+      inner = tree_node ();
+      DECL_TEMPLATE_RESULT (decl) = inner;
+      TREE_TYPE (decl) = TREE_TYPE (inner);
+      dump (dumper::TREE)
+	&& dump ("Read alias template %C:%N", TREE_CODE (inner), inner);
+      inner = NULL_TREE;
+    }
+
+  if (type && (!tree_node_vals (type)
+	       || (stub_decl && !tree_node_vals (stub_decl))))
+    goto bail;
+
+  tree constraints = tree_node ();
+
+  dump (dumper::TREE) && dump ("Read:%d %C:%N", tag, TREE_CODE (decl), decl);
+
+  /* Regular typedefs will have a NULL TREE_TYPE at this point.  */
+  bool is_typedef = (!type && inner
+		     && TREE_CODE (inner) == TYPE_DECL
+		     && DECL_ORIGINAL_TYPE (inner)
+		     && !TREE_TYPE (inner));
+  if (is_typedef)
+    {
+      /* Frob it to be ready for cloning.  */
+      TREE_TYPE (inner) = DECL_ORIGINAL_TYPE (inner);
+      DECL_ORIGINAL_TYPE (inner) = NULL_TREE;
+    }
+
+  existing = back_refs[~tag];
+  bool installed = install_entity (existing);
+  bool is_new = existing == decl;
+
+  if (inner && DECL_LANG_SPECIFIC (inner) && DECL_ATTACHED_DECLS_P (inner))
+    {
+      /* Read and maybe install the attached entities.  */
+      attachset *set
+	= attached_table->get (DECL_UID (STRIP_TEMPLATE (existing)));
+      unsigned num = u ();
+      if (!is_new == !set)
+	set_overrun ();
+      if (is_new)
+	set = attached_table->create (DECL_UID (inner), num, NULL_TREE);
+      for (unsigned ix = 0; !get_overrun () && ix != num; ix++)
+	{
+	  tree attached = tree_node ();
+	  dump (dumper::MERGE)
+	    && dump ("Read %d[%u] %s attached decl %N", tag, ix,
+		     is_new ? "new" : "matched", attached);
+	  if (is_new)
+	    set->values[ix] = attached;
+	  else if (set->values[ix] != attached)
+	    set_overrun ();
+	}
+    }
+
+  if (is_new)
+    {
+      /* A newly discovered node.  */
+      if (TREE_CODE (decl) == FUNCTION_DECL && DECL_VIRTUAL_P (decl))
+	/* Mark this identifier as naming a virtual function --
+	   lookup_overrides relies on this optimization.  */
+	IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = true;
+
+      if (installed)
+	{
+	  /* Mark the entity as imported and add it to the entity
+	     array and map.  */
+	  retrofit_lang_decl (decl);
+	  DECL_MODULE_IMPORT_P (decl) = true;
+	  if (inner_tag)
+	    {
+	      retrofit_lang_decl (inner);
+	      DECL_MODULE_IMPORT_P (inner) = true;
+	    }
+	}
+
+      if (constraints)
+	set_constraints (decl, constraints);
+
+      if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl))
+	{
+	  decl = cache_integer_cst (decl, true);
+	  back_refs[~tag] = decl;
+	}
+
+      if (is_typedef)
+	set_underlying_type (inner);
+
+      if (inner_tag)
+	/* Set the TEMPLATE_DECL's type.  */
+	TREE_TYPE (decl) = TREE_TYPE (inner);
+
+      /* The late insertion of an alias here or an implicit member
+         (next block), is ok, because we ensured that all imports were
+         loaded up before we started this cluster.  Thus an insertion
+         from some other import cannot have happened between the
+         merged insertion above and these insertions down here.  */
+      if (mk == MK_alias_spec)
+	{
+	  /* Insert into type table.  */
+	  tree ti = DECL_TEMPLATE_INFO (inner);
+	  tree texist = match_mergeable_specialization
+	    (false, TI_TEMPLATE (ti), TI_ARGS (ti), TREE_TYPE (inner));
+	  if (texist)
+	    set_overrun ();
+	}
+
+      if (DECL_ARTIFICIAL (decl)
+	  && TREE_CODE (decl) == FUNCTION_DECL
+	  && !DECL_TEMPLATE_INFO (decl)
+	  && DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl))
+	  && TYPE_SIZE (DECL_CONTEXT (decl))
+	  && !DECL_THUNK_P (decl))
+	/* A new implicit member function, when the class is
+	   complete.  This means the importee declared it, and
+	   we must now add it to the class.  Note that implicit
+	   member fns of template instantiations do not themselves
+	   look like templates.  */
+	if (!install_implicit_member (inner))
+	  set_overrun ();
+    }
+  else
+    {
+      /* DECL is the to-be-discarded decl.  Its internal pointers will
+	 be to the EXISTING's structure.  Frob it to point to its
+	 own other structures, so loading its definition will alter
+	 it, and not the existing decl.  */
+      dump (dumper::MERGE) && dump ("Deduping %N", existing);
+
+      if (inner_tag)
+	DECL_TEMPLATE_RESULT (decl) = inner;
+
+      if (type)
+	{
+	  /* Point at the to-be-discarded type & decl.  */
+	  TYPE_NAME (type) = inner;
+	  TREE_TYPE (inner) = type;
+
+	  TYPE_STUB_DECL (type) = stub_decl ? stub_decl : inner;
+	  if (stub_decl)
+	    TREE_TYPE (stub_decl) = type;
+	}
+
+      if (inner_tag)
+	/* Set the TEMPLATE_DECL's type.  */
+	TREE_TYPE (decl) = TREE_TYPE (inner);
+
+      if (!is_matching_decl (existing, decl))
+	unmatched_duplicate (existing);
+
+      /* And our result is the existing node.  */
+      decl = existing;
+    }
+
+  if (is_typedef)
+    {
+      /* Insert the type into the array now.  */
+      tag = insert (TREE_TYPE (decl));
+      dump (dumper::TREE)
+	&& dump ("Cloned:%d typedef %C:%N",
+		 tag, TREE_CODE (TREE_TYPE (decl)), TREE_TYPE (decl));
+    }
+
+  unused = saved_unused;
+
+  if (DECL_MAYBE_IN_CHARGE_CDTOR_P (decl))
+    {
+      unsigned flags = u ();
+
+      if (is_new)
+	{
+	  bool cloned_p = flags & 1;
+	  dump (dumper::TREE) && dump ("CDTOR %N is %scloned",
+				       decl, cloned_p ? "" : "not ");
+	  if (cloned_p)
+	    build_cdtor_clones (decl, flags & 2, flags & 4,
+				/* Update the member vec, if there is
+				   one (we're in a different cluster
+				   to the class defn).  */
+				CLASSTYPE_MEMBER_VEC (DECL_CONTEXT (decl)));
+	}
+    }
+
+  if (inner
+      && !NAMESPACE_SCOPE_P (inner)
+      && ((TREE_CODE (inner) == TYPE_DECL
+	   && TYPE_NAME (TREE_TYPE (inner)) == inner
+	   && !is_typedef)
+	  || TREE_CODE (inner) == FUNCTION_DECL)
+      && u ())
+    read_definition (decl);
+
+  return decl;
+}
+
+/* DECL is an unnameable member of CTX.  Return a suitable identifying
+   index.  */
+
+static unsigned
+get_field_ident (tree ctx, tree decl)
+{
+  gcc_checking_assert (TREE_CODE (decl) == USING_DECL
+		       || !DECL_NAME (decl)
+		       || IDENTIFIER_ANON_P (DECL_NAME (decl)));
+
+  unsigned ix = 0;
+  for (tree fields = TYPE_FIELDS (ctx);
+       fields; fields = DECL_CHAIN (fields))
+    {
+      if (fields == decl)
+	return ix;
+
+      if (DECL_CONTEXT (fields) == ctx
+	  && (TREE_CODE (fields) == USING_DECL
+	      || (TREE_CODE (fields) == FIELD_DECL
+		  && (!DECL_NAME (fields)
+		      || IDENTIFIER_ANON_P (DECL_NAME (fields))))))
+	/* Count this field.  */
+	ix++;
+    }
+  gcc_unreachable ();
+}
+
+static tree
+lookup_field_ident (tree ctx, unsigned ix)
+{
+  for (tree fields = TYPE_FIELDS (ctx);
+       fields; fields = DECL_CHAIN (fields))
+    if (DECL_CONTEXT (fields) == ctx
+	&& (TREE_CODE (fields) == USING_DECL
+	    || (TREE_CODE (fields) == FIELD_DECL
+		&& (!DECL_NAME (fields)
+		    || IDENTIFIER_ANON_P (DECL_NAME (fields))))))
+      if (!ix--)
+	return fields;
+
+  return NULL_TREE;
+}
+
+/* Reference DECL.  REF indicates the walk kind we are performing.
+   Return true if we should write this decl by value.  */
+
+bool
+trees_out::decl_node (tree decl, walk_kind ref)
+{
+  gcc_checking_assert (DECL_P (decl) && !DECL_TEMPLATE_PARM_P (decl)
+		       && DECL_CONTEXT (decl));
+
+  if (ref == WK_value)
+    {
+      depset *dep = dep_hash->find_dependency (decl);
+      decl_value (decl, dep);
+      return false;
+    }
+
+  switch (TREE_CODE (decl))
+    {
+    default:
+      break;
+
+    case FUNCTION_DECL:
+      gcc_checking_assert (!DECL_LOCAL_DECL_P (decl));
+      break;
+
+    case RESULT_DECL:
+      /* Unlike PARM_DECLs, RESULT_DECLs are only generated and
+         referenced when we're inside the function itself.  */
+      return true;
+
+    case PARM_DECL:
+      {
+	if (streaming_p ())
+	  i (tt_parm);
+	tree_node (DECL_CONTEXT (decl));
+	if (streaming_p ())
+	  {
+	    /* That must have put this in the map.  */
+	    walk_kind ref = ref_node (decl);
+	    if (ref != WK_none)
+	      // FIXME:OPTIMIZATION We can wander into bits of the
+	      // template this was instantiated from.  For instance
+	      // deferred noexcept and default parms.  Currently we'll
+	      // end up cloning those bits of tree.  It would be nice
+	      // to reference those specific nodes.  I think putting
+	      // those things in the map when we reference their
+	      // template by name.  See the note in add_indirects.
+	      return true;
+
+	    dump (dumper::TREE)
+	      && dump ("Wrote %s reference %N",
+		       TREE_CODE (decl) == PARM_DECL ? "parameter" : "result",
+		       decl);
+	  }
+      }
+      return false;
+
+    case IMPORTED_DECL:
+      /* This describes a USING_DECL to the ME's debug machinery.  It
+	 originates from the fortran FE, and has nothing to do with
+	 C++ modules.  */
+      return true;
+
+    case LABEL_DECL:
+      return true;
+
+    case CONST_DECL:
+      {
+	/* If I end up cloning enum decls, implementing C++20 using
+	   E::v, this will need tweaking.   */
+	if (streaming_p ())
+	  i (tt_enum_decl);
+	tree ctx = DECL_CONTEXT (decl);
+	gcc_checking_assert (TREE_CODE (ctx) == ENUMERAL_TYPE);
+	tree_node (ctx);
+	tree_node (DECL_NAME (decl));
+
+	int tag = insert (decl);
+	if (streaming_p ())
+	  dump (dumper::TREE)
+	    && dump ("Wrote enum decl:%d %C:%N", tag, TREE_CODE (decl), decl);
+	return false;
+      }
+      break;
+
+    case USING_DECL:
+      if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+	break;
+      /* FALLTHROUGH  */
+
+    case FIELD_DECL:
+      {
+	if (streaming_p ())
+	  i (tt_data_member);
+
+	tree ctx = DECL_CONTEXT (decl);
+	tree_node (ctx);
+
+	tree name = NULL_TREE;
+
+	if (TREE_CODE (decl) == USING_DECL)
+	  ;
+	else
+	  {
+	    name = DECL_NAME (decl);
+	    if (name && IDENTIFIER_ANON_P (name))
+	      name = NULL_TREE;
+	  }
+
+	tree_node (name);
+	if (!name && streaming_p ())
+	  {
+	    unsigned ix = get_field_ident (ctx, decl);
+	    u (ix);
+	  }
+
+	int tag = insert (decl);
+	if (streaming_p ())
+	  dump (dumper::TREE)
+	    && dump ("Wrote member:%d %C:%N", tag, TREE_CODE (decl), decl);
+	return false;
+      }
+      break;
+
+    case VAR_DECL:
+      gcc_checking_assert (!DECL_LOCAL_DECL_P (decl));
+      if (DECL_VTABLE_OR_VTT_P (decl))
+	{
+	  /* VTT or VTABLE, they are all on the vtables list.  */
+	  tree ctx = CP_DECL_CONTEXT (decl);
+	  tree vtable = CLASSTYPE_VTABLES (ctx);
+	  for (unsigned ix = 0; ; vtable = DECL_CHAIN (vtable), ix++)
+	    if (vtable == decl)
+	      {
+		gcc_checking_assert (DECL_VIRTUAL_P (decl));
+		if (streaming_p ())
+		  {
+		    u (tt_vtable);
+		    u (ix);
+		    dump (dumper::TREE)
+		      && dump ("Writing vtable %N[%u]", ctx, ix);
+		  }
+		tree_node (ctx);
+		return false;
+	      }
+	  gcc_unreachable ();
+	}
+
+      if (DECL_TINFO_P (decl))
+	{
+	tinfo:
+	  /* A typeinfo, tt_tinfo_typedef or tt_tinfo_var.  */
+	  bool is_var = TREE_CODE (decl) == VAR_DECL;
+	  tree type = TREE_TYPE (decl);
+	  unsigned ix = get_pseudo_tinfo_index (type);
+	  if (streaming_p ())
+	    {
+	      i (is_var ? tt_tinfo_var : tt_tinfo_typedef);
+	      u (ix);
+	    }
+
+	  if (is_var)
+	    {
+	      /* We also need the type it is for and mangled name, so
+		 the reader doesn't need to complete the type (which
+		 would break section ordering).  The type it is for is
+		 stashed on the name's TREE_TYPE.  */
+	      tree name = DECL_NAME (decl);
+	      tree_node (name);
+	      type = TREE_TYPE (name);
+	      tree_node (type);
+	    }
+
+	  int tag = insert (decl);
+	  if (streaming_p ())
+	    dump (dumper::TREE)
+	      && dump ("Wrote tinfo_%s:%d %u %N", is_var ? "var" : "type",
+		       tag, ix, type);
+
+	  if (!is_var)
+	    {
+	      tag = insert (type);
+	      if (streaming_p ())
+		dump (dumper::TREE)
+		  && dump ("Wrote tinfo_type:%d %u %N", tag, ix, type);
+	    }
+	  return false;
+	}
+      break;
+
+    case TYPE_DECL:
+      if (DECL_TINFO_P (decl))
+	goto tinfo;
+      break;
+    }
+
+  if (DECL_THUNK_P (decl))
+    {
+      /* Thunks are similar to binfos -- write the thunked-to decl and
+	 then thunk-specific key info.  */
+      if (streaming_p ())
+	{
+	  i (tt_thunk);
+	  i (THUNK_FIXED_OFFSET (decl));
+	}
+
+      tree target = decl;
+      while (DECL_THUNK_P (target))
+	target = THUNK_TARGET (target);
+      tree_node (target);
+      tree_node (THUNK_VIRTUAL_OFFSET (decl));
+      int tag = insert (decl);
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Wrote:%d thunk %N to %N", tag, DECL_NAME (decl), target);
+      return false;
+    }
+
+  if (DECL_CLONED_FUNCTION_P (decl))
+    {
+      tree target = get_clone_target (decl);
+      if (streaming_p ())
+	i (tt_clone_ref);
+
+      tree_node (target);
+      tree_node (DECL_NAME (decl));
+      int tag = insert (decl);
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Wrote:%d clone %N of %N", tag, DECL_NAME (decl), target);
+      return false;
+    }
+
+  /* Everything left should be a thing that is in the entity table.
+     Mostly things that can be defined outside of their (original
+     declaration) context.  */
+  gcc_checking_assert (TREE_CODE (decl) == TEMPLATE_DECL
+		       || TREE_CODE (decl) == VAR_DECL
+		       || TREE_CODE (decl) == FUNCTION_DECL
+		       || TREE_CODE (decl) == TYPE_DECL
+		       || TREE_CODE (decl) == USING_DECL
+		       || TREE_CODE (decl) == CONCEPT_DECL
+		       || TREE_CODE (decl) == NAMESPACE_DECL);
+
+  int use_tpl = -1;
+  tree ti = node_template_info (decl, use_tpl);
+  tree tpl = NULL_TREE;
+
+  /* If this is the TEMPLATE_DECL_RESULT of a TEMPLATE_DECL, get the
+     TEMPLATE_DECL.  Note TI_TEMPLATE is not a TEMPLATE_DECL for
+     (some) friends, so we need to check that.  */
+  // FIXME: Should local friend template specializations be by value?
+  // They don't get idents so we'll never know they're imported, but I
+  // think we can only reach them from the TU that defines the
+  // befriending class?
+  if (ti && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL
+      && DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == decl)
+    {
+      tpl = TI_TEMPLATE (ti);
+    partial_template:
+      if (streaming_p ())
+	{
+	  i (tt_template);
+	  dump (dumper::TREE)
+	    && dump ("Writing implicit template %C:%N%S",
+		     TREE_CODE (tpl), tpl, tpl);
+	}
+      tree_node (tpl);
+
+      /* Streaming TPL caused us to visit DECL and maybe its type.  */
+      gcc_checking_assert (TREE_VISITED (decl));
+      if (DECL_IMPLICIT_TYPEDEF_P (decl))
+	gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl)));
+      return false;
+    }
+
+  tree ctx = CP_DECL_CONTEXT (decl);
+  depset *dep = NULL;
+  if (streaming_p ())
+    dep = dep_hash->find_dependency (decl);
+  else if (TREE_CODE (ctx) != FUNCTION_DECL
+	   || TREE_CODE (decl) == TEMPLATE_DECL
+	   || (dep_hash->sneakoscope && DECL_IMPLICIT_TYPEDEF_P (decl))
+	   || (DECL_LANG_SPECIFIC (decl)
+	       && DECL_MODULE_IMPORT_P (decl)))
+    dep = dep_hash->add_dependency (decl,
+				    TREE_CODE (decl) == NAMESPACE_DECL
+				    && !DECL_NAMESPACE_ALIAS (decl)
+				    ? depset::EK_NAMESPACE : depset::EK_DECL);
+
+  if (!dep)
+    {
+      /* Some internal entity of context.  Do by value.  */
+      decl_value (decl, NULL);
+      return false;
+    }
+
+  if (dep->get_entity_kind () == depset::EK_REDIRECT)
+    {
+      /* The DECL_TEMPLATE_RESULT of a partial specialization.
+	 Write the partial specialization's template.  */
+      depset *redirect = dep->deps[0];
+      gcc_checking_assert (redirect->is_partial ());
+      tpl = redirect->get_entity ();
+      goto partial_template;
+    }
+
+  if (streaming_p ())
+    {
+      /* Locate the entity.  */
+      unsigned index = dep->cluster;
+      unsigned import = 0;
+
+      if (dep->is_import ())
+	import = dep->section;
+      else if (CHECKING_P)
+	/* It should be what we put there.  */
+	gcc_checking_assert (index == ~import_entity_index (decl));
+
+#if CHECKING_P
+      if (importedness)
+	gcc_assert (!import == (importedness < 0));
+#endif
+      i (tt_entity);
+      u (import);
+      u (index);
+    }
+
+  int tag = insert (decl);
+  if (streaming_p () && dump (dumper::TREE))
+    {
+      char const *kind = "import";
+      module_state *from = (*modules)[0];
+      if (dep->is_import ())
+	/* Rediscover the unremapped index.  */
+	from = import_entity_module (import_entity_index (decl));
+      else
+	{
+	  tree o = get_originating_module_decl (decl);
+	  kind = (DECL_LANG_SPECIFIC (o) && DECL_MODULE_PURVIEW_P (o)
+		  ? "purview" : "GMF");
+	}
+      dump ("Wrote %s:%d %C:%N@%M", kind,
+	    tag, TREE_CODE (decl), decl, from);
+    }
+
+  add_indirects (decl);
+
+  return false;
+}
+
+void
+trees_out::type_node (tree type)
+{
+  gcc_assert (TYPE_P (type));
+
+  tree root = (TYPE_NAME (type)
+	       ? TREE_TYPE (TYPE_NAME (type)) : TYPE_MAIN_VARIANT (type));
+
+  if (type != root)
+    {
+      if (streaming_p ())
+	i (tt_variant_type);
+      tree_node (root);
+
+      int flags = -1;
+
+      if (TREE_CODE (type) == FUNCTION_TYPE
+	  || TREE_CODE (type) == METHOD_TYPE)
+	{
+	  int quals = type_memfn_quals (type);
+	  int rquals = type_memfn_rqual (type);
+	  tree raises = TYPE_RAISES_EXCEPTIONS (type);
+	  bool late = TYPE_HAS_LATE_RETURN_TYPE (type);
+
+	  if (raises != TYPE_RAISES_EXCEPTIONS (root)
+	      || rquals != type_memfn_rqual (root)
+	      || quals != type_memfn_quals (root)
+	      || late != TYPE_HAS_LATE_RETURN_TYPE (root))
+	    flags = rquals | (int (late) << 2) | (quals << 3);
+	}
+      else
+	{
+	  if (TYPE_USER_ALIGN (type))
+	    flags = exact_log2 (TYPE_ALIGN (type));
+	}
+
+      if (streaming_p ())
+	i (flags);
+
+      if (flags < 0)
+	;
+      else if (TREE_CODE (type) == FUNCTION_TYPE
+	       || TREE_CODE (type) == METHOD_TYPE)
+	{
+	  tree raises = TYPE_RAISES_EXCEPTIONS (type);
+	  if (raises == TYPE_RAISES_EXCEPTIONS (root))
+	    raises = error_mark_node;
+	  tree_node (raises);
+	}
+
+      tree_node (TYPE_ATTRIBUTES (type));
+
+      if (streaming_p ())
+	{
+	  /* Qualifiers.  */
+	  int rquals = cp_type_quals (root);
+	  int quals = cp_type_quals (type);
+	  if (quals == rquals)
+	    quals = -1;
+	  i (quals);
+	}
+
+      if (ref_node (type) != WK_none)
+	{
+	  int tag = insert (type);
+	  if (streaming_p ())
+	    {
+	      i (0);
+	      dump (dumper::TREE)
+		&& dump ("Wrote:%d variant type %C", tag, TREE_CODE (type));
+	    }
+	}
+      return;
+    }
+
+  if (tree name = TYPE_NAME (type))
+    if ((TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name))
+	|| DECL_TEMPLATE_PARM_P (name)
+	|| TREE_CODE (type) == RECORD_TYPE
+	|| TREE_CODE (type) == UNION_TYPE
+	|| TREE_CODE (type) == ENUMERAL_TYPE)
+      {
+	/* We can meet template parms that we didn't meet in the
+	   tpl_parms walk, because we're referring to a derived type
+	   that was previously constructed from equivalent template
+	   parms. */
+	if (streaming_p ())
+	  {
+	    i (tt_typedef_type);
+	    dump (dumper::TREE)
+	      && dump ("Writing %stypedef %C:%N",
+		       DECL_IMPLICIT_TYPEDEF_P (name) ? "implicit " : "",
+		       TREE_CODE (name), name);
+	  }
+	tree_node (name);
+	if (streaming_p ())
+	  dump (dumper::TREE) && dump ("Wrote typedef %C:%N%S",
+				       TREE_CODE (name), name, name);
+	gcc_checking_assert (TREE_VISITED (type));
+	return;
+      }
+
+  if (TYPE_PTRMEMFUNC_P (type))
+    {
+      /* This is a distinct type node, masquerading as a structure. */
+      tree fn_type = TYPE_PTRMEMFUNC_FN_TYPE (type);
+      if (streaming_p ())
+	i (tt_ptrmem_type);
+      tree_node (fn_type);
+      int tag = insert (type);
+      if (streaming_p ())
+	dump (dumper::TREE) && dump ("Written:%d ptrmem type", tag);
+      return;
+    }
+
+  if (streaming_p ())
+    {
+      u (tt_derived_type);
+      u (TREE_CODE (type));
+    }
+
+  tree_node (TREE_TYPE (type));
+  switch (TREE_CODE (type))
+    {
+    default:
+      /* We should never meet a type here that is indescribable in
+	 terms of other types.  */
+      gcc_unreachable ();
+
+    case ARRAY_TYPE:
+      tree_node (TYPE_DOMAIN (type));
+      if (streaming_p ())
+	/* Dependent arrays are constructed with TYPE_DEPENENT_P
+	   already set.  */
+	u (TYPE_DEPENDENT_P (type));
+      break;
+
+    case COMPLEX_TYPE:
+      /* No additional data.  */
+      break;
+
+    case BOOLEAN_TYPE:
+      /* A non-standard boolean type.  */
+      if (streaming_p ())
+	u (TYPE_PRECISION (type));
+      break;
+
+    case INTEGER_TYPE:
+      if (TREE_TYPE (type))
+	{
+	  /* A range type (representing an array domain).  */
+	  tree_node (TYPE_MIN_VALUE (type));
+	  tree_node (TYPE_MAX_VALUE (type));
+	}
+      else
+	{
+	  /* A new integral type (representing a bitfield).  */
+	  if (streaming_p ())
+	    {
+	      unsigned prec = TYPE_PRECISION (type);
+	      bool unsigned_p = TYPE_UNSIGNED (type);
+
+	      u ((prec << 1) | unsigned_p);
+	    }
+	}
+      break;
+
+    case METHOD_TYPE:
+    case FUNCTION_TYPE:
+      {
+	gcc_checking_assert (type_memfn_rqual (type) == REF_QUAL_NONE);
+
+	tree arg_types = TYPE_ARG_TYPES (type);
+	if (TREE_CODE (type) == METHOD_TYPE)
+	  {
+	    tree_node (TREE_TYPE (TREE_VALUE (arg_types)));
+	    arg_types = TREE_CHAIN (arg_types);
+	  }
+	tree_node (arg_types);
+      }
+      break;
+
+    case OFFSET_TYPE:
+      tree_node (TYPE_OFFSET_BASETYPE (type));
+      break;
+
+    case POINTER_TYPE:
+      /* No additional data.  */
+      break;
+
+    case REFERENCE_TYPE:
+      if (streaming_p ())
+	u (TYPE_REF_IS_RVALUE (type));
+      break;
+
+    case DECLTYPE_TYPE:
+    case TYPEOF_TYPE:
+    case UNDERLYING_TYPE:
+      tree_node (TYPE_VALUES_RAW (type));
+      if (TREE_CODE (type) == DECLTYPE_TYPE)
+	/* We stash a whole bunch of things into decltype's
+	   flags.  */
+	if (streaming_p ())
+	  tree_node_bools (type);
+      break;
+
+    case TYPE_ARGUMENT_PACK:
+      /* No additional data.  */
+      break;
+
+    case TYPE_PACK_EXPANSION:
+      if (streaming_p ())
+	u (PACK_EXPANSION_LOCAL_P (type));
+      tree_node (PACK_EXPANSION_PARAMETER_PACKS (type));
+      break;
+
+    case TYPENAME_TYPE:
+      {
+	tree_node (TYPE_CONTEXT (type));
+	tree_node (DECL_NAME (TYPE_NAME (type)));
+	tree_node (TYPENAME_TYPE_FULLNAME (type));
+	if (streaming_p ())
+	  {
+	    enum tag_types tag_type = none_type;
+	    if (TYPENAME_IS_ENUM_P (type))
+	      tag_type = enum_type;
+	    else if (TYPENAME_IS_CLASS_P (type))
+	      tag_type = class_type;
+	    u (int (tag_type));
+	  }
+	}
+      break;
+
+    case UNBOUND_CLASS_TEMPLATE:
+      {
+	tree decl = TYPE_NAME (type);
+	tree_node (DECL_CONTEXT (decl));
+	tree_node (DECL_NAME (decl));
+	tree_node (DECL_TEMPLATE_PARMS (decl));
+      }
+      break;
+
+    case VECTOR_TYPE:
+      if (streaming_p ())
+	{
+	  poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (type);
+	  /* to_constant asserts that only coeff[0] is of interest.  */
+	  wu (static_cast<unsigned HOST_WIDE_INT> (nunits.to_constant ()));
+	}
+      break;
+    }
+
+  /* We may have met the type during emitting the above.  */
+  if (ref_node (type) != WK_none)
+    {
+      int tag = insert (type);
+      if (streaming_p ())
+	{
+	  i (0);
+	  dump (dumper::TREE)
+	    && dump ("Wrote:%d derived type %C", tag, TREE_CODE (type));
+	}
+    }
+
+  return;
+}
+
+/* T is (mostly*) a non-mergeable node that must be written by value.
+   The mergeable case is a BINFO, which are as-if DECLSs.   */
+
+void
+trees_out::tree_value (tree t)
+{
+  /* We should never be writing a type by value.  tree_type should
+     have streamed it, or we're going via its TYPE_DECL.  */
+  gcc_checking_assert (!TYPE_P (t));
+
+  if (DECL_P (t))
+    /* No template, type, var or function, except anonymous
+       non-context vars.  */
+    gcc_checking_assert ((TREE_CODE (t) != TEMPLATE_DECL
+			  && TREE_CODE (t) != TYPE_DECL
+			  && (TREE_CODE (t) != VAR_DECL
+			      || (!DECL_NAME (t) && !DECL_CONTEXT (t)))
+			  && TREE_CODE (t) != FUNCTION_DECL));
+
+  if (streaming_p ())
+    {
+      /* A new node -> tt_node.  */
+      tree_val_count++;
+      i (tt_node);
+      start (t);
+      tree_node_bools (t);
+    }
+
+  if  (TREE_CODE (t) == TREE_BINFO)
+    /* Binfos are decl-like and need merging information.  */
+    binfo_mergeable (t);
+
+  int tag = insert (t, WK_value);
+  if (streaming_p ())
+    dump (dumper::TREE)
+      && dump ("Writing tree:%d %C:%N", tag, TREE_CODE (t), t);
+
+  tree_node_vals (t);
+
+  if (streaming_p ())
+    dump (dumper::TREE) && dump ("Written tree:%d %C:%N", tag, TREE_CODE (t), t);
+}
+
+tree
+trees_in::tree_value ()
+{
+  tree t = start ();
+  if (!t || !tree_node_bools (t))
+    return NULL_TREE;
+
+  tree existing = t;
+  if (TREE_CODE (t) == TREE_BINFO)
+    {
+      tree type;
+      unsigned ix = binfo_mergeable (&type);
+      if (TYPE_BINFO (type))
+	{
+	  /* We already have a definition, this must be a duplicate.  */
+	  dump (dumper::MERGE)
+	    && dump ("Deduping binfo %N[%u]", type, ix);
+	  existing = TYPE_BINFO (type);
+	  while (existing && ix)
+	    existing = TREE_CHAIN (existing);
+	  if (existing)
+	    register_duplicate (t, existing);
+	  else
+	    /* Error, mismatch -- diagnose in read_class_def's
+	       checking.  */
+	    existing = t;
+	}
+    }
+
+  /* Insert into map.  */
+  int tag = insert (existing);
+  dump (dumper::TREE)
+    && dump ("Reading tree:%d %C", tag, TREE_CODE (t));
+
+  if (!tree_node_vals (t))
+    {
+      back_refs[~tag] = NULL_TREE;
+      set_overrun ();
+      /* Bail.  */
+      return NULL_TREE;
+    }
+
+  dump (dumper::TREE) && dump ("Read tree:%d %C:%N", tag, TREE_CODE (t), t);
+
+  if (TREE_CODE (existing) == INTEGER_CST && !TREE_OVERFLOW (existing))
+    {
+      existing = cache_integer_cst (t, true);
+      back_refs[~tag] = existing;
+    }
+
+  return existing;
+}
+
+/* Stream out tree node T.  We automatically create local back
+   references, which is essentially a single pass lisp
+   self-referential structure pretty-printer.  */
+
+void
+trees_out::tree_node (tree t)
+{
+  dump.indent ();
+  walk_kind ref = ref_node (t);
+  if (ref == WK_none)
+    goto done;
+
+  if (ref != WK_normal)
+    goto skip_normal;
+
+  if (TREE_CODE (t) == IDENTIFIER_NODE)
+    {
+      /* An identifier node -> tt_id, tt_conv_id, tt_anon_id, tt_lambda_id.  */
+      int code = tt_id;
+      if (IDENTIFIER_ANON_P (t))
+	code = IDENTIFIER_LAMBDA_P (t) ? tt_lambda_id : tt_anon_id;
+      else if (IDENTIFIER_CONV_OP_P (t))
+	code = tt_conv_id;
+
+      if (streaming_p ())
+	i (code);
+
+      if (code == tt_conv_id)
+	{
+	  tree type = TREE_TYPE (t);
+	  gcc_checking_assert (type || t == conv_op_identifier);
+	  tree_node (type);
+	}
+      else if (code == tt_id && streaming_p ())
+	str (IDENTIFIER_POINTER (t), IDENTIFIER_LENGTH (t));
+
+      int tag = insert (t);
+      if (streaming_p ())
+	{
+	  /* We know the ordering of the 4 id tags.  */
+	  static const char *const kinds[] = 
+	    {"", "conv_op ", "anon ", "lambda "};
+	  dump (dumper::TREE)
+	    && dump ("Written:%d %sidentifier:%N", tag,
+		     kinds[code - tt_id],
+		     code == tt_conv_id ? TREE_TYPE (t) : t);
+	}
+      goto done;
+    }
+
+  if (TREE_CODE (t) == TREE_BINFO)
+    {
+      /* A BINFO -> tt_binfo.
+	 We must do this by reference.  We stream the binfo tree
+	 itself when streaming its owning RECORD_TYPE.  That we got
+	 here means the dominating type is not in this SCC.  */
+      if (streaming_p ())
+	i (tt_binfo);
+      binfo_mergeable (t);
+      gcc_checking_assert (!TREE_VISITED (t));
+      int tag = insert (t);
+      if (streaming_p ())
+	dump (dumper::TREE) && dump ("Inserting binfo:%d %N", tag, t);
+      goto done;
+    }
+
+  if (TREE_CODE (t) == INTEGER_CST
+      && !TREE_OVERFLOW (t)
+      && TREE_CODE (TREE_TYPE (t)) == ENUMERAL_TYPE)
+    {
+      /* An integral constant of enumeral type.  See if it matches one
+	 of the enumeration values.  */
+      for (tree values = TYPE_VALUES (TREE_TYPE (t));
+	   values; values = TREE_CHAIN (values))
+	{
+	  tree decl = TREE_VALUE (values);
+	  if (tree_int_cst_equal (DECL_INITIAL (decl), t))
+	    {
+	      if (streaming_p ())
+		u (tt_enum_value);
+	      tree_node (decl);
+	      dump (dumper::TREE) && dump ("Written enum value %N", decl);
+	      goto done;
+	    }
+	}
+      /* It didn't match.  We'll write it a an explicit INTEGER_CST
+	 node.  */
+    }
+
+  if (TYPE_P (t))
+    {
+      type_node (t);
+      goto done;
+    }
+
+  if (DECL_P (t))
+    {
+      if (DECL_TEMPLATE_PARM_P (t))
+	{
+	  tpl_parm_value (t);
+	  goto done;
+	}
+
+      if (!DECL_CONTEXT (t))
+	{
+	  /* There are a few cases of decls with no context.  We'll write
+	     these by value, but first assert they are cases we expect.  */
+	  gcc_checking_assert (ref == WK_normal);
+	  switch (TREE_CODE (t))
+	    {
+	    default: gcc_unreachable ();
+
+	    case LABEL_DECL:
+	      /* CASE_LABEL_EXPRs contain uncontexted LABEL_DECLs.  */
+	      gcc_checking_assert (!DECL_NAME (t));
+	      break;
+
+	    case VAR_DECL:
+	      /* AGGR_INIT_EXPRs cons up anonymous uncontexted VAR_DECLs.  */
+	      gcc_checking_assert (!DECL_NAME (t)
+				   && DECL_ARTIFICIAL (t));
+	      break;
+
+	    case PARM_DECL:
+	      /* REQUIRES_EXPRs have a tree list of uncontexted
+		 PARM_DECLS.  It'd be nice if they had a
+		 distinguishing flag to double check.  */
+	      break;
+	    }
+	  goto by_value;
+	}
+    }
+
+ skip_normal:
+  if (DECL_P (t) && !decl_node (t, ref))
+    goto done;
+
+  /* Otherwise by value */
+ by_value:
+  tree_value (t);
+
+ done:
+  /* And, breath out.  */
+  dump.outdent ();
+}
+
+/* Stream in a tree node.  */
+
+tree
+trees_in::tree_node (bool is_use)
+{
+  if (get_overrun ())
+    return NULL_TREE;
+
+  dump.indent ();
+  int tag = i ();
+  tree res = NULL_TREE;
+  switch (tag)
+    {
+    default:
+      /* backref, pull it out of the map.  */
+      res = back_ref (tag);
+      break;
+
+    case tt_null:
+      /* NULL_TREE.  */
+      break;
+
+    case tt_fixed:
+      /* A fixed ref, find it in the fixed_ref array.   */
+      {
+	unsigned fix = u ();
+	if (fix < (*fixed_trees).length ())
+	  {
+	    res = (*fixed_trees)[fix];
+	    dump (dumper::TREE) && dump ("Read fixed:%u %C:%N%S", fix,
+					 TREE_CODE (res), res, res);
+	  }
+
+	if (!res)
+	  set_overrun ();
+      }
+      break;
+
+    case tt_parm:
+      {
+	tree fn = tree_node ();
+	if (fn && TREE_CODE (fn) == FUNCTION_DECL)
+	  res = tree_node ();
+	if (res)
+	  dump (dumper::TREE)
+	    && dump ("Read %s reference %N",
+		     TREE_CODE (res) == PARM_DECL ? "parameter" : "result",
+		     res);
+      }
+      break;
+
+    case tt_node:
+      /* A new node.  Stream it in.  */
+      res = tree_value ();
+      break;
+
+    case tt_decl:
+      /* A new decl.  Stream it in.  */
+      res = decl_value ();
+      break;
+
+    case tt_tpl_parm:
+      /* A template parameter.  Stream it in.  */
+      res = tpl_parm_value ();
+      break;
+
+    case tt_id:
+      /* An identifier node.  */
+      {
+	size_t l;
+	const char *chars = str (&l);
+	res = get_identifier_with_length (chars, l);
+	int tag = insert (res);
+	dump (dumper::TREE)
+	  && dump ("Read identifier:%d %N", tag, res);
+      }
+      break;
+
+    case tt_conv_id:
+      /* A conversion operator.  Get the type and recreate the
+	 identifier.  */
+      {
+	tree type = tree_node ();
+	if (!get_overrun ())
+	  {
+	    res = type ? make_conv_op_name (type) : conv_op_identifier;
+	    int tag = insert (res);
+	    dump (dumper::TREE)
+	      && dump ("Created conv_op:%d %S for %N", tag, res, type);
+	  }
+      }
+      break;
+
+    case tt_anon_id:
+    case tt_lambda_id:
+      /* An anonymous or lambda id.  */
+      {
+	res = make_anon_name ();
+	if (tag == tt_lambda_id)
+	  IDENTIFIER_LAMBDA_P (res) = true;
+	int tag = insert (res);
+	dump (dumper::TREE)
+	  && dump ("Read %s identifier:%d %N",
+		   IDENTIFIER_LAMBDA_P (res) ? "lambda" : "anon", tag, res);
+      }
+      break;
+
+    case tt_typedef_type:
+      res = tree_node ();
+      if (res)
+	{
+	  dump (dumper::TREE)
+	    && dump ("Read %stypedef %C:%N",
+		     DECL_IMPLICIT_TYPEDEF_P (res) ? "implicit " : "",
+		     TREE_CODE (res), res);
+	  res = TREE_TYPE (res);
+	}
+      break;
+
+    case tt_derived_type:
+      /* A type derived from some other type.  */
+      {
+	enum tree_code code = tree_code (u ());
+	res = tree_node ();
+
+	switch (code)
+	  {
+	  default:
+	    set_overrun ();
+	    break;
+
+	  case ARRAY_TYPE:
+	    {
+	      tree domain = tree_node ();
+	      int dep = u ();
+	      if (!get_overrun ())
+		res = build_cplus_array_type (res, domain, dep);
+	    }
+	    break;
+
+	  case COMPLEX_TYPE:
+	    if (!get_overrun ())
+	      res = build_complex_type (res);
+	    break;
+
+	  case BOOLEAN_TYPE:
+	    {
+	      unsigned precision = u ();
+	      if (!get_overrun ())
+		res = build_nonstandard_boolean_type (precision);
+	    }
+	    break;
+
+	  case INTEGER_TYPE:
+	    if (res)
+	      {
+		/* A range type (representing an array domain).  */
+		tree min = tree_node ();
+		tree max = tree_node ();
+
+		if (!get_overrun ())
+		  res = build_range_type (res, min, max);
+	      }
+	    else
+	      {
+		/* A new integral type (representing a bitfield).  */
+		unsigned enc = u ();
+		if (!get_overrun ())
+		  res = build_nonstandard_integer_type (enc >> 1, enc & 1);
+	      }
+	    break;
+
+	  case FUNCTION_TYPE:
+	  case METHOD_TYPE:
+	    {
+	      tree klass =  code == METHOD_TYPE ? tree_node () : NULL_TREE;
+	      tree args = tree_node ();
+	      if (!get_overrun ())
+		{
+		  if (klass)
+		    res = build_method_type_directly (klass, res, args);
+		  else
+		    res = build_function_type (res, args);
+		}
+	    }
+	    break;
+
+	  case OFFSET_TYPE:
+	    {
+	      tree base = tree_node ();
+	      if (!get_overrun ())
+		res = build_offset_type (base, res);
+	    }
+	    break;
+
+	  case POINTER_TYPE:
+	    if (!get_overrun ())
+	      res = build_pointer_type (res);
+	    break;
+
+	  case REFERENCE_TYPE:
+	    {
+	      bool rval = bool (u ());
+	      if (!get_overrun ())
+		res = cp_build_reference_type (res, rval);
+	    }
+	    break;
+
+	  case DECLTYPE_TYPE:
+	  case TYPEOF_TYPE:
+	  case UNDERLYING_TYPE:
+	    {
+	      tree expr = tree_node ();
+	      if (!get_overrun ())
+		{
+		  res = cxx_make_type (code);
+		  TYPE_VALUES_RAW (res) = expr;
+		  if (code == DECLTYPE_TYPE)
+		    tree_node_bools (res);
+		  SET_TYPE_STRUCTURAL_EQUALITY (res);
+		}
+	    }
+	    break;
+
+	  case TYPE_ARGUMENT_PACK:
+	    if (!get_overrun ())
+	      {
+		tree pack = cxx_make_type (TYPE_ARGUMENT_PACK);
+		SET_ARGUMENT_PACK_ARGS (pack, res);
+		res = pack;
+	      }
+	    break;
+
+	  case TYPE_PACK_EXPANSION:
+	    {
+	      bool local = u ();
+	      tree param_packs = tree_node ();
+	      if (!get_overrun ())
+		{
+		  tree expn = cxx_make_type (TYPE_PACK_EXPANSION);
+		  SET_TYPE_STRUCTURAL_EQUALITY (expn);
+		  SET_PACK_EXPANSION_PATTERN (expn, res);
+		  PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs;
+		  PACK_EXPANSION_LOCAL_P (expn) = local;
+		  res = expn;
+		}
+	    }
+	    break;
+
+	  case TYPENAME_TYPE:
+	    {
+	      tree ctx = tree_node ();
+	      tree name = tree_node ();
+	      tree fullname = tree_node ();
+	      enum tag_types tag_type = tag_types (u ());
+
+	      if (!get_overrun ())
+		res = build_typename_type (ctx, name, fullname, tag_type);
+	    }
+	    break;
+
+	  case UNBOUND_CLASS_TEMPLATE:
+	    {
+	      tree ctx = tree_node ();
+	      tree name = tree_node ();
+	      tree parms = tree_node ();
+
+	      if (!get_overrun ())
+		res = make_unbound_class_template_raw (ctx, name, parms);
+	    }
+	    break;
+
+	  case VECTOR_TYPE:
+	    {
+	      unsigned HOST_WIDE_INT nunits = wu ();
+	      if (!get_overrun ())
+		res = build_vector_type (res, static_cast<poly_int64> (nunits));
+	    }
+	    break;
+	  }
+
+	int tag = i ();
+	if (!tag)
+	  {
+	    tag = insert (res);
+	    if (res)
+	      dump (dumper::TREE)
+		&& dump ("Created:%d derived type %C", tag, code);
+	  }
+	else
+	  res = back_ref (tag);
+      }
+      break;
+
+    case tt_variant_type:
+      /* Variant of some type.  */
+      {
+	res = tree_node ();
+	int flags = i ();
+	if (get_overrun ())
+	  ;
+	else if (flags < 0)
+	  /* No change.  */;
+	else if (TREE_CODE (res) == FUNCTION_TYPE
+		 || TREE_CODE (res) == METHOD_TYPE)
+	  {
+	    cp_ref_qualifier rqual = cp_ref_qualifier (flags & 3);
+	    bool late = (flags >> 2) & 1;
+	    cp_cv_quals quals = cp_cv_quals (flags >> 3);
+
+	    tree raises = tree_node ();
+	    if (raises == error_mark_node)
+	      raises = TYPE_RAISES_EXCEPTIONS (res);
+
+	    res = build_cp_fntype_variant (res, rqual, raises, late);
+	    if (TREE_CODE (res) == FUNCTION_TYPE)
+	      res = apply_memfn_quals (res, quals, rqual);
+	  }
+	else
+	  {
+	    res = build_aligned_type (res, 1u << flags);
+	    TYPE_USER_ALIGN (res) = true;
+	  }
+
+	if (tree attribs = tree_node ())
+	  res = cp_build_type_attribute_variant (res, attribs);
+
+	int quals = i ();
+	if (quals >= 0 && !get_overrun ())
+	  res = cp_build_qualified_type (res, quals);
+
+	int tag = i ();
+	if (!tag)
+	  {
+	    tag = insert (res);
+	    if (res)
+	      dump (dumper::TREE)
+		&& dump ("Created:%d variant type %C", tag, TREE_CODE (res));
+	  }
+	else
+	  res = back_ref (tag);
+      }
+      break;
+
+    case tt_tinfo_var:
+    case tt_tinfo_typedef:
+      /* A tinfo var or typedef.  */
+      {
+	bool is_var = tag == tt_tinfo_var;
+	unsigned ix = u ();
+	tree type = NULL_TREE;
+
+	if (is_var)
+	  {
+	    tree name = tree_node ();
+	    type = tree_node ();
+
+	    if (!get_overrun ())
+	      res = get_tinfo_decl_direct (type, name, int (ix));
+	  }
+	else
+	  {
+	    if (!get_overrun ())
+	      {
+		type = get_pseudo_tinfo_type (ix);
+		res = TYPE_NAME (type);
+	      }
+	  }
+	if (res)
+	  {
+	    int tag = insert (res);
+	    dump (dumper::TREE)
+	      && dump ("Created tinfo_%s:%d %S:%u for %N",
+		       is_var ? "var" : "decl", tag, res, ix, type);
+	    if (!is_var)
+	      {
+		tag = insert (type);
+		dump (dumper::TREE)
+		  && dump ("Created tinfo_type:%d %u %N", tag, ix, type);
+	      }
+	  }
+      }
+      break;
+
+    case tt_ptrmem_type:
+      /* A pointer to member function.  */
+      {
+	tree type = tree_node ();
+	if (type && TREE_CODE (type) == POINTER_TYPE
+	    && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE)
+	  {
+	    res = build_ptrmemfunc_type (type);
+	    int tag = insert (res);
+	    dump (dumper::TREE) && dump ("Created:%d ptrmem type", tag);
+	  }
+	else
+	  set_overrun ();
+      }
+      break;
+
+    case tt_enum_value:
+      /* An enum const value.  */
+      {
+	if (tree decl = tree_node ())
+	  {
+	    dump (dumper::TREE) && dump ("Read enum value %N", decl);
+	    res = DECL_INITIAL (decl);
+	  }
+
+	if (!res)
+	  set_overrun ();
+      }
+      break;
+
+    case tt_enum_decl:
+      /* An enum decl.  */
+      {
+	tree ctx = tree_node ();
+	tree name = tree_node ();
+
+	if (!get_overrun ()
+	    && TREE_CODE (ctx) == ENUMERAL_TYPE)
+	  res = find_enum_member (ctx, name);
+
+	if (!res)
+	  set_overrun ();
+	else
+	  {
+	    int tag = insert (res);
+	    dump (dumper::TREE)
+	      && dump ("Read enum decl:%d %C:%N", tag, TREE_CODE (res), res);
+	  }
+      }
+      break;
+
+    case tt_data_member:
+      /* A data member.  */
+      {
+	tree ctx = tree_node ();
+	tree name = tree_node ();
+
+	if (!get_overrun ()
+	    && RECORD_OR_UNION_TYPE_P (ctx))
+	  {
+	    if (name)
+	      res = lookup_class_binding (ctx, name);
+	    else
+	      res = lookup_field_ident (ctx, u ());
+
+	    if (!res
+		|| TREE_CODE (res) != FIELD_DECL
+		|| DECL_CONTEXT (res) != ctx)
+	      res = NULL_TREE;
+	  }
+
+	if (!res)
+	  set_overrun ();
+	else
+	  {
+	    int tag = insert (res);
+	    dump (dumper::TREE)
+	      && dump ("Read member:%d %C:%N", tag, TREE_CODE (res), res);
+	  }
+      }
+      break;
+
+    case tt_binfo:
+      /* A BINFO.  Walk the tree of the dominating type.  */
+      {
+	tree type;
+	unsigned ix = binfo_mergeable (&type);
+	if (type)
+	  {
+	    res = TYPE_BINFO (type);
+	    for (; ix && res; res = TREE_CHAIN (res))
+	      ix--;
+	    if (!res)
+	      set_overrun ();
+	  }
+
+	if (get_overrun ())
+	  break;
+
+	/* Insert binfo into backreferences.  */
+	tag = insert (res);
+	dump (dumper::TREE) && dump ("Read binfo:%d %N", tag, res);
+      }
+      break;
+
+    case tt_vtable:
+      {
+	unsigned ix = u ();
+	tree ctx = tree_node ();
+	dump (dumper::TREE) && dump ("Reading vtable %N[%u]", ctx, ix);
+	if (TREE_CODE (ctx) == RECORD_TYPE && TYPE_LANG_SPECIFIC (ctx))
+	  for (res = CLASSTYPE_VTABLES (ctx); res; res = DECL_CHAIN (res))
+	    if (!ix--)
+	      break;
+	if (!res)
+	  set_overrun ();
+      }
+      break;
+
+    case tt_thunk:
+      {
+	int fixed = i ();
+	tree target = tree_node ();
+	tree virt = tree_node ();
+
+	for (tree thunk = DECL_THUNKS (target);
+	     thunk; thunk = DECL_CHAIN (thunk))
+	  if (THUNK_FIXED_OFFSET (thunk) == fixed
+	      && !THUNK_VIRTUAL_OFFSET (thunk) == !virt
+	      && (!virt
+		  || tree_int_cst_equal (virt, THUNK_VIRTUAL_OFFSET (thunk))))
+	    {
+	      res = thunk;
+	      break;
+	    }
+
+	int tag = insert (res);
+	if (res)
+	  dump (dumper::TREE)
+	    && dump ("Read:%d thunk %N to %N", tag, DECL_NAME (res), target);
+	else
+	  set_overrun ();
+      }
+      break;
+
+    case tt_clone_ref:
+      {
+	tree target = tree_node ();
+	tree name = tree_node ();
+
+	if (DECL_P (target) && DECL_MAYBE_IN_CHARGE_CDTOR_P (target))
+	  {
+	    tree clone;
+	    FOR_EVERY_CLONE (clone, target)
+	      if (DECL_NAME (clone) == name)
+		{
+		  res = clone;
+		  break;
+		}
+	  }
+
+	if (!res)
+	  set_overrun ();
+	int tag = insert (res);
+	if (res)
+	  dump (dumper::TREE)
+	    && dump ("Read:%d clone %N of %N", tag, DECL_NAME (res), target);
+	else
+	  set_overrun ();
+       }
+      break;
+
+    case tt_entity:
+      /* Index into the entity table.  Perhaps not loaded yet!  */
+      {
+	unsigned origin = state->slurp->remap_module (u ());
+	unsigned ident = u ();
+	module_state *from = (*modules)[origin];
+
+	if (!origin || ident >= from->entity_num)
+	  set_overrun ();
+	if (!get_overrun ())
+	  {
+	    mc_slot *slot = &(*entity_ary)[from->entity_lwm + ident];
+	    if (slot->is_lazy ())
+	      if (!from->lazy_load (ident, slot))
+		set_overrun ();
+	    res = *slot;
+	  }
+
+	if (res)
+	  {
+	    const char *kind = (origin != state->mod ? "Imported" : "Named");
+	    int tag = insert (res);
+	    dump (dumper::TREE)
+	      && dump ("%s:%d %C:%N@%M", kind, tag, TREE_CODE (res),
+		       res, (*modules)[origin]);
+
+	    if (!add_indirects (res))
+	      {
+		set_overrun ();
+		res = NULL_TREE;
+	      }
+	  }
+      }
+      break;
+
+    case tt_template:
+      /* A template.  */
+      if (tree tpl = tree_node ())
+	{
+	  res = DECL_TEMPLATE_RESULT (tpl);
+	  dump (dumper::TREE)
+	    && dump ("Read template %C:%N", TREE_CODE (res), res);
+	}
+      break;
+    }
+
+  if (is_use && !unused && res && DECL_P (res) && !TREE_USED (res))
+    {
+      /* Mark decl used as mark_used does -- we cannot call
+	 mark_used in the middle of streaming, we only need a subset
+	 of its functionality.   */
+      TREE_USED (res) = true;
+
+      /* And for structured bindings also the underlying decl.  */
+      if (DECL_DECOMPOSITION_P (res) && DECL_DECOMP_BASE (res))
+	TREE_USED (DECL_DECOMP_BASE (res)) = true;
+
+      if (DECL_CLONED_FUNCTION_P (res))
+	TREE_USED (DECL_CLONED_FUNCTION (res)) = true;
+    }
+
+  dump.outdent ();
+  return res;
+}
+
+void
+trees_out::tpl_parms (tree parms, unsigned &tpl_levels)
+{
+  if (!parms)
+    return;
+
+  if (TREE_VISITED (parms))
+    {
+      ref_node (parms);
+      return;
+    }
+
+  tpl_parms (TREE_CHAIN (parms), tpl_levels);
+
+  tree vec = TREE_VALUE (parms);
+  unsigned len = TREE_VEC_LENGTH (vec);
+  /* Depth.  */
+  int tag = insert (parms);
+  if (streaming_p ())
+    {
+      i (len + 1);
+      dump (dumper::TREE)
+	&& dump ("Writing template parms:%d level:%N length:%d",
+		 tag, TREE_PURPOSE (parms), len);
+    }
+  tree_node (TREE_PURPOSE (parms));
+
+  for (unsigned ix = 0; ix != len; ix++)
+    {
+      tree parm = TREE_VEC_ELT (vec, ix);
+      tree decl = TREE_VALUE (parm);
+
+      gcc_checking_assert (DECL_TEMPLATE_PARM_P (decl));
+      if (CHECKING_P)
+	switch (TREE_CODE (decl))
+	  {
+	  default: gcc_unreachable ();
+
+	  case TEMPLATE_DECL:
+	    gcc_assert ((TREE_CODE (TREE_TYPE (decl)) == TEMPLATE_TEMPLATE_PARM)
+			&& (TREE_CODE (DECL_TEMPLATE_RESULT (decl)) == TYPE_DECL)
+			&& (TYPE_NAME (TREE_TYPE (decl)) == decl));
+	    break;
+
+	  case TYPE_DECL:
+	    gcc_assert ((TREE_CODE (TREE_TYPE (decl)) == TEMPLATE_TYPE_PARM)
+			&& (TYPE_NAME (TREE_TYPE (decl)) == decl));
+	    break;
+
+	  case PARM_DECL:
+	    gcc_assert ((TREE_CODE (DECL_INITIAL (decl)) == TEMPLATE_PARM_INDEX)
+			&& (TREE_CODE (TEMPLATE_PARM_DECL (DECL_INITIAL (decl)))
+			    == CONST_DECL)
+			&& (DECL_TEMPLATE_PARM_P
+			    (TEMPLATE_PARM_DECL (DECL_INITIAL (decl)))));
+	    break;
+	  }
+
+      tree_node (decl);
+      tree_node (TEMPLATE_PARM_CONSTRAINTS (parm));
+    }
+
+  tpl_levels++;
+}
+
+tree
+trees_in::tpl_parms (unsigned &tpl_levels)
+{
+  tree parms = NULL_TREE;
+
+  while (int len = i ())
+    {
+      if (len < 0)
+	{
+	  parms = back_ref (len);
+	  continue;
+	}
+
+      len -= 1;
+      parms = tree_cons (NULL_TREE, NULL_TREE, parms);
+      int tag = insert (parms);
+      TREE_PURPOSE (parms) = tree_node ();
+
+      dump (dumper::TREE)
+	&& dump ("Reading template parms:%d level:%N length:%d",
+		 tag, TREE_PURPOSE (parms), len);
+
+      tree vec = make_tree_vec (len);
+      for (int ix = 0; ix != len; ix++)
+	{
+	  tree decl = tree_node ();
+	  if (!decl)
+	    return NULL_TREE;
+
+	  tree parm = build_tree_list (NULL, decl);
+	  TEMPLATE_PARM_CONSTRAINTS (parm) = tree_node ();
+
+	  TREE_VEC_ELT (vec, ix) = parm;
+	}
+
+      TREE_VALUE (parms) = vec;
+      tpl_levels++;
+    }
+
+  return parms;
+}
+
+void
+trees_out::tpl_parms_fini (tree tmpl, unsigned tpl_levels)
+{
+  for (tree parms = DECL_TEMPLATE_PARMS (tmpl);
+       tpl_levels--; parms = TREE_CHAIN (parms))
+    {
+      tree vec = TREE_VALUE (parms);
+
+      tree_node (TREE_TYPE (vec));
+      tree dflt = error_mark_node;
+      for (unsigned ix = TREE_VEC_LENGTH (vec); ix--;)
+	{
+	  tree parm = TREE_VEC_ELT (vec, ix);
+	  if (dflt)
+	    {
+	      dflt = TREE_PURPOSE (parm);
+	      tree_node (dflt);
+	    }
+
+	  if (streaming_p ())
+	    {
+	      tree decl = TREE_VALUE (parm);
+	      if (TREE_CODE (decl) == TEMPLATE_DECL)
+		{
+		  tree ctx = DECL_CONTEXT (decl);
+		  tree inner = DECL_TEMPLATE_RESULT (decl);
+		  tree tpi = (TREE_CODE (inner) == TYPE_DECL
+			      ? TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (decl))
+			      : DECL_INITIAL (inner));
+		  bool original = (TEMPLATE_PARM_LEVEL (tpi)
+				   == TEMPLATE_PARM_ORIG_LEVEL (tpi));
+		  /* Original template template parms have a context
+		     of their owning template.  Reduced ones do not.  */
+		  gcc_checking_assert (original ? ctx == tmpl : !ctx);
+		}
+	    }
+	}
+    }
+}
+
+bool
+trees_in::tpl_parms_fini (tree tmpl, unsigned tpl_levels)
+{
+  for (tree parms = DECL_TEMPLATE_PARMS (tmpl);
+       tpl_levels--; parms = TREE_CHAIN (parms))
+    {
+      tree vec = TREE_VALUE (parms);
+      tree dflt = error_mark_node;
+
+      TREE_TYPE (vec) = tree_node ();
+      for (unsigned ix = TREE_VEC_LENGTH (vec); ix--;)
+	{
+	  tree parm = TREE_VEC_ELT (vec, ix);
+	  if (dflt)
+	    {
+	      dflt = tree_node ();
+	      if (get_overrun ())
+		return false;
+	      TREE_PURPOSE (parm) = dflt;
+	    }
+
+	  tree decl = TREE_VALUE (parm);
+	  if (TREE_CODE (decl) == TEMPLATE_DECL)
+	    {
+	      tree inner = DECL_TEMPLATE_RESULT (decl);
+	      tree tpi = (TREE_CODE (inner) == TYPE_DECL
+			  ? TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (decl))
+			  : DECL_INITIAL (inner));
+	      bool original = (TEMPLATE_PARM_LEVEL (tpi)
+			       == TEMPLATE_PARM_ORIG_LEVEL (tpi));
+	      /* Original template template parms have a context
+		 of their owning template.  Reduced ones do not.  */
+	      if (original)
+		DECL_CONTEXT (decl) = tmpl;
+	    }
+	}
+    }
+  return true;
+}
+
+/* PARMS is a LIST, one node per level.
+   TREE_VALUE is a TREE_VEC of parm info for that level.
+   each ELT is a TREE_LIST
+   TREE_VALUE is PARM_DECL, TYPE_DECL or TEMPLATE_DECL
+   TREE_PURPOSE is the default value.  */
+
+void
+trees_out::tpl_header (tree tpl, unsigned *tpl_levels)
+{
+  tree parms = DECL_TEMPLATE_PARMS (tpl);
+  tpl_parms (parms, *tpl_levels);
+
+  /* Mark end.  */
+  if (streaming_p ())
+    u (0);
+
+  if (*tpl_levels)
+    tree_node (TEMPLATE_PARMS_CONSTRAINTS (parms));
+}
+
+bool
+trees_in::tpl_header (tree tpl, unsigned *tpl_levels)
+{
+  tree parms = tpl_parms (*tpl_levels);
+  if (!parms)
+    return false;
+
+  DECL_TEMPLATE_PARMS (tpl) = parms;
+
+  if (*tpl_levels)
+    TEMPLATE_PARMS_CONSTRAINTS (parms) = tree_node ();
+
+  return true;
+}
+
+/* Stream skeleton parm nodes, with their flags, type & parm indices.
+   All the parms will have consecutive tags.  */
+
+void
+trees_out::fn_parms_init (tree fn)
+{
+  /* First init them.  */
+  int base_tag = ref_num - 1;
+  int ix = 0;
+  for (tree parm = DECL_ARGUMENTS (fn);
+       parm; parm = DECL_CHAIN (parm), ix++)
+    {
+      if (streaming_p ())
+	{
+	  start (parm);
+	  tree_node_bools (parm);
+	}
+      int tag = insert (parm);
+      gcc_checking_assert (base_tag - ix == tag);
+    }
+  /* Mark the end.  */
+  if (streaming_p ())
+    u (0);
+
+  /* Now stream their contents.  */
+  ix = 0;
+  for (tree parm = DECL_ARGUMENTS (fn);
+       parm; parm = DECL_CHAIN (parm), ix++)
+    {
+      if (streaming_p ())
+	dump (dumper::TREE)
+	  && dump ("Writing parm:%d %u (%N) of %N",
+		   base_tag - ix, ix, parm, fn);
+      tree_node_vals (parm);
+    }
+}
+
+/* Build skeleton parm nodes, read their flags, type & parm indices.  */
+
+int
+trees_in::fn_parms_init (tree fn)
+{
+  int base_tag = ~(int)back_refs.length ();
+
+  tree *parm_ptr = &DECL_ARGUMENTS (fn);
+  int ix = 0;
+  for (; int code = u (); ix++)
+    {
+      tree parm = start (code);
+      if (!tree_node_bools (parm))
+	return 0;
+
+      int tag = insert (parm);
+      gcc_checking_assert (base_tag - ix == tag);
+      *parm_ptr = parm;
+      parm_ptr = &DECL_CHAIN (parm);
+    }
+
+  ix = 0;
+  for (tree parm = DECL_ARGUMENTS (fn);
+       parm; parm = DECL_CHAIN (parm), ix++)
+    {
+      dump (dumper::TREE)
+	&& dump ("Reading parm:%d %u (%N) of %N",
+		 base_tag - ix, ix, parm, fn);
+      if (!tree_node_vals (parm))
+	return 0;
+    }
+
+  return base_tag;
+}
+
+/* Read the remaining parm node data.  Replace with existing (if
+   non-null) in the map.  */
+
+void
+trees_in::fn_parms_fini (int tag, tree fn, tree existing, bool is_defn)
+{
+  tree existing_parm = existing ? DECL_ARGUMENTS (existing) : NULL_TREE;
+  tree parms = DECL_ARGUMENTS (fn);
+  unsigned ix = 0;
+  for (tree parm = parms; parm; parm = DECL_CHAIN (parm), ix++)
+    {
+      if (existing_parm)
+	{
+	  if (is_defn && !DECL_SAVED_TREE (existing))
+	    {
+	      /* If we're about to become the definition, set the
+		 names of the parms from us.  */
+	      DECL_NAME (existing_parm) = DECL_NAME (parm);
+	      DECL_SOURCE_LOCATION (existing_parm) = DECL_SOURCE_LOCATION (parm);
+	    }
+
+	  back_refs[~tag] = existing_parm;
+	  existing_parm = DECL_CHAIN (existing_parm);
+	}
+      tag--;
+    }
+}
+
+/* DEP is the depset of some decl we're streaming by value.  Determine
+   the merging behaviour.  */
+
+merge_kind
+trees_out::get_merge_kind (tree decl, depset *dep)
+{
+  if (!dep)
+    {
+      if (VAR_OR_FUNCTION_DECL_P (decl))
+	{
+	  /* Any var or function with template info should have DEP.  */
+	  gcc_checking_assert (!DECL_LANG_SPECIFIC (decl)
+			       || !DECL_TEMPLATE_INFO (decl));
+	  if (DECL_LOCAL_DECL_P (decl))
+	    return MK_unique;
+	}
+
+      /* Either unique, or some member of a class that cannot have an
+	 out-of-class definition.  For instance a FIELD_DECL.  */
+      tree ctx = CP_DECL_CONTEXT (decl);
+      if (TREE_CODE (ctx) == FUNCTION_DECL)
+	{
+	  /* USING_DECLs cannot have DECL_TEMPLATE_INFO -- this isn't
+	     permitting them to have one.   */
+	  gcc_checking_assert (TREE_CODE (decl) == USING_DECL
+			       || !DECL_LANG_SPECIFIC (decl)
+			       || !DECL_TEMPLATE_INFO (decl));
+
+	  return MK_unique;
+	}
+
+      if (TREE_CODE (decl) == TEMPLATE_DECL
+	  && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
+	return MK_local_friend;
+
+      gcc_checking_assert (TYPE_P (ctx));
+      if (TREE_CODE (decl) == USING_DECL)
+	return MK_field;
+
+      if (TREE_CODE (decl) == FIELD_DECL)
+	{
+	  if (DECL_NAME (decl))
+	    {
+	      /* Anonymous FIELD_DECLs have a NULL name.  */
+	      gcc_checking_assert (!IDENTIFIER_ANON_P (DECL_NAME (decl)));
+	      return MK_named;
+	    }
+
+	  if (!DECL_NAME (decl)
+	      && !RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))
+	      && !DECL_BIT_FIELD_REPRESENTATIVE (decl))
+	    {
+	      /* The underlying storage unit for a bitfield.  We do not
+		 need to dedup it, because it's only reachable through
+		 the bitfields it represents.  And those are deduped.  */
+	      // FIXME: Is that assertion correct -- do we ever fish it
+	      // out and put it in an expr?
+	      gcc_checking_assert ((TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+				    ? TREE_CODE (TREE_TYPE (TREE_TYPE (decl)))
+				    : TREE_CODE (TREE_TYPE (decl)))
+				   == INTEGER_TYPE);
+	      return MK_unique;
+	    }
+
+	  return MK_field;
+	}
+
+      if (TREE_CODE (decl) == CONST_DECL)
+	return MK_named;
+
+      if (TREE_CODE (decl) == VAR_DECL
+	  && DECL_VTABLE_OR_VTT_P (decl))
+	return MK_vtable;
+
+      if (DECL_THUNK_P (decl))
+	/* Thunks are unique-enough, because they're only referenced
+	   from the vtable.  And that's either new (so we want the
+	   thunks), or it's a duplicate (so it will be dropped).  */
+	return MK_unique;
+
+      /* There should be no other cases.  */
+      gcc_unreachable ();
+    }
+
+  gcc_checking_assert (TREE_CODE (decl) != FIELD_DECL
+		       && TREE_CODE (decl) != USING_DECL
+		       && TREE_CODE (decl) != CONST_DECL);
+
+  if (is_key_order ())
+    {
+      /* When doing the mergeablilty graph, there's an indirection to
+	 the actual depset.  */
+      gcc_assert (dep->is_special ());
+      dep = dep->deps[0];
+    }
+
+  gcc_checking_assert (decl == dep->get_entity ());
+
+  merge_kind mk = MK_named;
+  switch (dep->get_entity_kind ())
+    {
+    default:
+      gcc_unreachable ();
+
+    case depset::EK_DECL:
+      {
+	if (dep->is_partial ())
+	  {
+	    mk = MK_partial;
+	    break;
+	  }
+	tree ctx = CP_DECL_CONTEXT (decl);
+
+	switch (TREE_CODE (ctx))
+	  {
+	  default:
+	    gcc_unreachable ();
+
+	  case FUNCTION_DECL:
+	    // FIXME: This can occur for (a) voldemorty TYPE_DECLS
+	    // (which are returned from a function), or (b)
+	    // block-scope class definitions in template functions.
+	    // These are as unique as the containing function.  While
+	    // on read-back we can discover if the CTX was a
+	    // duplicate, we don't have a mechanism to get from the
+	    // existing CTX to the existing version of this decl.
+	    gcc_checking_assert
+	      (DECL_IMPLICIT_TYPEDEF_P (STRIP_TEMPLATE (decl)));
+
+	    mk = MK_unique;
+	    break;
+
+	  case RECORD_TYPE:
+	  case UNION_TYPE:
+	    if (DECL_NAME (decl) == as_base_identifier)
+	      mk = MK_as_base;
+	    else if (IDENTIFIER_ANON_P (DECL_NAME (decl)))
+	      mk = MK_field;
+	    break;
+
+	  case NAMESPACE_DECL:
+	    if (DECL_IMPLICIT_TYPEDEF_P (STRIP_TEMPLATE (decl))
+		&& LAMBDA_TYPE_P (TREE_TYPE (decl)))
+	      if (tree scope
+		  = LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR
+					     (TREE_TYPE (decl))))
+		if (TREE_CODE (scope) == VAR_DECL
+		    && DECL_ATTACHED_DECLS_P (scope))
+		  {
+		    mk = MK_attached;
+		    break;
+		  }
+
+	    if (TREE_CODE (decl) == TEMPLATE_DECL
+		&& DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
+	      mk = MK_local_friend;
+	    else if (IDENTIFIER_ANON_P (DECL_NAME (decl)))
+	      {
+		if (DECL_IMPLICIT_TYPEDEF_P (decl)
+		    && UNSCOPED_ENUM_P (TREE_TYPE (decl))
+		    && TYPE_VALUES (TREE_TYPE (decl)))
+		  /* Keyed by first enum value, and underlying type.  */
+		  mk = MK_enum;
+		else
+		  /* No way to merge it, it is an ODR land-mine.  */
+		  mk = MK_unique;
+	      }
+	  }
+      }
+      break;
+
+    case depset::EK_SPECIALIZATION:
+      {
+	gcc_checking_assert (dep->is_special ());
+	spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
+
+	if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+	  /* An block-scope classes of templates are themselves
+	     templates.  */
+	  gcc_checking_assert (DECL_IMPLICIT_TYPEDEF_P (decl));
+
+	if (dep->is_friend_spec ())
+	  mk = MK_friend_spec;
+	else if (dep->is_type_spec ())
+	  mk = dep->is_partial () ? MK_type_partial_spec : MK_type_spec;
+	else
+	  mk = dep->is_alias () ? MK_alias_spec : MK_decl_spec;
+
+	if (TREE_CODE (decl) == TEMPLATE_DECL)
+	  {
+	    tree res = DECL_TEMPLATE_RESULT (decl);
+	    if (!(mk & MK_tmpl_decl_mask))
+	      res = TREE_TYPE (res);
+
+	    if (res == entry->spec)
+	      /* We check we can get back to the template during
+		 streaming.  */
+	      mk = merge_kind (mk | MK_tmpl_tmpl_mask);
+	  }
+	else
+	  gcc_checking_assert (mk != MK_type_partial_spec);
+      }
+      break;
+    }
+
+  return mk;
+}
+
+
+/* The container of DECL -- not necessarily its context!  */
+
+tree
+trees_out::decl_container (tree decl)
+{
+  tree container = NULL_TREE;
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL
+      && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
+    container = DECL_CHAIN (decl);
+  else
+    container = CP_DECL_CONTEXT (decl);
+
+  if (TYPE_P (container))
+    container = TYPE_NAME (container);
+
+  int use_tpl;
+  if (tree template_info = node_template_info (container, use_tpl))
+    if (!use_tpl)
+      container = TI_TEMPLATE (template_info);
+
+  tree_node (container);
+
+  return STRIP_TEMPLATE (container);
+}
+
+tree
+trees_in::decl_container ()
+{
+  tree container = tree_node ();
+
+  return STRIP_TEMPLATE (container);
+}
+
+/* Write out key information about a mergeable DEP.  Does not write
+   the contents of DEP itself.  The context has already been
+   written.  The container has already been streamed.  */
+
+void
+trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
+			  tree container, depset *dep)
+{
+  if (dep && is_key_order ())
+    {
+      gcc_checking_assert (dep->is_special ());
+      dep = dep->deps[0];
+    }
+
+  if (streaming_p ())
+    dump (dumper::MERGE)
+      && dump ("Writing:%d's %s merge key (%s) %C:%N", tag, merge_kind_name[mk],
+	       dep ? dep->entity_kind_name () : "contained",
+	       TREE_CODE (decl), decl);
+
+  /* Now write the locating information. */
+  if (mk & MK_template_mask)
+    {
+      /* Specializations are located via their originating template,
+	 and the set of template args they specialize.  */
+      gcc_checking_assert (dep && dep->is_special ());
+      spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
+
+      tree_node (entry->tmpl);
+      tree_node (entry->args);
+      if (streaming_p ())
+	u (get_mergeable_specialization_flags (entry->tmpl, decl));
+
+      // FIXME: Do variable templates with concepts need constraints
+      // from the specialization?  See spec_hasher::equal
+      if (CHECKING_P)
+	{
+	  /* Make sure we can locate the decl.  */
+	  tree existing = check_mergeable_specialization
+	    (bool (mk & MK_tmpl_decl_mask), entry);
+
+	  gcc_assert (existing);
+	  if (mk & MK_tmpl_decl_mask)
+	    {
+	      if (mk & MK_tmpl_alias_mask)
+		/* It should be in both tables.  */
+		gcc_assert (check_mergeable_specialization (false, entry)
+			    == TREE_TYPE (existing));
+	      else if (mk & MK_tmpl_tmpl_mask)
+		if (tree ti = DECL_TEMPLATE_INFO (existing))
+		  existing = TI_TEMPLATE (ti);
+	    }
+	  else
+	    {
+	      if (!(mk & MK_tmpl_tmpl_mask))
+		existing = TYPE_NAME (existing);
+	      else if (mk & MK_tmpl_partial_mask)
+		{
+		  /* A partial specialization.  */
+		  for (tree partial
+			 = DECL_TEMPLATE_SPECIALIZATIONS (entry->tmpl);
+		       partial; partial = TREE_CHAIN (partial))
+		    if (TREE_TYPE (partial) == existing)
+		      {
+			existing = TREE_VALUE (partial);
+			break;
+		      }
+		}
+	      else
+		if (tree ti = CLASSTYPE_TEMPLATE_INFO (existing))
+		  existing = TI_TEMPLATE (ti);
+	    }
+	  /* The walkabout should have found ourselves.  */
+	  gcc_assert (existing == decl);
+	}
+    }
+  else if (mk != MK_unique)
+    {
+      merge_key key;
+      tree name = DECL_NAME (decl);
+
+      switch (mk)
+	{
+	default:
+	  gcc_unreachable ();
+
+	case MK_named:
+	case MK_friend_spec:
+	  if (IDENTIFIER_CONV_OP_P (name))
+	    name = conv_op_identifier;
+
+	  if (inner && TREE_CODE (inner) == FUNCTION_DECL)
+	    {
+	      /* Functions are distinguished by parameter types.  */
+	      tree fn_type = TREE_TYPE (inner);
+
+	      key.ref_q = type_memfn_rqual (fn_type);
+	      key.args = TYPE_ARG_TYPES (fn_type);
+
+	      if (tree reqs = get_constraints (inner))
+		{
+		  if (cxx_dialect < cxx20)
+		    reqs = CI_ASSOCIATED_CONSTRAINTS (reqs);
+		  else
+		    reqs = CI_DECLARATOR_REQS (reqs);
+		  key.constraints = reqs;
+		}
+
+	      if (IDENTIFIER_CONV_OP_P (name)
+		  || (decl != inner
+		      && !(name == fun_identifier
+			   /* In case the user names something _FUN  */
+			   && LAMBDA_TYPE_P (DECL_CONTEXT (inner)))))
+		/* And a function template, or conversion operator needs
+		   the return type.  Except for the _FUN thunk of a
+		   generic lambda, which has a recursive decl_type'd
+		   return type.  */
+		// FIXME: What if the return type is a voldemort?
+		key.ret = fndecl_declared_return_type (inner);
+	    }
+
+	  if (mk == MK_friend_spec)
+	    {
+	      gcc_checking_assert (dep && dep->is_special ());
+	      spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
+
+	      tree_node (entry->tmpl);
+	      tree_node (entry->args);
+	      if (streaming_p ())
+		u (get_mergeable_specialization_flags (entry->tmpl, decl));
+	    }
+	  break;
+
+	case MK_field:
+	  {
+	    unsigned ix = 0;
+	    if (TREE_CODE (inner) != FIELD_DECL)
+	      name = NULL_TREE;
+	    else
+	      gcc_checking_assert (!name || !IDENTIFIER_ANON_P (name));
+
+	    for (tree field = TYPE_FIELDS (TREE_TYPE (container));
+		 ; field = DECL_CHAIN (field))
+	      {
+		tree finner = STRIP_TEMPLATE (field);
+		if (TREE_CODE (finner) == TREE_CODE (inner))
+		  {
+		    if (finner == inner)
+		      break;
+		    ix++;
+		  }
+	      }
+	    key.index = ix;
+	  }
+	  break;
+
+	case MK_vtable:
+	  {
+	    tree vtable = CLASSTYPE_VTABLES (TREE_TYPE (container));
+	    for (unsigned ix = 0; ; vtable = DECL_CHAIN (vtable), ix++)
+	      if (vtable == decl)
+		{
+		  key.index = ix;
+		  break;
+		}
+	    name = NULL_TREE;
+	  }
+	  break;
+
+	case MK_as_base:
+	  gcc_checking_assert
+	    (decl == TYPE_NAME (CLASSTYPE_AS_BASE (TREE_TYPE (container))));
+	  break;
+
+	case MK_local_friend:
+	  {
+	    /* Find by index on the class's DECL_LIST  */
+	    unsigned ix = 0;
+	    for (tree decls = CLASSTYPE_DECL_LIST (TREE_CHAIN (decl));
+		 decls; decls = TREE_CHAIN (decls))
+	      if (!TREE_PURPOSE (decls))
+		{
+		  tree frnd = friend_from_decl_list (TREE_VALUE (decls));
+		  if (frnd == decl)
+		    break;
+		  ix++;
+		}
+	    key.index = ix;
+	    name = NULL_TREE;
+	  }
+	  break;
+
+	case MK_enum:
+	  {
+	    /* Anonymous enums are located by their first identifier,
+	       and underlying type.  */
+	    tree type = TREE_TYPE (decl);
+
+	    gcc_checking_assert (UNSCOPED_ENUM_P (type));
+	    /* Using the type name drops the bit precision we might
+	       have been using on the enum.  */
+	    key.ret = TYPE_NAME (ENUM_UNDERLYING_TYPE (type));
+	    if (tree values = TYPE_VALUES (type))
+	      name = DECL_NAME (TREE_VALUE (values));
+	  }
+	  break;
+
+	case MK_attached:
+	  {
+	    gcc_checking_assert (LAMBDA_TYPE_P (TREE_TYPE (inner)));
+	    tree scope = LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR
+						  (TREE_TYPE (inner)));
+	    gcc_checking_assert (TREE_CODE (scope) == VAR_DECL);
+	    attachset *root = attached_table->get (DECL_UID (scope));
+	    unsigned ix = root->num;
+	    /* If we don't find it, we'll write a really big number
+	       that the reader will ignore.  */
+	    while (ix--)
+	      if (root->values[ix] == inner)
+		break;
+
+	    /* Use the attached-to decl as the 'name'.  */
+	    name = scope;
+	    key.index = ix;
+	  }
+	  break;
+
+	case MK_partial:
+	  {
+	    key.constraints = get_constraints (inner);
+	    key.ret = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner));
+	    key.args = CLASSTYPE_TI_ARGS (TREE_TYPE (inner));
+	  }
+	  break;
+	}
+
+      tree_node (name);
+      if (streaming_p ())
+	{
+	  unsigned code = (key.ref_q << 0) | (key.index << 2);
+	  u (code);
+	}
+
+      if (mk == MK_enum)
+	tree_node (key.ret);
+      else if (mk == MK_partial
+	       || (mk == MK_named && inner
+		   && TREE_CODE (inner) == FUNCTION_DECL))
+	{
+	  tree_node (key.ret);
+	  tree arg = key.args;
+	  if (mk == MK_named)
+	    while (arg && arg != void_list_node)
+	      {
+		tree_node (TREE_VALUE (arg));
+		arg = TREE_CHAIN (arg);
+	      }
+	  tree_node (arg);
+	  tree_node (key.constraints);
+	}
+    }
+}
+
+/* DECL is a new declaration that may be duplicated in OVL.  Use RET &
+   ARGS to find its clone, or NULL.  If DECL's DECL_NAME is NULL, this
+   has been found by a proxy.  It will be an enum type located by it's
+   first member.
+
+   We're conservative with matches, so ambiguous decls will be
+   registered as different, then lead to a lookup error if the two
+   modules are both visible.  Perhaps we want to do something similar
+   to duplicate decls to get ODR errors on loading?  We already have
+   some special casing for namespaces.  */
+
+static tree
+check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key)
+{
+  for (ovl_iterator iter (ovl); iter; ++iter)
+    {
+      tree match = *iter;
+
+      tree d_inner = decl;
+      tree m_inner = match;
+
+    again:
+      if (TREE_CODE (d_inner) != TREE_CODE (m_inner))
+	{
+	  if (TREE_CODE (match) == NAMESPACE_DECL
+	      && !DECL_NAMESPACE_ALIAS (match))
+	    /* Namespaces are never overloaded.  */
+	    return match;
+
+	  continue;
+	}
+
+      switch (TREE_CODE (d_inner))
+	{
+	case TEMPLATE_DECL:
+	  if (template_heads_equivalent_p (d_inner, m_inner))
+	    {
+	      d_inner = DECL_TEMPLATE_RESULT (d_inner);
+	      m_inner = DECL_TEMPLATE_RESULT (m_inner);
+	      if (d_inner == error_mark_node
+		  && TYPE_DECL_ALIAS_P (m_inner))
+		return match;
+	      goto again;
+	    }
+	  break;
+
+	case FUNCTION_DECL:
+	  if (tree m_type = TREE_TYPE (m_inner))
+	    if ((!key.ret
+		 || same_type_p (key.ret, fndecl_declared_return_type (m_inner)))
+		&& type_memfn_rqual (m_type) == key.ref_q
+		&& compparms (key.args, TYPE_ARG_TYPES (m_type))
+		/* Reject if old is a "C" builtin and new is not "C".
+		   Matches decls_match behaviour.  */
+		&& (!DECL_IS_BUILTIN (m_inner)
+		    || !DECL_EXTERN_C_P (m_inner)
+		    || DECL_EXTERN_C_P (d_inner)))
+	      {
+		tree m_reqs = get_constraints (m_inner);
+		if (m_reqs)
+		  {
+		    if (cxx_dialect < cxx20)
+		      m_reqs = CI_ASSOCIATED_CONSTRAINTS (m_reqs);
+		    else
+		      m_reqs = CI_DECLARATOR_REQS (m_reqs);
+		  }
+
+		if (cp_tree_equal (m_reqs, key.constraints))
+		  return match;
+	      }
+	  break;
+
+	case TYPE_DECL:
+	  if (DECL_IMPLICIT_TYPEDEF_P (d_inner)
+	      == DECL_IMPLICIT_TYPEDEF_P (m_inner))
+	    {
+	      if (!IDENTIFIER_ANON_P (DECL_NAME (m_inner)))
+		return match;
+	      else if (mk == MK_enum
+		       && (TYPE_NAME (ENUM_UNDERLYING_TYPE (TREE_TYPE (m_inner)))
+			   == key.ret))
+		return match;
+	    }
+	  break;
+
+	default:
+	  return match;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* DECL, INNER & TYPE are a skeleton set of nodes for a decl.  Only
+   the bools have been filled in.  Read its merging key and merge it.
+   Returns the existing decl if there is one.  */
+
+tree
+trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
+			 tree type, tree container, bool is_mod)
+{
+  const char *kind = "new";
+  tree existing = NULL_TREE;
+
+  if (mk & MK_template_mask)
+    {
+      tree tmpl = tree_node ();
+      tree args = tree_node ();
+      unsigned flags = u ();
+
+      DECL_NAME (decl) = DECL_NAME (tmpl);
+      DECL_CONTEXT (decl) = DECL_CONTEXT (tmpl);
+      DECL_NAME (inner) = DECL_NAME (decl);
+      DECL_CONTEXT (inner) = DECL_CONTEXT (decl);
+
+      tree insert = decl;
+      if (mk & MK_tmpl_tmpl_mask)
+	{
+	  if (inner == decl)
+	    return error_mark_node;
+	  insert = inner;
+	}
+      bool is_decl = mk & MK_tmpl_decl_mask;
+      if (!is_decl)
+	{
+	  if (mk == MK_type_spec && inner != decl)
+	    return error_mark_node;
+	  insert = type;
+	}
+
+      existing = match_mergeable_specialization (is_decl, tmpl, args, insert);
+
+      if (!existing)
+	add_mergeable_specialization (tmpl, args, decl, flags);
+      else if (mk & MK_tmpl_decl_mask)
+	{
+	  /* A declaration specialization.  */
+	  if (mk & MK_tmpl_tmpl_mask)
+	    if (tree ti = DECL_TEMPLATE_INFO (existing))
+	      {
+		tree tmpl = TI_TEMPLATE (ti);
+		if (DECL_TEMPLATE_RESULT (tmpl) == existing)
+		  existing = tmpl;
+	      }
+	}
+      else
+	{
+	  /* A type specialization.  */
+	  if (!(mk & MK_tmpl_tmpl_mask))
+	    existing = TYPE_NAME (existing);
+	  else if (mk & MK_tmpl_partial_mask)
+	    {
+	      /* A partial specialization.  */
+	      for (tree partial = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
+		   partial; partial = TREE_CHAIN (partial))
+		if (TREE_TYPE (partial) == existing)
+		  {
+		    existing = TREE_VALUE (partial);
+		    break;
+		  }
+	      gcc_assert (TREE_CODE (existing) == TEMPLATE_DECL);
+	    }
+	  else
+	    if (tree ti = CLASSTYPE_TEMPLATE_INFO (existing))
+	      {
+		tree tmpl = TI_TEMPLATE (ti);
+		if (DECL_TEMPLATE_RESULT (tmpl) == TYPE_NAME (existing))
+		  existing = tmpl;
+	      }
+	}
+    }
+  else if (mk == MK_unique)
+    kind = "unique";
+  else
+    {
+      tree name = tree_node ();
+
+      merge_key key;
+      unsigned code = u ();
+      key.ref_q = cp_ref_qualifier ((code >> 0) & 3);
+      key.index = code >> 2;
+
+      if (mk == MK_enum)
+	key.ret = tree_node ();
+      else if (mk == MK_partial
+	       || ((mk == MK_named || mk == MK_friend_spec)
+		   && inner && TREE_CODE (inner) == FUNCTION_DECL))
+	{
+	  key.ret = tree_node ();
+	  tree arg, *arg_ptr = &key.args;
+	  while ((arg = tree_node ())
+		 && arg != void_list_node
+		 && mk != MK_partial)
+	    {
+	      *arg_ptr = tree_cons (NULL_TREE, arg, NULL_TREE);
+	      arg_ptr = &TREE_CHAIN (*arg_ptr);
+	    }
+	  *arg_ptr = arg;
+	  key.constraints = tree_node ();
+	}
+
+      if (get_overrun ())
+	return error_mark_node;
+
+      if (mk < MK_indirect_lwm)
+	{
+	  DECL_NAME (decl) = name;
+	  DECL_CONTEXT (decl) = FROB_CONTEXT (container);
+	}
+      if (inner)
+	{
+	  DECL_NAME (inner) = DECL_NAME (decl);
+	  DECL_CONTEXT (inner) = DECL_CONTEXT (decl);
+	}
+
+      if (mk == MK_partial)
+	{
+	  for (tree spec = DECL_TEMPLATE_SPECIALIZATIONS (key.ret);
+	       spec; spec = TREE_CHAIN (spec))
+	    {
+	      tree tmpl = TREE_VALUE (spec);
+	      if (template_args_equal (key.args,
+				       CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)))
+		  && cp_tree_equal (key.constraints,
+				    get_constraints
+				    (DECL_TEMPLATE_RESULT (tmpl))))
+		{
+		  existing = tmpl;
+		  break;
+		}
+	    }
+	  if (!existing)
+	    add_mergeable_specialization (key.ret, key.args, decl, 2);
+	}
+      else
+	switch (TREE_CODE (container))
+	  {
+	  default:
+	    gcc_unreachable ();
+
+	  case NAMESPACE_DECL:
+	    if (mk == MK_attached)
+	      {
+		if (DECL_LANG_SPECIFIC (name)
+		    && DECL_ATTACHED_DECLS_P (name))
+		  if (attachset *set = attached_table->get (DECL_UID (name)))
+		    if (key.index < set->num)
+		      {
+			existing = set->values[key.index];
+			if (existing)
+			  {
+			    gcc_checking_assert
+			      (DECL_IMPLICIT_TYPEDEF_P (existing));
+			    if (inner != decl)
+			      existing
+				= CLASSTYPE_TI_TEMPLATE (TREE_TYPE (existing));
+			  }
+		      }
+	      }
+	    else if (is_mod && !(state->is_module () || state->is_partition ()))
+	      kind = "unique";
+	    else
+	      {
+		gcc_checking_assert (mk == MK_named || mk == MK_enum);
+		tree mvec;
+		tree *vslot = mergeable_namespace_slots (container, name,
+							 !is_mod, &mvec);
+		existing = check_mergeable_decl (mk, decl, *vslot, key);
+		if (!existing)
+		  add_mergeable_namespace_entity (vslot, decl);
+		else
+		  {
+		    /* Note that we now have duplicates to deal with in
+		       name lookup.  */
+		    if (is_mod)
+		      MODULE_VECTOR_PARTITION_DUPS_P (mvec) = true;
+		    else
+		      MODULE_VECTOR_GLOBAL_DUPS_P (mvec) = true;
+		  }
+	      }
+	    break;
+
+	  case FUNCTION_DECL:
+	    // FIXME: What about a voldemort? how do we find what it
+	    // duplicates? Do we have to number vmorts relative to
+	    // their containing function?  But how would that work
+	    // when matching an in-TU declaration?
+	    kind = "unique";
+	    break;
+
+	  case TYPE_DECL:
+	    if (is_mod && !(state->is_module () || state->is_partition ())
+		/* Implicit member functions can come from
+		   anywhere.  */
+		&& !(DECL_ARTIFICIAL (decl)
+		     && TREE_CODE (decl) == FUNCTION_DECL
+		     && !DECL_THUNK_P (decl)))
+	      kind = "unique";
+	    else
+	      {
+		tree ctx = TREE_TYPE (container);
+
+		/* For some reason templated enumeral types are not marked
+		   as COMPLETE_TYPE_P, even though they have members.
+		   This may well be a bug elsewhere.  */
+		if (TREE_CODE (ctx) == ENUMERAL_TYPE)
+		  existing = find_enum_member (ctx, name);
+		else if (COMPLETE_TYPE_P (ctx))
+		  {
+		    switch (mk)
+		      {
+		      default:
+			gcc_unreachable ();
+
+		      case MK_named:
+			existing = lookup_class_binding (ctx, name);
+			if (existing)
+			  {
+			    tree inner = decl;
+			    if (TREE_CODE (inner) == TEMPLATE_DECL
+				&& !DECL_MEMBER_TEMPLATE_P (inner))
+			      inner = DECL_TEMPLATE_RESULT (inner);
+
+			    existing = check_mergeable_decl
+			      (mk, inner, existing, key);
+			    
+			    if (!existing && DECL_ALIAS_TEMPLATE_P (decl))
+			      {} // FIXME: Insert into specialization
+			    // tables, we'll need the arguments for that!
+			  }
+			break;
+
+		      case MK_field:
+			{
+			  unsigned ix = key.index;
+			  for (tree field = TYPE_FIELDS (ctx);
+			       field; field = DECL_CHAIN (field))
+			    {
+			      tree finner = STRIP_TEMPLATE (field);
+			      if (TREE_CODE (finner) == TREE_CODE (inner))
+				if (!ix--)
+				  {
+				    existing = field;
+				    break;
+				  }
+			    }
+			}
+			break;
+
+		      case MK_vtable:
+			{
+			  unsigned ix = key.index;
+			  for (tree vtable = CLASSTYPE_VTABLES (ctx);
+			       vtable; vtable = DECL_CHAIN (vtable))
+			    if (!ix--)
+			      {
+				existing = vtable;
+				break;
+			      }
+			}
+			break;
+
+		      case MK_as_base:
+			{
+			  tree as_base = CLASSTYPE_AS_BASE (ctx);
+			  if (as_base && as_base != ctx)
+			    existing = TYPE_NAME (as_base);
+			}
+			break;
+
+		      case MK_local_friend:
+			{
+			  unsigned ix = key.index;
+			  for (tree decls = CLASSTYPE_DECL_LIST (ctx);
+			       decls; decls = TREE_CHAIN (decls))
+			    if (!TREE_PURPOSE (decls) && !ix--)
+			      {
+				existing
+				  = friend_from_decl_list (TREE_VALUE (decls));
+				break;
+			      }
+			}
+			break;
+		      }
+
+		    if (existing && mk < MK_indirect_lwm && mk != MK_partial
+			&& TREE_CODE (decl) == TEMPLATE_DECL
+			&& !DECL_MEMBER_TEMPLATE_P (decl))
+		      {
+			tree ti;
+			if (DECL_IMPLICIT_TYPEDEF_P (existing))
+			  ti = TYPE_TEMPLATE_INFO (TREE_TYPE (existing));
+			else
+			  ti = DECL_TEMPLATE_INFO (existing);
+			existing = TI_TEMPLATE (ti);
+		      }
+		  }
+	      }
+	  }
+
+      if (mk == MK_friend_spec)
+	{
+	  tree tmpl = tree_node ();
+	  tree args = tree_node ();
+	  unsigned flags = u ();
+
+	  tree e = match_mergeable_specialization (true, tmpl, args, decl);
+	  if (!e)
+	    add_mergeable_specialization (tmpl, args,
+					  existing ? existing : decl, flags);
+	  else if (e != existing)
+	    set_overrun ();
+	}
+    }
+
+  dump (dumper::MERGE)
+    && dump ("Read:%d's %s merge key (%s) %C:%N", tag, merge_kind_name[mk],
+	     existing ? "matched" : kind, TREE_CODE (decl), decl);
+
+  return existing;
+}
+
+void
+trees_out::binfo_mergeable (tree binfo)
+{
+  tree dom = binfo;
+  while (tree parent = BINFO_INHERITANCE_CHAIN (dom))
+    dom = parent;
+  tree type = BINFO_TYPE (dom);
+  gcc_checking_assert (TYPE_BINFO (type) == dom);
+  tree_node (type);
+  if (streaming_p ())
+    {
+      unsigned ix = 0;
+      for (; dom != binfo; dom = TREE_CHAIN (dom))
+	ix++;
+      u (ix);
+    }
+}
+
+unsigned
+trees_in::binfo_mergeable (tree *type)
+{
+  *type = tree_node ();
+  return u ();
+}
+
+/* DECL is a just streamed mergeable decl that should match EXISTING.  Check
+   it does and issue an appropriate diagnostic if not.  Merge any
+   bits from DECL to EXISTING.  This is stricter matching than
+   decls_match, because we can rely on ODR-sameness, and we cannot use
+   decls_match because it can cause instantiations of constraints.  */
+
+bool
+trees_in::is_matching_decl (tree existing, tree decl)
+{
+  // FIXME: We should probably do some duplicate decl-like stuff here
+  // (beware, default parms should be the same?)  Can we just call
+  // duplicate_decls and teach it how to handle the module-specific
+  // permitted/required duplications?
+
+  // We know at this point that the decls have matched by key, so we
+  // can elide some of the checking
+  gcc_checking_assert (TREE_CODE (existing) == TREE_CODE (decl));
+
+  tree inner = decl;
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      inner = DECL_TEMPLATE_RESULT (decl);
+      gcc_checking_assert (TREE_CODE (DECL_TEMPLATE_RESULT (existing))
+			   == TREE_CODE (inner));
+    }
+  
+  if (TREE_CODE (inner) == FUNCTION_DECL)
+    {
+      tree e_ret = fndecl_declared_return_type (existing);
+      tree d_ret = fndecl_declared_return_type (decl);
+
+      if (decl != inner && DECL_NAME (inner) == fun_identifier
+	  && LAMBDA_TYPE_P (DECL_CONTEXT (inner)))
+	/* This has a recursive type that will compare different.  */;
+      else if (!same_type_p (e_ret, d_ret))
+	goto mismatch;
+
+      tree e_type = TREE_TYPE (existing);
+      tree d_type = TREE_TYPE (decl);
+
+      if (DECL_EXTERN_C_P (decl) != DECL_EXTERN_C_P (existing))
+	goto mismatch;
+
+      for (tree e_args = TYPE_ARG_TYPES (e_type),
+	     d_args = TYPE_ARG_TYPES (d_type);
+	   e_args != d_args && (e_args || d_args);
+	   e_args = TREE_CHAIN (e_args), d_args = TREE_CHAIN (d_args))
+	{
+	  if (!(e_args && d_args))
+	    goto mismatch;
+
+	  if (!same_type_p (TREE_VALUE (e_args), TREE_VALUE (d_args)))
+	    goto mismatch;
+	  
+	  // FIXME: Check default values
+	}
+
+      /* If EXISTING has an undeduced or uninstantiated exception
+	 specification, but DECL does not, propagate the exception
+	 specification.  Otherwise we end up asserting or trying to
+	 instantiate it in the middle of loading.   */
+      tree e_spec = TYPE_RAISES_EXCEPTIONS (e_type);
+      tree d_spec = TYPE_RAISES_EXCEPTIONS (d_type);
+      if (DEFERRED_NOEXCEPT_SPEC_P (e_spec))
+	{
+	  if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec)
+	      || (UNEVALUATED_NOEXCEPT_SPEC_P (e_spec)
+		  && !UNEVALUATED_NOEXCEPT_SPEC_P (d_spec)))
+	    {
+	      dump (dumper::MERGE)
+		&& dump ("Propagating instantiated noexcept to %N", existing);
+	      TREE_TYPE (existing) = d_type;
+
+	      /* Propagate to existing clones.  */
+	      tree clone;
+	      FOR_EACH_CLONE (clone, existing)
+		{
+		  if (TREE_TYPE (clone) == e_type)
+		    TREE_TYPE (clone) = d_type;
+		  else
+		    TREE_TYPE (clone)
+		      = build_exception_variant (TREE_TYPE (clone), d_spec);
+		}
+	    }
+	}
+      else if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec)
+	       && !comp_except_specs (e_spec, d_spec, ce_type))
+	goto mismatch;
+    }
+  /* Using cp_tree_equal because we can meet TYPE_ARGUMENT_PACKs
+     here. I suspect the entities that directly do that are things
+     that shouldn't go to duplicate_decls (FIELD_DECLs etc).   */
+  else if (!cp_tree_equal (TREE_TYPE (existing), TREE_TYPE (decl)))
+    {
+    mismatch:
+      if (DECL_UNDECLARED_BUILTIN_P (existing))
+	/* Just like duplicate_decls, presum the user knows what
+	   they're doing in overriding a builtin.   */
+	TREE_TYPE (existing) = TREE_TYPE (decl);
+      else
+	{
+	  // FIXME:QOI Might be template specialization from a module,
+	  // not necessarily global module
+	  error_at (DECL_SOURCE_LOCATION (decl),
+		    "conflicting global module declaration %#qD", decl);
+	  inform (DECL_SOURCE_LOCATION (existing),
+		  "existing declaration %#qD", existing);
+	  return false;
+	}
+    }
+
+  if (DECL_UNDECLARED_BUILTIN_P (existing)
+      && !DECL_UNDECLARED_BUILTIN_P (decl))
+    {
+      /* We're matching a builtin that the user has yet to declare.
+	 We are the one!  This is very much duplicate-decl
+	 shenanigans. */
+      DECL_SOURCE_LOCATION (existing) = DECL_SOURCE_LOCATION (decl);
+      if (TREE_CODE (decl) != TYPE_DECL)
+	{
+	  /* Propagate exceptions etc.  */
+	  TREE_TYPE (existing) = TREE_TYPE (decl);
+	  TREE_NOTHROW (existing) = TREE_NOTHROW (decl);
+	}
+      /* This is actually an import! */
+      DECL_MODULE_IMPORT_P (existing) = true;
+
+      /* Yay, sliced!  */
+      existing->base = decl->base;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	{
+	  /* Ew :(  */
+	  memcpy (&existing->decl_common.size,
+		  &decl->decl_common.size,
+		  (offsetof (tree_decl_common, pt_uid)
+		   - offsetof (tree_decl_common, size)));
+	  auto bltin_class = DECL_BUILT_IN_CLASS (decl);
+	  existing->function_decl.built_in_class = bltin_class;
+	  auto fncode = DECL_UNCHECKED_FUNCTION_CODE (decl);
+	  DECL_UNCHECKED_FUNCTION_CODE (existing) = fncode;
+	  if (existing->function_decl.built_in_class == BUILT_IN_NORMAL)
+	    {
+	      if (builtin_decl_explicit_p (built_in_function (fncode)))
+		switch (fncode)
+		  {
+		  case BUILT_IN_STPCPY:
+		    set_builtin_decl_implicit_p
+		      (built_in_function (fncode), true);
+		    break;
+		  default:
+		    set_builtin_decl_declared_p
+		      (built_in_function (fncode), true);
+		    break;
+		  }
+	      copy_attributes_to_builtin (decl);
+	    }
+	}
+    }
+
+  if (VAR_OR_FUNCTION_DECL_P (decl)
+      && DECL_TEMPLATE_INSTANTIATED (decl))
+    /* Don't instantiate again!  */
+    DECL_TEMPLATE_INSTANTIATED (existing) = true;
+
+  tree e_inner = inner == decl ? existing : DECL_TEMPLATE_RESULT (existing);
+
+  if (TREE_CODE (inner) == FUNCTION_DECL
+      && DECL_DECLARED_INLINE_P (inner))
+    DECL_DECLARED_INLINE_P (e_inner) = true;
+  if (!DECL_EXTERNAL (inner))
+    DECL_EXTERNAL (e_inner) = false;
+
+  // FIXME: Check default tmpl and fn parms here
+
+  return true;
+}
+
+/* FN is an implicit member function that we've discovered is new to
+   the class.  Add it to the TYPE_FIELDS chain and the method vector.
+   Reset the appropriate classtype lazy flag.   */
+
+bool
+trees_in::install_implicit_member (tree fn)
+{
+  tree ctx = DECL_CONTEXT (fn);
+  tree name = DECL_NAME (fn);
+  /* We know these are synthesized, so the set of expected prototypes
+     is quite restricted.  We're not validating correctness, just
+     distinguishing beteeen the small set of possibilities.  */
+  tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (fn));
+  if (IDENTIFIER_CTOR_P (name))
+    {
+      if (CLASSTYPE_LAZY_DEFAULT_CTOR (ctx)
+	  && VOID_TYPE_P (parm_type))
+	CLASSTYPE_LAZY_DEFAULT_CTOR (ctx) = false;
+      else if (!TYPE_REF_P (parm_type))
+	return false;
+      else if (CLASSTYPE_LAZY_COPY_CTOR (ctx)
+	       && !TYPE_REF_IS_RVALUE (parm_type))
+	CLASSTYPE_LAZY_COPY_CTOR (ctx) = false;
+      else if (CLASSTYPE_LAZY_MOVE_CTOR (ctx))
+	CLASSTYPE_LAZY_MOVE_CTOR (ctx) = false;
+      else
+	return false;
+    }
+  else if (IDENTIFIER_DTOR_P (name))
+    {
+      if (CLASSTYPE_LAZY_DESTRUCTOR (ctx))
+	CLASSTYPE_LAZY_DESTRUCTOR (ctx) = false;
+      else
+	return false;
+      if (DECL_VIRTUAL_P (fn))
+	/* A virtual dtor should have been created when the class
+	   became complete.  */
+	return false;
+    }
+  else if (name == assign_op_identifier)
+    {
+      if (!TYPE_REF_P (parm_type))
+	return false;
+      else if (CLASSTYPE_LAZY_COPY_ASSIGN (ctx)
+	       && !TYPE_REF_IS_RVALUE (parm_type))
+	CLASSTYPE_LAZY_COPY_ASSIGN (ctx) = false;
+      else if (CLASSTYPE_LAZY_MOVE_ASSIGN (ctx))
+	CLASSTYPE_LAZY_MOVE_ASSIGN (ctx) = false;
+      else
+	return false;
+    }
+  else
+    return false;
+
+  dump (dumper::MERGE) && dump ("Adding implicit member %N", fn);
+
+  DECL_CHAIN (fn) = TYPE_FIELDS (ctx);
+  TYPE_FIELDS (ctx) = fn;
+
+  add_method (ctx, fn, false);
+
+    /* Propagate TYPE_FIELDS.  */
+  fixup_type_variants (ctx);
+
+  return true;
+}
+
+/* Return non-zero if DECL has a definition that would be interesting to
+   write out.  */
+
+static bool
+has_definition (tree decl)
+{
+  bool is_tmpl = TREE_CODE (decl) == TEMPLATE_DECL;
+  if (is_tmpl)
+    decl = DECL_TEMPLATE_RESULT (decl);
+
+  switch (TREE_CODE (decl))
+    {
+    default:
+      break;
+
+    case FUNCTION_DECL:
+      if (!DECL_SAVED_TREE (decl))
+	/* Not defined.  */
+	break;
+
+      if (DECL_DECLARED_INLINE_P (decl))
+	return true;
+
+      if (DECL_THIS_STATIC (decl)
+	  && (header_module_p ()
+	      || (!DECL_LANG_SPECIFIC (decl) || !DECL_MODULE_PURVIEW_P (decl))))
+	/* GM static function.  */
+	return true;
+
+      if (DECL_TEMPLATE_INFO (decl))
+	{
+	  int use_tpl = DECL_USE_TEMPLATE (decl);
+
+	  // FIXME: Partial specializations have definitions too.
+	  if (use_tpl < 2)
+	    return true;
+	}
+      break;
+
+    case TYPE_DECL:
+      {
+	tree type = TREE_TYPE (decl);
+	if (type == TYPE_MAIN_VARIANT (type)
+	    && decl == TYPE_NAME (type)
+	    && (TREE_CODE (type) == ENUMERAL_TYPE
+		? TYPE_VALUES (type) : TYPE_FIELDS (type)))
+	  return true;
+      }
+      break;
+
+    case VAR_DECL:
+      if (DECL_TEMPLATE_INFO (decl)
+	  && DECL_USE_TEMPLATE (decl) < 2)
+	return DECL_INITIAL (decl);
+      else
+	{
+	  if (!DECL_INITIALIZED_P (decl))
+	    return false;
+
+	  if (header_module_p ()
+	      || (!DECL_LANG_SPECIFIC (decl) || !DECL_MODULE_PURVIEW_P (decl)))
+	    /* GM static variable.  */
+	    return true;
+
+	  if (!TREE_CONSTANT (decl))
+	    return false;
+
+	  return true;
+	}
+      break;
+
+    case CONCEPT_DECL:
+      if (DECL_INITIAL (decl))
+	return true;
+
+      break;
+    }
+
+  return false;
+}
+
+uintptr_t *
+trees_in::find_duplicate (tree existing)
+{
+  if (!duplicates)
+    return NULL;
+
+  return duplicates->get (existing);
+}
+
+/* We're starting to read a duplicate DECL.  EXISTING is the already
+   known node.  */
+
+void
+trees_in::register_duplicate (tree decl, tree existing)
+{
+  if (!duplicates)
+    duplicates = new duplicate_hash_map (40);
+
+  bool existed;
+  uintptr_t &slot = duplicates->get_or_insert (existing, &existed);
+  gcc_checking_assert (!existed);
+  slot = reinterpret_cast<uintptr_t> (decl);
+}
+
+/* We've read a definition of MAYBE_EXISTING.  If not a duplicate,
+   return MAYBE_EXISTING (into which the definition should be
+   installed).  Otherwise return NULL if already known bad, or the
+   duplicate we read (for ODR checking, or extracting addtional merge
+   information).  */
+
+tree
+trees_in::odr_duplicate (tree maybe_existing, bool has_defn)
+{
+  tree res = NULL_TREE;
+
+  if (uintptr_t *dup = find_duplicate (maybe_existing))
+    {
+      if (!(*dup & 1))
+	res = reinterpret_cast<tree> (*dup);
+    }
+  else
+    res = maybe_existing;
+
+  assert_definition (maybe_existing, res && !has_defn);
+
+  // FIXME: We probably need to return the template, so that the
+  // template header can be checked?
+  return res ? STRIP_TEMPLATE (res) : NULL_TREE;
+}
+
+/* The following writer functions rely on the current behaviour of
+   depset::hash::add_dependency making the decl and defn depset nodes
+   depend on eachother.  That way we don't have to worry about seeding
+   the tree map with named decls that cannot be looked up by name (I.e
+   template and function parms).  We know the decl and definition will
+   be in the same cluster, which is what we want.  */
+
+void
+trees_out::write_function_def (tree decl)
+{
+  tree_node (DECL_RESULT (decl));
+  tree_node (DECL_INITIAL (decl));
+  tree_node (DECL_SAVED_TREE (decl));
+  tree_node (DECL_FRIEND_CONTEXT (decl));
+
+  constexpr_fundef *cexpr = retrieve_constexpr_fundef (decl);
+  int tag = 0;
+  if (cexpr)
+    {
+      if (cexpr->result == error_mark_node)
+	/* We'll stream the RESULT_DECL naturally during the
+	   serialization.  We never need to fish it back again, so
+	   that's ok.  */
+	tag = 0;
+      else
+	tag = insert (cexpr->result);
+    }
+  if (streaming_p ())
+    {
+      i (tag);
+      if (tag)
+	dump (dumper::TREE)
+	  && dump ("Constexpr:%d result %N", tag, cexpr->result);
+    }
+  if (tag)
+    {
+      unsigned ix = 0;
+      for (tree parm = cexpr->parms; parm; parm = DECL_CHAIN (parm), ix++)
+	{
+	  tag = insert (parm);
+	  if (streaming_p ())
+	    dump (dumper::TREE)
+	      && dump ("Constexpr:%d parm:%u %N", tag, ix, parm);
+	}
+      tree_node (cexpr->body);
+    }
+
+  if (streaming_p ())
+    {
+      unsigned flags = 0;
+
+      if (DECL_NOT_REALLY_EXTERN (decl))
+	flags |= 1;
+
+      u (flags);
+    }
+}
+
+void
+trees_out::mark_function_def (tree)
+{
+}
+
+bool
+trees_in::read_function_def (tree decl, tree maybe_template)
+{
+  dump () && dump ("Reading function definition %N", decl);
+  tree result = tree_node ();
+  tree initial = tree_node ();
+  tree saved = tree_node ();
+  tree context = tree_node ();
+  constexpr_fundef cexpr;
+
+  tree maybe_dup = odr_duplicate (maybe_template, DECL_SAVED_TREE (decl));
+  bool installing = maybe_dup && !DECL_SAVED_TREE (decl);
+
+  if (maybe_dup)
+    for (auto parm = DECL_ARGUMENTS (maybe_dup); parm; parm = DECL_CHAIN (parm))
+      DECL_CONTEXT (parm) = decl;
+
+  if (int wtag = i ())
+    {
+      int tag = 1;
+      cexpr.result = error_mark_node;
+
+      cexpr.result = copy_decl (result);
+      tag = insert (cexpr.result);
+
+      if (wtag != tag)
+	set_overrun ();
+      dump (dumper::TREE)
+	&& dump ("Constexpr:%d result %N", tag, cexpr.result);
+
+      cexpr.parms = NULL_TREE;
+      tree *chain = &cexpr.parms;
+      unsigned ix = 0;
+      for (tree parm = DECL_ARGUMENTS (maybe_dup ? maybe_dup : decl);
+	   parm; parm = DECL_CHAIN (parm), ix++)
+	{
+	  tree p = copy_decl (parm);
+	  tag = insert (p);
+	  dump (dumper::TREE)
+	    && dump ("Constexpr:%d parm:%u %N", tag, ix, p);
+	  *chain = p;
+	  chain = &DECL_CHAIN (p);
+	}
+      cexpr.body = tree_node ();
+      cexpr.decl = decl;
+    }
+  else
+    cexpr.decl = NULL_TREE;
+
+  unsigned flags = u ();
+
+  if (get_overrun ())
+    return NULL_TREE;
+
+  if (installing)
+    {
+      DECL_NOT_REALLY_EXTERN (decl) = flags & 1;
+      DECL_RESULT (decl) = result;
+      DECL_INITIAL (decl) = initial;
+      DECL_SAVED_TREE (decl) = saved;
+      if (maybe_dup)
+	DECL_ARGUMENTS (decl) = DECL_ARGUMENTS (maybe_dup);
+
+      if (context)
+	SET_DECL_FRIEND_CONTEXT (decl, context);
+      if (cexpr.decl)
+	register_constexpr_fundef (cexpr);
+      post_process (maybe_template);
+    }
+  else if (maybe_dup)
+    {
+      // FIXME:QOI Check matching defn
+    }
+  
+  return true;
+}
+
+/* Also for CONCEPT_DECLs.  */
+
+void
+trees_out::write_var_def (tree decl)
+{
+  tree init = DECL_INITIAL (decl);
+  tree_node (init);
+  if (!init)
+    {
+      tree dyn_init = NULL_TREE;
+
+      if (DECL_NONTRIVIALLY_INITIALIZED_P (decl))
+	{
+	  dyn_init = value_member (decl,
+				   CP_DECL_THREAD_LOCAL_P (decl)
+				   ? tls_aggregates : static_aggregates);
+	  gcc_checking_assert (dyn_init);
+	  /* Mark it so write_inits knows this is needed.  */
+	  TREE_LANG_FLAG_0 (dyn_init) = true;
+	  dyn_init = TREE_PURPOSE (dyn_init);
+	}
+      tree_node (dyn_init);
+    }
+}
+
+void
+trees_out::mark_var_def (tree)
+{
+}
+
+bool
+trees_in::read_var_def (tree decl, tree maybe_template)
+{
+  /* Do not mark the virtual table entries as used.  */
+  bool vtable = TREE_CODE (decl) == VAR_DECL && DECL_VTABLE_OR_VTT_P (decl);
+  unused += vtable;
+  tree init = tree_node ();
+  tree dyn_init = init ? NULL_TREE : tree_node ();
+  unused -= vtable;
+
+  if (get_overrun ())
+    return false;
+
+  bool initialized = (VAR_P (decl) ? bool (DECL_INITIALIZED_P (decl))
+		      : bool (DECL_INITIAL (decl)));
+  tree maybe_dup = odr_duplicate (maybe_template, initialized);
+  bool installing = maybe_dup && !initialized;
+  if (installing)
+    {
+      if (DECL_EXTERNAL (decl))
+	DECL_NOT_REALLY_EXTERN (decl) = true;
+      if (VAR_P (decl))
+	DECL_INITIALIZED_P (decl) = true;
+      DECL_INITIAL (decl) = init;
+      if (!dyn_init)
+	;
+      else if (CP_DECL_THREAD_LOCAL_P (decl))
+	tls_aggregates = tree_cons (dyn_init, decl, tls_aggregates);
+      else
+	static_aggregates = tree_cons (dyn_init, decl, static_aggregates);
+    }
+  else if (maybe_dup)
+    {
+      // FIXME:QOI Check matching defn
+    }
+
+  return true;
+}
+
+/* If MEMBER doesn't have an independent life outside the class,
+   return it (or it's TEMPLATE_DECL).  Otherwise NULL.  */
+
+static tree
+member_owned_by_class (tree member)
+{
+  gcc_assert (DECL_P (member));
+
+  /* Clones are owned by their origin.  */
+  if (DECL_CLONED_FUNCTION_P (member))
+    return NULL;
+
+  if (TREE_CODE (member) == FIELD_DECL)
+    /* FIELD_DECLS can have template info in some cases.  We always
+       want the FIELD_DECL though, as there's never a TEMPLATE_DECL
+       wrapping them.  */
+    return member;
+
+  int use_tpl = -1;
+  if (tree ti = node_template_info (member, use_tpl))
+    {
+      // FIXME: Don't bail on things that CANNOT have their own
+      // template header.  No, make sure they're in the same cluster.
+      if (use_tpl > 0)
+	return NULL_TREE;
+
+      if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == member)
+	member = TI_TEMPLATE (ti);
+    }
+  return member;
+}
+
+void
+trees_out::write_class_def (tree defn)
+{
+  gcc_assert (DECL_P (defn));
+  if (streaming_p ())
+    dump () && dump ("Writing class definition %N", defn);
+
+  tree type = TREE_TYPE (defn);
+  tree_node (TYPE_SIZE (type));
+  tree_node (TYPE_SIZE_UNIT (type));
+  tree_node (TYPE_VFIELD (type));
+  tree_node (TYPE_BINFO (type));
+
+  vec_chained_decls (TYPE_FIELDS (type));
+
+  /* Every class but __as_base has a type-specific.  */
+  gcc_checking_assert (!TYPE_LANG_SPECIFIC (type) == IS_FAKE_BASE_TYPE (type));
+
+  if (TYPE_LANG_SPECIFIC (type))
+    {
+      {
+	vec<tree, va_gc> *v = CLASSTYPE_MEMBER_VEC (type);
+	if (!v)
+	  {
+	    gcc_checking_assert (!streaming_p ());
+	    /* Force a class vector.  */
+	    v = set_class_bindings (type, -1);
+	    gcc_checking_assert (v);
+	  }
+
+	unsigned len = v->length ();
+	if (streaming_p ())
+	  u (len);
+	for (unsigned ix = 0; ix != len; ix++)
+	  {
+	    tree m = (*v)[ix];
+	    if (TREE_CODE (m) == TYPE_DECL
+		&& DECL_ARTIFICIAL (m)
+		&& TYPE_STUB_DECL (TREE_TYPE (m)) == m)
+	      /* This is a using-decl for a type, or an anonymous
+		 struct (maybe with a typedef name).  Write the type.  */
+	      m = TREE_TYPE (m);
+	    tree_node (m);
+	  }
+      }
+      tree_node (CLASSTYPE_LAMBDA_EXPR (type));
+
+      /* TYPE_CONTAINS_VPTR_P looks at the vbase vector, which the
+	 reader won't know at this point.  */
+      int has_vptr = TYPE_CONTAINS_VPTR_P (type);
+
+      if (streaming_p ())
+	{
+	  unsigned nvbases = vec_safe_length (CLASSTYPE_VBASECLASSES (type));
+	  u (nvbases);
+	  i (has_vptr);
+	}
+
+      if (has_vptr)
+	{
+	  tree_vec (CLASSTYPE_PURE_VIRTUALS (type));
+	  tree_pair_vec (CLASSTYPE_VCALL_INDICES (type));
+	  tree_node (CLASSTYPE_KEY_METHOD (type));
+	}
+    }
+
+  if (TYPE_LANG_SPECIFIC (type))
+    {
+      tree_node (CLASSTYPE_PRIMARY_BINFO (type));
+
+      tree as_base = CLASSTYPE_AS_BASE (type);
+      if (as_base)
+	as_base = TYPE_NAME (as_base);
+      tree_node (as_base);
+
+      /* Write the vtables.  */
+      tree vtables = CLASSTYPE_VTABLES (type);
+      vec_chained_decls (vtables);
+      for (; vtables; vtables = TREE_CHAIN (vtables))
+	write_definition (vtables);
+
+      /* Write the friend classes.  */
+      tree_list (CLASSTYPE_FRIEND_CLASSES (type), false);
+
+      /* Write the friend functions.  */
+      for (tree friends = DECL_FRIENDLIST (defn);
+	   friends; friends = TREE_CHAIN (friends))
+	{
+	  /* Name of these friends.  */
+	  tree_node (TREE_PURPOSE (friends));
+	  tree_list (TREE_VALUE (friends), false);
+	}
+      /* End of friend fns.  */
+      tree_node (NULL_TREE);
+
+      /* Write the decl list.  */
+      tree_list (CLASSTYPE_DECL_LIST (type), true);
+
+      if (TYPE_CONTAINS_VPTR_P (type))
+	{
+	  /* Write the thunks.  */
+	  for (tree decls = TYPE_FIELDS (type);
+	       decls; decls = DECL_CHAIN (decls))
+	    if (TREE_CODE (decls) == FUNCTION_DECL
+		&& DECL_VIRTUAL_P (decls)
+		&& DECL_THUNKS (decls))
+	      {
+		tree_node (decls);
+		/* Thunks are always unique, so chaining is ok.  */
+		chained_decls (DECL_THUNKS (decls));
+	      }
+	  tree_node (NULL_TREE);
+	}
+    }
+}
+
+void
+trees_out::mark_class_member (tree member, bool do_defn)
+{
+  gcc_assert (DECL_P (member));
+
+  member = member_owned_by_class (member);
+  if (member)
+    mark_declaration (member, do_defn && has_definition (member));
+}
+
+void
+trees_out::mark_class_def (tree defn)
+{
+  gcc_assert (DECL_P (defn));
+  tree type = TREE_TYPE (defn);
+  for (tree member = TYPE_FIELDS (type); member; member = DECL_CHAIN (member))
+    /* Do not mark enum consts here.  */
+    if (TREE_CODE (member) == FIELD_DECL
+	|| TREE_CODE (member) == USING_DECL)
+      {
+	mark_class_member (member);
+	if (TREE_CODE (member) == FIELD_DECL)
+	  if (tree repr = DECL_BIT_FIELD_REPRESENTATIVE (member))
+	    mark_declaration (repr, false);
+      }
+
+  /* Mark the binfo hierarchy.  */
+  for (tree child = TYPE_BINFO (type); child; child = TREE_CHAIN (child))
+    mark_by_value (child);
+
+  if (TYPE_LANG_SPECIFIC (type))
+    {
+      for (tree vtable = CLASSTYPE_VTABLES (type);
+	   vtable; vtable = TREE_CHAIN (vtable))
+	mark_declaration (vtable, true);
+
+      if (TYPE_CONTAINS_VPTR_P (type))
+	/* Mark the thunks, they belong to the class definition,
+	   /not/ the thunked-to function.  */
+	for (tree decls = TYPE_FIELDS (type);
+	     decls; decls = DECL_CHAIN (decls))
+	  if (TREE_CODE (decls) == FUNCTION_DECL)
+	    for (tree thunks = DECL_THUNKS (decls);
+		 thunks; thunks = DECL_CHAIN (thunks))
+	      mark_declaration (thunks, false);
+    }
+}
+
+/* Nop sorting, needed for resorting the member vec.  */
+
+static void
+nop (void *, void *)
+{
+}
+
+bool
+trees_in::read_class_def (tree defn, tree maybe_template)
+{
+  gcc_assert (DECL_P (defn));
+  dump () && dump ("Reading class definition %N", defn);
+  tree type = TREE_TYPE (defn);
+  tree size = tree_node ();
+  tree size_unit = tree_node ();
+  tree vfield = tree_node ();
+  tree binfo = tree_node ();
+  vec<tree, va_gc> *vbase_vec = NULL;
+  vec<tree, va_gc> *member_vec = NULL;
+  vec<tree, va_gc> *pure_virts = NULL;
+  vec<tree_pair_s, va_gc> *vcall_indices = NULL;
+  tree key_method = NULL_TREE;
+  tree lambda = NULL_TREE;
+
+  /* Read the fields.  */
+  vec<tree, va_heap> *fields = vec_chained_decls ();
+
+  if (TYPE_LANG_SPECIFIC (type))
+    {
+      if (unsigned len = u ())
+	{
+	  vec_alloc (member_vec, len);
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      tree m = tree_node ();
+	      if (get_overrun ())
+		break;
+	      if (TYPE_P (m))
+		m = TYPE_STUB_DECL (m);
+	      member_vec->quick_push (m);
+	    }
+	}
+      lambda = tree_node ();
+
+      if (!get_overrun ())
+	{
+	  unsigned nvbases = u ();
+	  if (nvbases)
+	    {
+	      vec_alloc (vbase_vec, nvbases);
+	      for (tree child = binfo; child; child = TREE_CHAIN (child))
+		if (BINFO_VIRTUAL_P (child))
+		  vbase_vec->quick_push (child);
+	    }
+	}
+
+      if (!get_overrun ())
+	{
+	  int has_vptr = i ();
+	  if (has_vptr)
+	    {
+	      pure_virts = tree_vec ();
+	      vcall_indices = tree_pair_vec ();
+	      key_method = tree_node ();
+	    }
+	}
+    }
+
+  tree maybe_dup = odr_duplicate (maybe_template, TYPE_SIZE (type));
+  bool installing = maybe_dup && !TYPE_SIZE (type);
+  if (installing)
+    {
+      if (DECL_EXTERNAL (defn) && TYPE_LANG_SPECIFIC (type))
+	{
+	  /* We don't deal with not-really-extern, because, for a
+	     module you want the import to be the interface, and for a
+	     header-unit, you're doing it wrong.  */
+	  CLASSTYPE_INTERFACE_UNKNOWN (type) = false;
+	  CLASSTYPE_INTERFACE_ONLY (type) = true;
+	}
+
+      if (maybe_dup != defn)
+	{
+	  // FIXME: This is needed on other defns too, almost
+	  // duplicate-decl like?  See is_matching_decl too.
+	  /* Copy flags from the duplicate.  */
+	  tree type_dup = TREE_TYPE (maybe_dup);
+
+	  /* Core pieces.  */
+	  TYPE_MODE_RAW (type) = TYPE_MODE_RAW (type_dup);
+	  SET_DECL_MODE (defn, DECL_MODE (maybe_dup));
+	  TREE_ADDRESSABLE (type) = TREE_ADDRESSABLE (type_dup);
+	  DECL_SIZE (defn) = DECL_SIZE (maybe_dup);
+	  DECL_SIZE_UNIT (defn) = DECL_SIZE_UNIT (maybe_dup);
+	  DECL_ALIGN_RAW (defn) = DECL_ALIGN_RAW (maybe_dup);
+	  DECL_WARN_IF_NOT_ALIGN_RAW (defn)
+	    = DECL_WARN_IF_NOT_ALIGN_RAW (maybe_dup);
+	  DECL_USER_ALIGN (defn) = DECL_USER_ALIGN (maybe_dup);
+
+	  /* C++ pieces.  */
+	  TYPE_POLYMORPHIC_P (type) = TYPE_POLYMORPHIC_P (type_dup);
+	  TYPE_HAS_USER_CONSTRUCTOR (type)
+	    = TYPE_HAS_USER_CONSTRUCTOR (type_dup);
+	  TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
+	    = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type_dup);
+
+	  if (auto ls = TYPE_LANG_SPECIFIC (type_dup))
+	    {
+	      if (TYPE_LANG_SPECIFIC (type))
+		{
+		  CLASSTYPE_BEFRIENDING_CLASSES (type_dup)
+		    = CLASSTYPE_BEFRIENDING_CLASSES (type);
+		  CLASSTYPE_TYPEINFO_VAR (type_dup)
+		    = CLASSTYPE_TYPEINFO_VAR (type);
+		}
+	      for (tree v = type; v; v = TYPE_NEXT_VARIANT (v))
+		TYPE_LANG_SPECIFIC (v) = ls;
+	    }
+	}
+
+      TYPE_SIZE (type) = size;
+      TYPE_SIZE_UNIT (type) = size_unit;
+
+      if (fields)
+	{
+	  tree *chain = &TYPE_FIELDS (type);
+	  unsigned len = fields->length ();
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      tree decl = (*fields)[ix];
+
+	      if (!decl)
+		{
+		  /* An anonymous struct with typedef name.  */
+		  tree tdef = (*fields)[ix+1];
+		  decl = TYPE_STUB_DECL (TREE_TYPE (tdef));
+		  gcc_checking_assert (IDENTIFIER_ANON_P (DECL_NAME (decl))
+				       && decl != tdef);
+		}
+
+	      gcc_checking_assert (!*chain == !DECL_CLONED_FUNCTION_P (decl));
+	      *chain = decl;
+	      chain = &DECL_CHAIN (decl);
+
+	      if (TREE_CODE (decl) == USING_DECL
+		  && TREE_CODE (USING_DECL_SCOPE (decl)) == RECORD_TYPE)
+		{
+		  /* Reconstruct DECL_ACCESS.  */
+		  tree decls = USING_DECL_DECLS (decl);
+		  tree access = declared_access (decl);
+
+		  for (ovl_iterator iter (decls); iter; ++iter)
+		    {
+		      tree d = *iter;
+
+		      retrofit_lang_decl (d);
+		      tree list = DECL_ACCESS (d);
+
+		      if (!purpose_member (type, list))
+			DECL_ACCESS (d) = tree_cons (type, access, list);
+		    }
+		}
+	    }
+	}
+
+      TYPE_VFIELD (type) = vfield;
+      TYPE_BINFO (type) = binfo;
+
+      if (TYPE_LANG_SPECIFIC (type))
+	{
+	  CLASSTYPE_LAMBDA_EXPR (type) = lambda;
+
+	  CLASSTYPE_MEMBER_VEC (type) = member_vec;
+	  CLASSTYPE_PURE_VIRTUALS (type) = pure_virts;
+	  CLASSTYPE_VCALL_INDICES (type) = vcall_indices;
+
+	  CLASSTYPE_KEY_METHOD (type) = key_method;
+
+	  CLASSTYPE_VBASECLASSES (type) = vbase_vec;
+
+	  /* Resort the member vector.  */
+	  resort_type_member_vec (member_vec, NULL, nop, NULL);
+	}
+    }
+  else if (maybe_dup)
+    {
+      // FIXME:QOI Check matching defn
+    }
+
+  if (TYPE_LANG_SPECIFIC (type))
+    {
+      tree primary = tree_node ();
+      tree as_base = tree_node ();
+
+      if (as_base)
+	as_base = TREE_TYPE (as_base);
+
+      /* Read the vtables.  */
+      vec<tree, va_heap> *vtables = vec_chained_decls ();
+      if (vtables)
+	{
+	  unsigned len = vtables->length ();
+	  for (unsigned ix = 0; ix != len; ix++)
+	    {
+	      tree vtable = (*vtables)[ix];
+	      read_var_def (vtable, vtable);
+	    }
+	}
+
+      tree friend_classes = tree_list (false);
+      tree friend_functions = NULL_TREE;
+      for (tree *chain = &friend_functions;
+	   tree name = tree_node (); chain = &TREE_CHAIN (*chain))
+	{
+	  tree val = tree_list (false);
+	  *chain = build_tree_list (name, val);
+	}
+      tree decl_list = tree_list (true);
+
+      if (installing)
+	{
+	  CLASSTYPE_PRIMARY_BINFO (type) = primary;
+	  CLASSTYPE_AS_BASE (type) = as_base;
+
+	  if (vtables)
+	    {
+	      if (!CLASSTYPE_KEY_METHOD (type)
+		  /* Sneaky user may have defined it inline
+		     out-of-class.  */
+		  || DECL_DECLARED_INLINE_P (CLASSTYPE_KEY_METHOD (type)))
+		vec_safe_push (keyed_classes, type);
+	      unsigned len = vtables->length ();
+	      tree *chain = &CLASSTYPE_VTABLES (type);
+	      for (unsigned ix = 0; ix != len; ix++)
+		{
+		  tree vtable = (*vtables)[ix];
+		  gcc_checking_assert (!*chain);
+		  *chain = vtable;
+		  chain = &DECL_CHAIN (vtable);
+		}
+	    }
+	  CLASSTYPE_FRIEND_CLASSES (type) = friend_classes;
+	  DECL_FRIENDLIST (defn) = friend_functions;
+	  CLASSTYPE_DECL_LIST (type) = decl_list;
+
+	  for (; friend_classes; friend_classes = TREE_CHAIN (friend_classes))
+	    {
+	      tree f = TREE_VALUE (friend_classes);
+
+	      if (TYPE_P (f))
+		{
+		  CLASSTYPE_BEFRIENDING_CLASSES (f)
+		    = tree_cons (NULL_TREE, type,
+				 CLASSTYPE_BEFRIENDING_CLASSES (f));
+		  dump () && dump ("Class %N befriending %C:%N",
+				   type, TREE_CODE (f), f);
+		}
+	    }
+
+	  for (; friend_functions;
+	       friend_functions = TREE_CHAIN (friend_functions))
+	    for (tree friend_decls = TREE_VALUE (friend_functions);
+		 friend_decls; friend_decls = TREE_CHAIN (friend_decls))
+	      {
+		tree f = TREE_VALUE (friend_decls);
+		
+		DECL_BEFRIENDING_CLASSES (f)
+		  = tree_cons (NULL_TREE, type, DECL_BEFRIENDING_CLASSES (f));
+		dump () && dump ("Class %N befriending %C:%N",
+				 type, TREE_CODE (f), f);
+	      }
+	}
+
+      if (TYPE_CONTAINS_VPTR_P (type))
+	/* Read and install the thunks.  */
+	while (tree vfunc = tree_node ())
+	  {
+	    tree thunks = chained_decls ();
+	    if (installing)
+	      SET_DECL_THUNKS (vfunc, thunks);
+	  }
+
+      vec_free (vtables);
+    }
+
+  /* Propagate to all variants.  */
+  if (installing)
+    fixup_type_variants (type);
+
+  /* IS_FAKE_BASE_TYPE is inaccurate at this point, because if this is
+     the fake base, we've not hooked it into the containing class's
+     data structure yet.  Fortunately it has a unique name.  */
+  if (installing
+      && DECL_NAME (defn) != as_base_identifier
+      && (!CLASSTYPE_TEMPLATE_INFO (type)
+	  || !uses_template_parms (TI_ARGS (CLASSTYPE_TEMPLATE_INFO (type)))))
+    /* Emit debug info.  It'd be nice to know if the interface TU
+       already emitted this.  */
+    rest_of_type_compilation (type, !LOCAL_CLASS_P (type));
+
+  vec_free (fields);
+
+  return !get_overrun ();
+}
+
+void
+trees_out::write_enum_def (tree decl)
+{
+  tree type = TREE_TYPE (decl);
+
+  tree_node (TYPE_VALUES (type));
+  tree_node (TYPE_MIN_VALUE (type));
+  tree_node (TYPE_MAX_VALUE (type));
+}
+
+void
+trees_out::mark_enum_def (tree decl)
+{
+  tree type = TREE_TYPE (decl);
+
+  for (tree values = TYPE_VALUES (type); values; values = TREE_CHAIN (values))
+    {
+      tree cst = TREE_VALUE (values);
+      mark_by_value (cst);
+      /* We must mark the init to avoid circularity in tt_enum_int.  */
+      if (tree init = DECL_INITIAL (cst))
+	if (TREE_CODE (init) == INTEGER_CST)
+	  mark_by_value (init);
+    }
+}
+
+bool
+trees_in::read_enum_def (tree defn, tree maybe_template)
+{
+  tree type = TREE_TYPE (defn);
+  tree values = tree_node ();
+  tree min = tree_node ();
+  tree max = tree_node ();
+
+  if (get_overrun ())
+    return false;
+
+  tree maybe_dup = odr_duplicate (maybe_template, TYPE_VALUES (type));
+  bool installing = maybe_dup && !TYPE_VALUES (type);
+
+  if (installing)
+    {
+      TYPE_VALUES (type) = values;
+      TYPE_MIN_VALUE (type) = min;
+      TYPE_MAX_VALUE (type) = max;
+
+      rest_of_type_compilation (type, DECL_NAMESPACE_SCOPE_P (defn));
+    }
+  else if (maybe_dup)
+    {
+      tree known = TYPE_VALUES (type);
+      for (; known && values;
+	   known = TREE_CHAIN (known), values = TREE_CHAIN (values))
+	{
+	  tree known_decl = TREE_VALUE (known);
+	  tree new_decl = TREE_VALUE (values);
+
+	  if (DECL_NAME (known_decl) != DECL_NAME (new_decl))
+	    goto bad;
+	      
+	  new_decl = maybe_duplicate (new_decl);
+
+	  if (!cp_tree_equal (DECL_INITIAL (known_decl),
+			      DECL_INITIAL (new_decl)))
+	    goto bad;
+	}
+
+      if (known || values)
+	goto bad;
+
+      if (!cp_tree_equal (TYPE_MIN_VALUE (type), min)
+	  || !cp_tree_equal (TYPE_MAX_VALUE (type), max))
+	{
+	bad:;
+	  error_at (DECL_SOURCE_LOCATION (maybe_dup),
+		    "definition of %qD does not match", maybe_dup);
+	  inform (DECL_SOURCE_LOCATION (defn),
+		  "existing definition %qD", defn);
+
+	  tree known_decl = NULL_TREE, new_decl = NULL_TREE;
+
+	  if (known)
+	    known_decl = TREE_VALUE (known);
+	  if (values)
+	    new_decl = maybe_duplicate (TREE_VALUE (values));
+
+	  if (known_decl && new_decl)
+	    {
+	      inform (DECL_SOURCE_LOCATION (new_decl),
+		      "... this enumerator %qD", new_decl);
+	      inform (DECL_SOURCE_LOCATION (known_decl),
+		      "enumerator %qD does not match ...", known_decl);
+	    }
+	  else if (known_decl || new_decl)
+	    {
+	      tree extra = known_decl ? known_decl : new_decl;
+	      inform (DECL_SOURCE_LOCATION (extra),
+		      "additional enumerators beginning with %qD", extra);
+	    }
+	  else
+	    inform (DECL_SOURCE_LOCATION (maybe_dup),
+		    "enumeration range differs");
+
+	  /* Mark it bad.  */
+	  unmatched_duplicate (maybe_template);
+	}
+    }
+
+  return true;
+}
+
+/* Write out the body of DECL.  See above circularity note.  */
+
+void
+trees_out::write_definition (tree decl)
+{
+  if (streaming_p ())
+    {
+      assert_definition (decl);
+      dump ()
+	&& dump ("Writing definition %C:%N", TREE_CODE (decl), decl);
+    }
+  else
+    dump (dumper::DEPEND)
+      && dump ("Depending definition %C:%N", TREE_CODE (decl), decl);
+
+ again:
+  switch (TREE_CODE (decl))
+    {
+    default:
+      gcc_unreachable ();
+
+    case TEMPLATE_DECL:
+      decl = DECL_TEMPLATE_RESULT (decl);
+      goto again;
+
+    case FUNCTION_DECL:
+      write_function_def (decl);
+      break;
+
+    case TYPE_DECL:
+      {
+	tree type = TREE_TYPE (decl);
+	gcc_assert (TYPE_MAIN_VARIANT (type) == type
+		    && TYPE_NAME (type) == decl);
+	if (TREE_CODE (type) == ENUMERAL_TYPE)
+	  write_enum_def (decl);
+	else
+	  write_class_def (decl);
+      }
+      break;
+
+    case VAR_DECL:
+    case CONCEPT_DECL:
+      write_var_def (decl);
+      break;
+    }
+}
+
+/* Mark a declaration for by-value walking.  If DO_DEFN is true, mark
+   its body too.  */
+
+void
+trees_out::mark_declaration (tree decl, bool do_defn)
+{
+  mark_by_value (decl);
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    decl = DECL_TEMPLATE_RESULT (decl);
+
+  if (!do_defn)
+    return;
+
+  switch (TREE_CODE (decl))
+    {
+    default:
+      gcc_unreachable ();
+
+    case FUNCTION_DECL:
+      mark_function_def (decl);
+      break;
+
+    case TYPE_DECL:
+      {
+	tree type = TREE_TYPE (decl);
+	gcc_assert (TYPE_MAIN_VARIANT (type) == type
+		    && TYPE_NAME (type) == decl);
+	if (TREE_CODE (type) == ENUMERAL_TYPE)
+	  mark_enum_def (decl);
+	else
+	  mark_class_def (decl);
+      }
+      break;
+
+    case VAR_DECL:
+    case CONCEPT_DECL:
+      mark_var_def (decl);
+      break;
+    }
+}
+
+/* Read in the body of DECL.  See above circularity note.  */
+
+bool
+trees_in::read_definition (tree decl)
+{
+  dump () && dump ("Reading definition %C %N", TREE_CODE (decl), decl);
+
+  tree maybe_template = decl;
+
+ again:
+  switch (TREE_CODE (decl))
+    {
+    default:
+      break;
+
+    case TEMPLATE_DECL:
+      decl = DECL_TEMPLATE_RESULT (decl);
+      goto again;
+
+    case FUNCTION_DECL:
+      return read_function_def (decl, maybe_template);
+
+    case TYPE_DECL:
+      {
+	tree type = TREE_TYPE (decl);
+	gcc_assert (TYPE_MAIN_VARIANT (type) == type
+		    && TYPE_NAME (type) == decl);
+	if (TREE_CODE (type) == ENUMERAL_TYPE)
+	  return read_enum_def (decl, maybe_template);
+	else
+	  return read_class_def (decl, maybe_template);
+      }
+      break;
+
+    case VAR_DECL:
+    case CONCEPT_DECL:
+      return read_var_def (decl, maybe_template);
+    }
+
+  return false;
+}
+
+/* Lookup an maybe insert a slot for depset for KEY.  */
+
+depset **
+depset::hash::entity_slot (tree entity, bool insert)
+{
+  traits::compare_type key (entity, NULL);
+  depset **slot = find_slot_with_hash (key, traits::hash (key),
+				       insert ? INSERT : NO_INSERT);
+
+  return slot;
+}
+
+depset **
+depset::hash::binding_slot (tree ctx, tree name, bool insert)
+{
+  traits::compare_type key (ctx, name);
+  depset **slot = find_slot_with_hash (key, traits::hash (key),
+				       insert ? INSERT : NO_INSERT);
+
+  return slot;
+}
+
+depset *
+depset::hash::find_dependency (tree decl)
+{
+  depset **slot = entity_slot (decl, false);
+
+  return slot ? *slot : NULL;
+}
+
+depset *
+depset::hash::find_binding (tree ctx, tree name)
+{
+  depset **slot = binding_slot (ctx, name, false);
+
+  return slot ? *slot : NULL;
+}
+
+/* DECL is a newly discovered dependency.  Create the depset, if it
+   doesn't already exist.  Add it to the worklist if so.
+
+   DECL will be an OVL_USING_P OVERLOAD, if it's from a binding that's
+   a using decl.
+
+   We do not have to worry about adding the same dependency more than
+   once.  First it's harmless, but secondly the TREE_VISITED marking
+   prevents us wanting to do it anyway.  */
+
+depset *
+depset::hash::make_dependency (tree decl, entity_kind ek)
+{
+  /* Make sure we're being told consistent information.  */
+  gcc_checking_assert ((ek == EK_NAMESPACE)
+		       == (TREE_CODE (decl) == NAMESPACE_DECL
+			   && !DECL_NAMESPACE_ALIAS (decl)));
+  gcc_checking_assert (ek != EK_BINDING && ek != EK_REDIRECT);
+  gcc_checking_assert (TREE_CODE (decl) != FIELD_DECL
+		       && (TREE_CODE (decl) != USING_DECL
+			   || TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL));
+  gcc_checking_assert (!is_key_order ());
+  if (ek == EK_USING)
+    gcc_checking_assert (TREE_CODE (decl) == OVERLOAD);
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    {
+      /* The template should have copied these from its result decl.  */
+      tree res = DECL_TEMPLATE_RESULT (decl);
+
+      gcc_checking_assert (DECL_MODULE_EXPORT_P (decl)
+			   == DECL_MODULE_EXPORT_P (res));
+      if (DECL_LANG_SPECIFIC (res))
+	{
+	  gcc_checking_assert (DECL_MODULE_PURVIEW_P (decl)
+			       == DECL_MODULE_PURVIEW_P (res));
+	  gcc_checking_assert ((DECL_MODULE_IMPORT_P (decl)
+				== DECL_MODULE_IMPORT_P (res)));
+	}
+    }
+
+  depset **slot = entity_slot (decl, true);
+  depset *dep = *slot;
+  bool for_binding = ek == EK_FOR_BINDING;
+
+  if (!dep)
+    {
+      /* We should only be creating dependencies when initializing
+	 non-DECL entries, or when discovering dependencies.  */
+      gcc_checking_assert (ek != EK_DECL || current);
+
+      if (DECL_IMPLICIT_TYPEDEF_P (decl)
+	  /* ... not an enum, for instance.  */
+	  && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))
+	  && TYPE_LANG_SPECIFIC (TREE_TYPE (decl))
+	  && CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl)) == 2)
+	{
+	  /* A partial or explicit specialization. Partial
+	     specializations constrained by requires clauses are not
+	     in the hash table, because they have the same set of
+	     template parameters as their general template:
+
+	     template<typename T> class silly;
+	     template<typename T> requires true class silly {};
+
+	     We need to find them, insert their TEMPLATE_DECL in the
+	     dep_hash, and then convert the dep we just found into a
+	     redirect.  */
+
+	  tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl));
+	  tree tmpl = TI_TEMPLATE (ti);
+	  tree partial = NULL_TREE;
+	  for (tree spec = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
+	       spec; spec = TREE_CHAIN (spec))
+	    if (DECL_TEMPLATE_RESULT (TREE_VALUE (spec)) == decl)
+	      {
+		partial = TREE_VALUE (spec);
+		break;
+	      }
+
+	  if (partial)
+	    {
+	      depset *tmpl_dep = make_dependency (partial, EK_DECL);
+	      
+	      gcc_checking_assert (tmpl_dep->get_entity_kind () == EK_DECL);
+	      return add_partial_redirect (tmpl_dep, slot);
+	    }
+	}
+
+      bool has_def = ek != EK_USING && has_definition (decl);
+      if (ek > EK_BINDING)
+	ek = EK_DECL;
+
+      /* The only OVERLOADS we should see are USING decls from
+	 bindings.  */
+      *slot = dep = make_entity (decl, ek, has_def);
+
+      if (TREE_CODE (decl) == TEMPLATE_DECL)
+	{
+	  if (DECL_ALIAS_TEMPLATE_P (decl) && DECL_TEMPLATE_INFO (decl))
+	    dep->set_flag_bit<DB_ALIAS_TMPL_INST_BIT> ();
+	  else 
+	    /* The template_result should otherwise not be in the
+	       table.  */
+	    gcc_checking_assert
+	      (!entity_slot (DECL_TEMPLATE_RESULT (decl), false));
+	}
+
+      if (ek != EK_USING
+	  && DECL_LANG_SPECIFIC (decl)
+	  && DECL_MODULE_IMPORT_P (decl))
+	{
+	  /* Store the module number and index in cluster/section, so
+	     we don't have to look them up again.  */
+	  unsigned index = import_entity_index (decl);
+	  module_state *from = import_entity_module (index);
+	  // Remap will be zero for imports from partitions, which we
+	  // want to treat asif declared in this TU.
+	  if (from->remap)
+	    {
+	      dep->cluster = index - from->entity_lwm;
+	      dep->section = from->remap;
+	      dep->set_flag_bit<DB_IMPORTED_BIT> ();
+	    }
+	}
+
+      if (ek == EK_DECL
+	  && !dep->is_import ()
+	  && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL
+	  && !(TREE_CODE (decl) == TEMPLATE_DECL
+	       && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)))
+	{
+	  tree ctx = CP_DECL_CONTEXT (decl);
+	  tree not_tmpl = STRIP_TEMPLATE (decl);
+
+	  if (!TREE_PUBLIC (ctx))
+	    /* Member of internal namespace.  */
+	    dep->set_flag_bit<DB_IS_INTERNAL_BIT> ();
+	  else if (VAR_OR_FUNCTION_DECL_P (not_tmpl)
+		   && DECL_THIS_STATIC (not_tmpl))
+	    {
+	      /* An internal decl.  This is ok in a GM entity.  */
+	      if (!(header_module_p ()
+		    || !DECL_LANG_SPECIFIC (not_tmpl)
+		    || !DECL_MODULE_PURVIEW_P (not_tmpl)))
+		dep->set_flag_bit<DB_IS_INTERNAL_BIT> ();
+	    }
+
+	}
+
+      if (!dep->is_import ())
+	worklist.safe_push (dep);
+    }
+
+  dump (dumper::DEPEND)
+    && dump ("%s on %s %C:%N found",
+	     ek == EK_REDIRECT ? "Redirect"
+	     : for_binding ? "Binding" : "Dependency",
+	     dep->entity_kind_name (), TREE_CODE (decl), decl);
+
+  return dep;
+}
+
+/* DEP is a newly discovered dependency.  Append it to current's
+   depset.  */
+
+void
+depset::hash::add_dependency (depset *dep)
+{
+  gcc_checking_assert (current && !is_key_order ());
+  current->deps.safe_push (dep);
+
+  if (dep->is_internal () && !current->is_internal ())
+    current->set_flag_bit<DB_REFS_INTERNAL_BIT> ();
+
+  if (current->get_entity_kind () == EK_USING
+      && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ())
+      && TREE_CODE (TREE_TYPE (dep->get_entity ())) == ENUMERAL_TYPE)
+    {
+      /* CURRENT is an unwrapped using-decl and DECL is an enum's
+	 implicit typedef.  Is CURRENT a member of the enum?  */
+      tree c_decl = OVL_FUNCTION (current->get_entity ());
+
+      if (TREE_CODE (c_decl) == CONST_DECL
+	  && (current->deps[0]->get_entity ()
+	      == CP_DECL_CONTEXT (dep->get_entity ())))
+	/* Make DECL depend on CURRENT.  */
+	dep->deps.safe_push (current);
+    }
+
+  if (dep->is_unreached ())
+    {
+      /* The dependency is reachable now.  */
+      reached_unreached = true;
+      dep->clear_flag_bit<DB_UNREACHED_BIT> ();
+      dump (dumper::DEPEND)
+	&& dump ("Reaching unreached %s %C:%N", dep->entity_kind_name (),
+		 TREE_CODE (dep->get_entity ()), dep->get_entity ());
+    }
+}
+
+depset *
+depset::hash::add_dependency (tree decl, entity_kind ek)
+{
+  depset *dep;
+
+  if (is_key_order ())
+    {
+      dep = find_dependency (decl);
+      if (dep)
+	{
+	  current->deps.safe_push (dep);
+	  dump (dumper::MERGE)
+	    && dump ("Key dependency on %s %C:%N found",
+		     dep->entity_kind_name (), TREE_CODE (decl), decl);
+	}
+      else
+	{
+	  /* It's not a mergeable decl, look for it in the original
+	     table.  */
+	  dep = chain->find_dependency (decl);
+	  gcc_checking_assert (dep);
+	}
+    }
+  else
+    {
+      dep = make_dependency (decl, ek);
+      if (dep->get_entity_kind () != EK_REDIRECT)
+	add_dependency (dep);
+    }
+
+  return dep;
+}
+
+void
+depset::hash::add_namespace_context (depset *dep, tree ns)
+{
+  depset *ns_dep = make_dependency (ns, depset::EK_NAMESPACE);
+  dep->deps.safe_push (ns_dep);
+
+  /* Mark it as special if imported so we don't walk connect when
+     SCCing.  */
+  if (!dep->is_binding () && ns_dep->is_import ())
+    dep->set_special ();
+}
+
+struct add_binding_data
+{
+  tree ns;
+  bitmap partitions;
+  depset *binding;
+  depset::hash *hash;
+  bool met_namespace;
+};
+
+bool
+depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
+{
+  auto data = static_cast <add_binding_data *> (data_);
+
+  if (TREE_CODE (decl) != NAMESPACE_DECL || DECL_NAMESPACE_ALIAS (decl))
+    {
+      tree inner = decl;
+
+      if (TREE_CODE (inner) == CONST_DECL
+	  && TREE_CODE (DECL_CONTEXT (inner)) == ENUMERAL_TYPE)
+	inner = TYPE_NAME (DECL_CONTEXT (inner));
+      else if (TREE_CODE (inner) == TEMPLATE_DECL)
+	inner = DECL_TEMPLATE_RESULT (inner);
+
+      if (!DECL_LANG_SPECIFIC (inner) || !DECL_MODULE_PURVIEW_P (inner))
+	/* Ignore global module fragment entities.  */
+	return false;
+
+      if (VAR_OR_FUNCTION_DECL_P (inner)
+	  && DECL_THIS_STATIC (inner))
+	{
+	  if (!header_module_p ())
+	    /* Ignore internal-linkage entitites.  */
+	    return false;
+	}
+
+      if ((TREE_CODE (decl) == VAR_DECL
+	   || TREE_CODE (decl) == TYPE_DECL)
+	  && DECL_TINFO_P (decl))
+	/* Ignore TINFO things.  */
+	return false;
+
+      if (!(flags & WMB_Using) && CP_DECL_CONTEXT (decl) != data->ns)
+	{
+	  /* A using that lost its wrapper or an unscoped enum
+	     constant.  */
+	  flags = WMB_Flags (flags | WMB_Using);
+	  if (DECL_MODULE_EXPORT_P (TREE_CODE (decl) == CONST_DECL
+				    ? TYPE_NAME (TREE_TYPE (decl))
+				    : STRIP_TEMPLATE (decl)))
+	    flags = WMB_Flags (flags | WMB_Export);
+	}
+
+      if (!data->binding)
+	/* No binding to check.  */;
+      else if (flags & WMB_Using)
+	{
+	  /* Look in the binding to see if we already have this
+	     using.  */
+	  for (unsigned ix = data->binding->deps.length (); --ix;)
+	    {
+	      depset *d = data->binding->deps[ix];
+	      if (d->get_entity_kind () == EK_USING
+		  && OVL_FUNCTION (d->get_entity ()) == decl)
+		{
+		  if (!(flags & WMB_Hidden))
+		    d->clear_hidden_binding ();
+		  if (flags & WMB_Export)
+		    OVL_EXPORT_P (d->get_entity ()) = true;
+		  return false;
+		}
+	    }
+	}
+      else if (flags & WMB_Dups)
+	{
+	  /* Look in the binding to see if we already have this decl.  */
+	  for (unsigned ix = data->binding->deps.length (); --ix;)
+	    {
+	      depset *d = data->binding->deps[ix];
+	      if (d->get_entity () == decl)
+		{
+		  if (!(flags & WMB_Hidden))
+		    d->clear_hidden_binding ();
+		  return false;
+		}
+	    }
+	}
+
+      /* We're adding something.  */
+      if (!data->binding)
+	{
+	  data->binding = make_binding (data->ns, DECL_NAME (decl));
+	  data->hash->add_namespace_context (data->binding, data->ns);
+
+	  depset **slot = data->hash->binding_slot (data->ns,
+						    DECL_NAME (decl), true);
+	  gcc_checking_assert (!*slot);
+	  *slot = data->binding;
+	}
+
+      if (flags & WMB_Using)
+	{
+	  decl = ovl_make (decl, NULL_TREE);
+	  if (flags & WMB_Export)
+	    OVL_EXPORT_P (decl) = true;
+	}
+
+      depset *dep = data->hash->make_dependency
+	(decl, flags & WMB_Using ? EK_USING : EK_FOR_BINDING);
+      if (flags & WMB_Hidden)
+	dep->set_hidden_binding ();
+      data->binding->deps.safe_push (dep);
+      /* Binding and contents are mutually dependent.  */
+      dep->deps.safe_push (data->binding);
+
+      return true;
+    }
+  else if (DECL_NAME (decl) && !data->met_namespace)
+    {
+      /* Namespace, walk exactly once.  */
+      gcc_checking_assert (TREE_PUBLIC (decl));
+      data->met_namespace = true;
+      if (data->hash->add_namespace_entities (decl, data->partitions)
+	  || DECL_MODULE_EXPORT_P (decl))
+	{
+	  data->hash->make_dependency (decl, depset::EK_NAMESPACE);
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Recursively find all the namespace bindings of NS.
+   Add a depset for every binding that contains an export or
+   module-linkage entity.  Add a defining depset for every such decl
+   that we need to write a definition.  Such defining depsets depend
+   on the binding depset.  Returns true if we contain something
+   explicitly exported.  */
+
+bool
+depset::hash::add_namespace_entities (tree ns, bitmap partitions)
+{
+  dump () && dump ("Looking for writables in %N", ns);
+  dump.indent ();
+
+  unsigned count = 0;
+  add_binding_data data;
+  data.ns = ns;
+  data.partitions = partitions;
+  data.hash = this;
+
+  hash_table<named_decl_hash>::iterator end
+    (DECL_NAMESPACE_BINDINGS (ns)->end ());
+  for (hash_table<named_decl_hash>::iterator iter
+	 (DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter)
+    {
+      data.binding = nullptr;
+      data.met_namespace = false;
+      if (walk_module_binding (*iter, partitions, add_binding_entity, &data))
+	count++;
+    }
+
+  if (count)
+    dump () && dump ("Found %u entries", count);
+  dump.outdent ();
+
+  return count != 0;
+}
+
+/* Add the members of imported classes that we defined in this TU.
+   This will also include lazily created implicit member function
+   declarations.  (All others will be definitions.)  */
+
+void
+depset::hash::add_class_entities (vec<tree, va_gc> *class_members)
+{
+  for (unsigned ix = 0; ix != class_members->length (); ix++)
+    {
+      tree defn = (*class_members)[ix];
+      depset *dep = make_dependency (defn, EK_INNER_DECL);
+
+      if (dep->get_entity_kind () == EK_REDIRECT)
+	dep = dep->deps[0];
+
+      /* Only non-instantiations need marking as members.  */
+      if (dep->get_entity_kind () == EK_DECL)
+	dep->set_flag_bit <DB_IS_MEMBER_BIT> ();
+    }
+}
+
+/* We add the partial & explicit specializations, and the explicit
+   instantiations.  */
+
+static void
+specialization_add (bool decl_p, spec_entry *entry, void *data_)
+{
+  vec<spec_entry *> *data = reinterpret_cast <vec<spec_entry *> *> (data_);
+
+  if (!decl_p)
+    {
+      /* We exclusively use decls to locate things.  Make sure there's
+	 no mismatch between the two specialization tables we keep.
+	 pt.c optimizes instantiation lookup using a complicated
+	 heuristic.  We don't attempt to replicate that algorithm, but
+	 observe its behaviour and reproduce it upon read back.  */
+
+       gcc_checking_assert (DECL_ALIAS_TEMPLATE_P (entry->tmpl)
+			   || TREE_CODE (entry->spec) == ENUMERAL_TYPE
+			   || DECL_CLASS_TEMPLATE_P (entry->tmpl));
+
+       /* Only alias templates can appear in both tables (and
+	  if they're in the type table they must also be in the decl table).  */
+       gcc_checking_assert (!check_mergeable_specialization (true, entry)
+			    == (decl_p || !DECL_ALIAS_TEMPLATE_P (entry->tmpl)));
+    }
+  else if (VAR_OR_FUNCTION_DECL_P (entry->spec))
+    gcc_checking_assert (!DECL_LOCAL_DECL_P (entry->spec));
+
+  data->safe_push (entry);
+}
+
+/* Arbitrary stable comparison.  */
+
+static int
+specialization_cmp (const void *a_, const void *b_)
+{
+  const spec_entry *ea = *reinterpret_cast<const spec_entry *const *> (a_);
+  const spec_entry *eb = *reinterpret_cast<const spec_entry *const *> (b_);
+
+  if (ea == eb)
+    return 0;
+
+  tree a = ea->spec;
+  tree b = eb->spec;
+  if (TYPE_P (a))
+    {
+      a = TYPE_NAME (a);
+      b = TYPE_NAME (b);
+    }
+
+  if (a == b)
+    /* This can happen with friend specializations.  Just order by
+       entry address.  See note in depset_cmp.  */
+    return ea < eb ? -1 : +1;
+
+  return DECL_UID (a) < DECL_UID (b) ? -1 : +1;
+}
+
+/* Insert a redirect for the DECL_TEMPLATE_RESULT of a partial
+   specialization, as we're unable to go from there to here (without
+   repeating the DECL_TEMPLATE_SPECIALIZATIONS walk for *every*
+   dependency add.  */
+
+depset *
+depset::hash::add_partial_redirect (depset *partial, depset **slot)
+{
+  partial->set_flag_bit<DB_PARTIAL_BIT> ();
+
+  tree inner = DECL_TEMPLATE_RESULT (partial->get_entity ());
+  depset *redirect = make_entity (inner, EK_REDIRECT);
+
+  /* Redirects are never reached -- always snap to their target.  */
+  redirect->set_flag_bit<DB_UNREACHED_BIT> ();
+
+  if (!slot)
+    slot = entity_slot (inner, true);
+  gcc_checking_assert (!*slot);
+  *slot = redirect;
+  redirect->deps.safe_push (partial);
+
+  return redirect;
+}
+
+/* We add all kinds of specialializations.  Implicit specializations
+   should only streamed and walked if they are reachable from
+   elsewhere.  Hence the UNREACHED flag.  This is making the
+   assumption that it is cheaper to reinstantiate them on demand
+   elsewhere, rather than stream them in when we instantiate their
+   general template.  Also, if we do stream them, we can only do that
+   if they are not internal (which they can become if they themselves
+   touch an internal entity?).  */
+
+void
+depset::hash::add_specializations (bool decl_p)
+{
+  vec<spec_entry *> data;
+  data.create (100);
+  walk_specializations (decl_p, specialization_add, &data);
+  data.qsort (specialization_cmp);
+  while (data.length ())
+    {
+      spec_entry *entry = data.pop ();
+      tree spec = entry->spec;
+      bool is_partial = false;
+      int use_tpl = 0;
+      bool is_alias = false;
+      bool is_friend = false;
+
+      if (decl_p && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl))
+	/* A friend of a template.  This is keyed to the
+	   instantiation.  */
+	is_friend = true;
+
+      if (!decl_p && DECL_ALIAS_TEMPLATE_P (entry->tmpl))
+	{
+	  spec = TYPE_NAME (spec);
+	  is_alias = true;
+	}
+
+      if (decl_p || is_alias)
+	{
+	  if (tree ti = DECL_TEMPLATE_INFO (spec))
+	    {
+	      tree tmpl = TI_TEMPLATE (ti);
+
+	      use_tpl = DECL_USE_TEMPLATE (spec);
+	      if (spec == DECL_TEMPLATE_RESULT (tmpl))
+		{
+		  spec = tmpl;
+		  gcc_checking_assert (DECL_USE_TEMPLATE (spec) == use_tpl);
+		}
+	      else if (is_friend)
+		{
+		  if (TI_TEMPLATE (ti) != entry->tmpl
+		      || !template_args_equal (TI_ARGS (ti), entry->tmpl))
+		    goto template_friend;
+		}
+	    }
+	  else
+	    {
+	    template_friend:;
+	      gcc_checking_assert (is_friend);
+	      /* This is a friend of a template class, but not the one
+		 that generated entry->spec itself (i.e. it's an
+		 equivalent clone).  We do not need to record
+		 this.  */
+	      continue;
+	    }
+	}
+      else
+	{
+	  if (TREE_CODE (spec) == ENUMERAL_TYPE)
+	    {
+	      tree ctx = DECL_CONTEXT (TYPE_NAME (spec));
+
+	      if (TYPE_P (ctx))
+		use_tpl = CLASSTYPE_USE_TEMPLATE (ctx);
+	      else
+		use_tpl = DECL_USE_TEMPLATE (ctx);
+	    }
+	  else
+	    {
+	      use_tpl = CLASSTYPE_USE_TEMPLATE (spec);
+
+	      tree partial = DECL_TEMPLATE_SPECIALIZATIONS (entry->tmpl);
+	      for (; partial; partial = TREE_CHAIN (partial))
+		if (TREE_TYPE (partial) == spec)
+		  break;
+
+	      if (partial)
+		{
+		  gcc_checking_assert (entry->args == TREE_PURPOSE (partial));
+		  is_partial = true;
+		  /* Get the TEMPLATE_DECL for the partial
+		     specialization.  */
+		  spec = TREE_VALUE (partial);
+		  gcc_assert (DECL_USE_TEMPLATE (spec) == use_tpl);
+		}
+	    }
+
+	  if (!is_partial)
+	    {
+	      tree ti = TYPE_TEMPLATE_INFO (spec);
+	      tree tmpl = TI_TEMPLATE (ti);
+
+	      spec = TYPE_NAME (spec);
+	      if (spec == DECL_TEMPLATE_RESULT (tmpl))
+		{
+		  spec = tmpl;
+		  use_tpl = DECL_USE_TEMPLATE (spec);
+		}
+	    }
+	}
+
+      bool needs_reaching = false;
+      if (use_tpl == 1)
+	/* Implicit instantiations only walked if we reach them.  */
+	needs_reaching = true;
+      else if (!DECL_LANG_SPECIFIC (spec)
+	       || !DECL_MODULE_PURVIEW_P (spec))
+	/* Likewise, GMF explicit or partial specializations.  */
+	needs_reaching = true;
+
+#if false && CHECKING_P
+      /* The instantiation isn't always on
+	 DECL_TEMPLATE_INSTANTIATIONS, */
+      // FIXME: we probably need to remember this information?
+      /* Verify the specialization is on the
+	 DECL_TEMPLATE_INSTANTIATIONS of the template.  */
+      for (tree cons = DECL_TEMPLATE_INSTANTIATIONS (entry->tmpl);
+	   cons; cons = TREE_CHAIN (cons))
+	if (TREE_VALUE (cons) == entry->spec)
+	  {
+	    gcc_assert (entry->args == TREE_PURPOSE (cons));
+	    goto have_spec;
+	  }
+      gcc_unreachable ();
+    have_spec:;
+#endif
+
+      depset *dep = make_dependency (spec, depset::EK_SPECIALIZATION);
+      if (dep->is_special ())
+	{
+	  /* An already located specialization, this must be the TYPE
+	     corresponding to an alias_decl we found in the decl
+	     table.  */
+	  spec_entry *other = reinterpret_cast <spec_entry *> (dep->deps[0]);
+	  gcc_checking_assert (!decl_p && is_alias && !dep->is_type_spec ());
+	  gcc_checking_assert (other->tmpl == entry->tmpl
+			       && template_args_equal (other->args, entry->args)
+			       && TREE_TYPE (other->spec) == entry->spec);
+	  dep->set_flag_bit<DB_ALIAS_SPEC_BIT> ();
+	}
+      else
+	{
+	  gcc_checking_assert (decl_p || !is_alias);
+	  dep->set_special ();
+	  dep->deps.safe_push (reinterpret_cast<depset *> (entry));
+	  if (needs_reaching)
+	    dep->set_flag_bit<DB_UNREACHED_BIT> ();
+	  if (is_partial)
+	    add_partial_redirect (dep);
+	  if (!decl_p)
+	    dep->set_flag_bit<DB_TYPE_SPEC_BIT> ();
+	  if (is_friend)
+	    dep->set_flag_bit<DB_FRIEND_SPEC_BIT> ();
+	}
+    }
+  data.release ();
+}
+
+/* Add a depset into the mergeable hash.  */
+
+void
+depset::hash::add_mergeable (depset *mergeable)
+{
+  gcc_checking_assert (is_key_order ());
+  entity_kind ek = mergeable->get_entity_kind ();
+  tree decl = mergeable->get_entity ();
+  gcc_checking_assert (ek == EK_DECL || ek == EK_SPECIALIZATION);
+
+  depset **slot = entity_slot (decl, true);
+  gcc_checking_assert (!*slot);
+  depset *dep = make_entity (decl, ek);
+  *slot = dep;
+
+  worklist.safe_push (dep);
+
+  /* So we can locate the mergeable depset this depset refers to,
+     mark the first dep.  */
+  dep->set_special ();
+  dep->deps.safe_push (mergeable);
+}
+
+/* Iteratively find dependencies.  During the walk we may find more
+   entries on the same binding that need walking.  */
+
+void
+depset::hash::find_dependencies ()
+{
+  trees_out walker (NULL, NULL, *this);
+  vec<depset *> unreached;
+  unreached.create (worklist.length ());
+
+  for (;;)
+    {
+      reached_unreached = false;
+      while (worklist.length ())
+	{
+	  depset *item = worklist.pop ();
+
+	  gcc_checking_assert (!item->is_binding ());
+	  if (item->is_unreached ())
+	    unreached.quick_push (item);
+	  else
+	    {
+	      current = item;
+	      tree decl = current->get_entity ();
+	      dump (is_key_order () ? dumper::MERGE : dumper::DEPEND)
+		&& dump ("Dependencies of %s %C:%N",
+			 is_key_order () ? "key-order"
+			 : current->entity_kind_name (), TREE_CODE (decl), decl);
+	      dump.indent ();
+	      walker.begin ();
+	      if (current->get_entity_kind () == EK_USING)
+		walker.tree_node (OVL_FUNCTION (decl));
+	      else if (TREE_VISITED (decl))
+		/* A global tree.  */;
+	      else if (TREE_CODE (decl) == NAMESPACE_DECL
+		       && !DECL_NAMESPACE_ALIAS (decl))
+		add_namespace_context (current, CP_DECL_CONTEXT (decl));
+	      else
+		{
+		  walker.mark_declaration (decl, current->has_defn ());
+
+		  // FIXME: Perhaps p1815 makes this redundant? Or at
+		  // least simplifies it.  Voldemort types are only
+		  // ever emissable when containing (inline) function
+		  // definition is emitted?
+		  /* Turn the Sneakoscope on when depending the decl.  */
+		  sneakoscope = true;
+		  walker.decl_value (decl, current);
+		  sneakoscope = false;
+		  if (current->has_defn ())
+		    walker.write_definition (decl);
+		}
+	      walker.end ();
+
+	      if (!walker.is_key_order ()
+		  && TREE_CODE (decl) == TEMPLATE_DECL
+		  && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
+		/* Mark all the explicit & partial specializations as
+		   reachable.  */
+		for (tree cons = DECL_TEMPLATE_INSTANTIATIONS (decl);
+		     cons; cons = TREE_CHAIN (cons))
+		  {
+		    tree spec = TREE_VALUE (cons);
+		    if (TYPE_P (spec))
+		      spec = TYPE_NAME (spec);
+		    int use_tpl;
+		    node_template_info (spec, use_tpl);
+		    if (use_tpl & 2)
+		      {
+			depset *spec_dep = find_dependency (spec);
+			if (spec_dep->get_entity_kind () == EK_REDIRECT)
+			  spec_dep = spec_dep->deps[0];
+			gcc_checking_assert (spec_dep->get_entity_kind ()
+					     == EK_SPECIALIZATION);
+			if (spec_dep->is_unreached ())
+			  {
+			    reached_unreached = true;
+			    spec_dep->clear_flag_bit<DB_UNREACHED_BIT> ();
+			    dump (dumper::DEPEND)
+			      && dump ("Reaching unreached specialization"
+				       " %C:%N", TREE_CODE (spec), spec);
+			  }
+		      }
+		  }
+
+	      dump.outdent ();
+	      current = NULL;
+	    }
+	}
+
+      if (!reached_unreached)
+	break;
+
+      /* It's possible the we reached the unreached before we
+	 processed it in the above loop, so we'll be doing this an
+	 extra time.  However, to avoid that we have to do some
+	 bit shuffling that also involves a scan of the list.
+	 Swings & roundabouts I guess.  */
+      std::swap (worklist, unreached);
+    }
+
+  unreached.release ();
+}
+
+/* Compare two entries of a single binding.  TYPE_DECL before
+   non-exported before exported.  */
+
+static int
+binding_cmp (const void *a_, const void *b_)
+{
+  depset *a = *(depset *const *)a_;
+  depset *b = *(depset *const *)b_;
+
+  tree a_ent = a->get_entity ();
+  tree b_ent = b->get_entity ();
+  gcc_checking_assert (a_ent != b_ent
+		       && !a->is_binding ()
+		       && !b->is_binding ());
+
+  /* Implicit typedefs come first.  */
+  bool a_implicit = DECL_IMPLICIT_TYPEDEF_P (a_ent);
+  bool b_implicit = DECL_IMPLICIT_TYPEDEF_P (b_ent);
+  if (a_implicit || b_implicit)
+    {
+      /* A binding with two implicit type decls?  That's unpossible!  */
+      gcc_checking_assert (!(a_implicit && b_implicit));
+      return a_implicit ? -1 : +1;  /* Implicit first.  */
+    }
+
+  /* Hidden before non-hidden.  */
+  bool a_hidden = a->is_hidden ();
+  bool b_hidden = b->is_hidden ();
+  if (a_hidden != b_hidden)
+    return a_hidden ? -1 : +1;
+
+  bool a_using = a->get_entity_kind () == depset::EK_USING;
+  bool a_export;
+  if (a_using)
+    {
+      a_export = OVL_EXPORT_P (a_ent);
+      a_ent = OVL_FUNCTION (a_ent);
+    }
+  else
+    a_export = DECL_MODULE_EXPORT_P (TREE_CODE (a_ent) == CONST_DECL
+				     ? TYPE_NAME (TREE_TYPE (a_ent))
+				     : STRIP_TEMPLATE (a_ent));
+  
+  bool b_using = b->get_entity_kind () == depset::EK_USING;
+  bool b_export;
+  if (b_using)
+    {
+      b_export = OVL_EXPORT_P (b_ent);
+      b_ent = OVL_FUNCTION (b_ent);
+    }
+  else
+    b_export = DECL_MODULE_EXPORT_P (TREE_CODE (b_ent) == CONST_DECL
+				     ? TYPE_NAME (TREE_TYPE (b_ent))
+				     : STRIP_TEMPLATE (b_ent));
+
+  /* Non-exports before exports.  */
+  if (a_export != b_export)
+    return a_export ? +1 : -1;
+
+  /* At this point we don't care, but want a stable sort.  */
+
+  if (a_using != b_using)
+    /* using first.  */
+    return a_using? -1 : +1;
+
+  return DECL_UID (a_ent) < DECL_UID (b_ent) ? -1 : +1;
+}
+
+/* Sort the bindings, issue errors about bad internal refs.  */
+
+bool
+depset::hash::finalize_dependencies ()
+{
+  bool ok = true;
+  depset::hash::iterator end (this->end ());
+  for (depset::hash::iterator iter (begin ()); iter != end; ++iter)
+    {
+      depset *dep = *iter;
+      if (dep->is_binding ())
+	{
+	  /* Keep the containing namespace dep first.  */
+	  gcc_checking_assert (dep->deps.length () > 1
+			       && (dep->deps[0]->get_entity_kind ()
+				   == EK_NAMESPACE)
+			       && (dep->deps[0]->get_entity ()
+				   == dep->get_entity ()));
+	  if (dep->deps.length () > 2)
+	    gcc_qsort (&dep->deps[1], dep->deps.length () - 1,
+		       sizeof (dep->deps[1]), binding_cmp);
+	}
+      else if (dep->refs_internal ())
+	{
+	  for (unsigned ix = dep->deps.length (); ix--;)
+	    {
+	      depset *rdep = dep->deps[ix];
+	      if (rdep->is_internal ())
+		{
+		  // FIXME:QOI Better location information?  We're
+		  // losing, so it doesn't matter about efficiency
+		  tree decl = dep->get_entity ();
+		  error_at (DECL_SOURCE_LOCATION (decl),
+			    "%q#D references internal linkage entity %q#D",
+			    decl, rdep->get_entity ());
+		  break;
+		}
+	    }
+	  ok = false;
+	}
+    }
+
+  return ok;
+}
+
+/* Core of TARJAN's algorithm to find Strongly Connected Components
+   within a graph.  See https://en.wikipedia.org/wiki/
+   Tarjan%27s_strongly_connected_components_algorithm for details.
+
+   We use depset::section as lowlink.  Completed nodes have
+   depset::cluster containing the cluster number, with the top
+   bit set.
+
+   A useful property is that the output vector is a reverse
+   topological sort of the resulting DAG.  In our case that means
+   dependent SCCs are found before their dependers.  We make use of
+   that property.  */
+
+void
+depset::tarjan::connect (depset *v)
+{
+  gcc_checking_assert (v->is_binding ()
+		       || !(v->is_unreached () || v->is_import ()));
+
+  v->cluster = v->section = ++index;
+  stack.safe_push (v);
+
+  /* Walk all our dependencies, ignore a first marked slot  */
+  for (unsigned ix = v->is_special (); ix != v->deps.length (); ix++)
+    {
+      depset *dep = v->deps[ix];
+
+      if (dep->is_binding () || !dep->is_import ())
+	{
+	  unsigned lwm = dep->cluster;
+
+	  if (!dep->cluster)
+	    {
+	      /* A new node.  Connect it.  */
+	      connect (dep);
+	      lwm = dep->section;
+	    }
+
+	  if (dep->section && v->section > lwm)
+	    v->section = lwm;
+	}
+    }
+
+  if (v->section == v->cluster)
+    {
+      /* Root of a new SCC.  Push all the members onto the result list. */
+      unsigned num = v->cluster;
+      depset *p;
+      do
+	{
+	  p = stack.pop ();
+	  p->cluster = num;
+	  p->section = 0;
+	  result.quick_push (p);
+	}
+      while (p != v);
+    }
+}
+
+/* Compare two depsets.  The specific ordering is unimportant, we're
+   just trying to get consistency.  */
+
+static int
+depset_cmp (const void *a_, const void *b_)
+{
+  depset *a = *(depset *const *)a_;
+  depset *b = *(depset *const *)b_;
+
+  depset::entity_kind a_kind = a->get_entity_kind ();
+  depset::entity_kind b_kind = b->get_entity_kind ();
+
+  if  (a_kind != b_kind)
+    /* Different entity kinds, order by that.  */
+    return a_kind < b_kind ? -1 : +1;
+  
+  tree a_decl = a->get_entity ();
+  tree b_decl = b->get_entity ();
+  if (a_kind == depset::EK_USING)
+    {
+      /* If one is a using, the other must be too.  */
+      a_decl = OVL_FUNCTION (a_decl);
+      b_decl = OVL_FUNCTION (b_decl);
+    }
+
+  if (a_decl != b_decl)
+    /* Different entities, order by their UID.  */
+    return DECL_UID (a_decl) < DECL_UID (b_decl) ? -1 : +1;
+
+  if (a_kind == depset::EK_BINDING)
+    {
+      /* Both are bindings.  Order by identifier hash.  */
+      gcc_checking_assert (a->get_name () != b->get_name ());
+      return (IDENTIFIER_HASH_VALUE (a->get_name ())
+	      < IDENTIFIER_HASH_VALUE (b->get_name ())
+	      ? -1 : +1);
+    }
+
+  /* They are the same decl.  This can happen with two using decls
+     pointing to the same target.  The best we can aim for is
+     consistently telling qsort how to order them.  Hopefully we'll
+     never have to debug a case that depends on this.  Oh, who am I
+     kidding?  Good luck.  */
+  gcc_checking_assert (a_kind == depset::EK_USING);
+
+  /* Order by depset address.  Not the best, but it is something.  */
+  return a < b ? -1 : +1;
+}
+
+/* Sort the clusters in SCC such that those that depend on one another
+   are placed later.   */
+
+// FIXME: I am not convinced this is needed and, if needed,
+// sufficient.  We emit the decls in this order but that emission
+// could walk into later decls (from the body of the decl, or default
+// arg-like things).  Why doesn't that walk do the right thing?  And
+// if it DTRT why do we need to sort here -- won't things naturally
+// work?  I think part of the issue is that when we're going to refer
+// to an entity by name, and that entity is in the same cluster as us,
+// we need to actually walk that entity, if we've not already walked
+// it.
+static void
+sort_cluster (depset::hash *original, depset *scc[], unsigned size)
+{
+  depset::hash table (size, original);
+
+  dump.indent ();
+
+  /* Place bindings last, usings before that.  It's not strictly
+     necessary, but it does make things neater.  Says Mr OCD.  */
+  unsigned bind_lwm = size;
+  unsigned use_lwm = size;
+  for (unsigned ix = 0; ix != use_lwm;)
+    {
+      depset *dep = scc[ix];
+      switch (dep->get_entity_kind ())
+	{
+	case depset::EK_BINDING:
+	  /* Move to end.  No increment.  Notice this could be moving
+	     a using decl, which we'll then move again.  */
+	  if (--bind_lwm != ix)
+	    {
+	      scc[ix] = scc[bind_lwm];
+	      scc[bind_lwm] = dep;
+	    }
+	  if (use_lwm > bind_lwm)
+	    {
+	      use_lwm--;
+	      break;
+	    }
+	  /* We must have copied a using, so move it too.  */
+	  dep = scc[ix];
+	  gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING);
+	  /* FALLTHROUGH  */
+
+	case depset::EK_USING:
+	  if (--use_lwm != ix)
+	    {
+	      scc[ix] = scc[use_lwm];
+	      scc[use_lwm] = dep;
+	    }
+	  break;
+
+	case depset::EK_DECL:
+	case depset::EK_SPECIALIZATION:
+	  table.add_mergeable (dep);
+	  ix++;
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  gcc_checking_assert (use_lwm <= bind_lwm);
+  dump (dumper::MERGE) && dump ("Ordering %u/%u depsets", use_lwm, size);
+
+  table.find_dependencies ();
+
+  vec<depset *> order = table.connect ();
+  gcc_checking_assert (order.length () == use_lwm);
+
+  /* Now rewrite entries [0,lwm), in the dependency order we
+     discovered.  Usually each entity is in its own cluster.  Rarely,
+     we can get multi-entity clusters, in which case all but one must
+     only be reached from within the cluster.  This happens for
+     something like:
+
+     template<typename T>
+     auto Foo (const T &arg) -> TPL<decltype (arg)>;
+
+     The instantiation of TPL will be in the specialization table, and
+     refer to Foo via arg.  But we can only get to that specialization
+     from Foo's declaration, so we only need to treat Foo as mergable
+     (We'll do structural comparison of TPL<decltype (arg)>).
+
+     Finding the single cluster entry dep is very tricky and
+     expensive.  Let's just not do that.  It's harmless in this case
+     anyway. */
+  unsigned pos = 0;
+  unsigned cluster = ~0u;
+  for (unsigned ix = 0; ix != order.length (); ix++)
+    {
+      gcc_checking_assert (order[ix]->is_special ());
+      depset *dep = order[ix]->deps[0];
+      scc[pos++] = dep;
+      dump (dumper::MERGE)
+	&& dump ("Mergeable %u is %N%s", ix, dep->get_entity (),
+		 order[ix]->cluster == cluster ? " (tight)" : "");
+      cluster = order[ix]->cluster;
+    }
+
+  gcc_checking_assert (pos == use_lwm);
+
+  order.release ();
+  dump (dumper::MERGE) && dump ("Ordered %u keys", pos);
+  dump.outdent ();
+}
+
+/* Reduce graph to SCCS clusters.  SCCS will be populated with the
+   depsets in dependency order.  Each depset's CLUSTER field contains
+   its cluster number.  Each SCC has a unique cluster number, and are
+   contiguous in SCCS. Cluster numbers are otherwise arbitrary.  */
+
+vec<depset *>
+depset::hash::connect ()
+{
+  tarjan connector (size ());
+  vec<depset *> deps;
+  deps.create (size ());
+  iterator end (this->end ());
+  for (iterator iter (begin ()); iter != end; ++iter)
+    {
+      depset *item = *iter;
+
+      entity_kind kind = item->get_entity_kind ();
+      if (kind == EK_BINDING
+	  || !(kind == EK_REDIRECT
+	       || item->is_unreached ()
+	       || item->is_import ()))
+	deps.quick_push (item);
+    }
+
+  /* Iteration over the hash table is an unspecified ordering.  While
+     that has advantages, it causes 2 problems.  Firstly repeatable
+     builds are tricky.  Secondly creating testcases that check
+     dependencies are correct by making sure a bad ordering would
+     happen if that was wrong.  */
+  deps.qsort (depset_cmp);
+
+  while (deps.length ())
+    {
+      depset *v = deps.pop ();
+      dump (dumper::CLUSTER) &&
+	(v->is_binding ()
+	 ? dump ("Connecting binding %P", v->get_entity (), v->get_name ())
+	 : dump ("Connecting %s %s %C:%N",
+		 is_key_order () ? "key-order"
+		 : !v->has_defn () ? "declaration" : "definition",
+		 v->entity_kind_name (), TREE_CODE (v->get_entity ()),
+		 v->get_entity ()));
+      if (!v->cluster)
+	connector.connect (v);
+    }
+
+  deps.release ();
+  return connector.result;
+}
+
+/* Load the entities referred to by this pendset.  */
+
+static bool
+pendset_lazy_load (pendset *pendings, bool specializations_p)
+{
+  bool ok = true;
+
+  for (unsigned ix = 0; ok && ix != pendings->num; ix++)
+    {
+      unsigned index = pendings->values[ix];
+      if (index & ~(~0u >> 1))
+	{
+	  /* An indirection.  */
+	  if (specializations_p)
+	    index = ~index;
+	  pendset *other = pending_table->get (index, true);
+	  if (!pendset_lazy_load (other, specializations_p))
+	    ok = false;
+	}
+      else
+	{
+	  module_state *module = import_entity_module (index);
+	  mc_slot *slot = &(*entity_ary)[index];
+	  if (!slot->is_lazy ())
+	    dump () && dump ("Specialiation %M[%u] already loaded",
+			     module, index - module->entity_lwm);
+	  else if (!module->lazy_load (index - module->entity_lwm, slot))
+	    ok = false;
+	}
+    }
+
+  /* We own set, so delete it now.  */
+  delete pendings;
+
+  return ok;
+}
+
+/* Initialize location spans.  */
+
+void
+loc_spans::init (const line_maps *lmaps, const line_map_ordinary *map)
+{
+  gcc_checking_assert (!init_p ());
+  spans.reserve (20);
+
+  span interval;
+  interval.ordinary.first = 0;
+  interval.macro.second = MAX_LOCATION_T + 1;
+  interval.ordinary_delta = interval.macro_delta = 0;
+
+  /* A span for reserved fixed locs.  */
+  interval.ordinary.second
+    = MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0));
+  interval.macro.first = interval.macro.second;
+  dump (dumper::LOCATION)
+    && dump ("Fixed span %u ordinary:[%u,%u) macro:[%u,%u)", spans.length (),
+	     interval.ordinary.first, interval.ordinary.second,
+	     interval.macro.first, interval.macro.second);
+  spans.quick_push (interval);
+
+  /* A span for command line & forced headers.  */
+  interval.ordinary.first = interval.ordinary.second;
+  interval.macro.second = interval.macro.first;
+  if (map)
+    {
+      interval.ordinary.second = map->start_location;
+      interval.macro.first = LINEMAPS_MACRO_LOWEST_LOCATION (lmaps);
+    }
+  dump (dumper::LOCATION)
+    && dump ("Pre span %u ordinary:[%u,%u) macro:[%u,%u)", spans.length (),
+	     interval.ordinary.first, interval.ordinary.second,
+	     interval.macro.first, interval.macro.second);
+  spans.quick_push (interval);
+  
+  /* Start an interval for the main file.  */
+  interval.ordinary.first = interval.ordinary.second;
+  interval.macro.second = interval.macro.first;
+  dump (dumper::LOCATION)
+    && dump ("Main span %u ordinary:[%u,*) macro:[*,%u)", spans.length (),
+	     interval.ordinary.first, interval.macro.second);
+  spans.quick_push (interval);
+}
+
+/* Reopen the span, if we want the about-to-be-inserted set of maps to
+   be propagated in our own location table.  I.e. we are the primary
+   interface and we're importing a partition.  */
+
+bool
+loc_spans::maybe_propagate (module_state *import,
+			    location_t loc = UNKNOWN_LOCATION)
+{
+  bool opened = (module_interface_p () && !module_partition_p ()
+		 && import->is_partition ());
+  if (opened)
+    open (loc);
+  return opened;
+}
+
+/* Open a new linemap interval.  The just-created ordinary map is the
+   first map of the interval.  */
+
+void
+loc_spans::open (location_t hwm = UNKNOWN_LOCATION)
+{
+  if (hwm == UNKNOWN_LOCATION)
+    hwm = MAP_START_LOCATION (LINEMAPS_LAST_ORDINARY_MAP (line_table));
+
+  span interval;
+  interval.ordinary.first = interval.ordinary.second = hwm;
+  interval.macro.first = interval.macro.second
+    = LINEMAPS_MACRO_LOWEST_LOCATION (line_table);
+  interval.ordinary_delta = interval.macro_delta = 0;
+  dump (dumper::LOCATION)
+    && dump ("Opening span %u ordinary:[%u,... macro:...,%u)",
+	     spans.length (), interval.ordinary.first,
+	     interval.macro.second);
+  spans.safe_push (interval);
+}
+
+/* Close out the current linemap interval.  The last maps are within
+   the interval.  */
+
+void
+loc_spans::close ()
+{
+  span &interval = spans.last ();
+
+  interval.ordinary.second
+    = ((line_table->highest_location + (1 << line_table->default_range_bits))
+       & ~((1u << line_table->default_range_bits) - 1));
+  interval.macro.first = LINEMAPS_MACRO_LOWEST_LOCATION (line_table);
+  dump (dumper::LOCATION)
+    && dump ("Closing span %u ordinary:[%u,%u) macro:[%u,%u)",
+	     spans.length () - 1,
+	     interval.ordinary.first,interval.ordinary.second,
+	     interval.macro.first, interval.macro.second);
+}
+
+/* Given an ordinary location LOC, return the lmap_interval it resides
+   in.  NULL if it is not in an interval.  */
+
+const loc_spans::span *
+loc_spans::ordinary (location_t loc)
+{
+  unsigned len = spans.length ();
+  unsigned pos = 0;
+  while (len)
+    {
+      unsigned half = len / 2;
+      const span &probe = spans[pos + half];
+      if (loc < probe.ordinary.first)
+	len = half;
+      else if (loc < probe.ordinary.second)
+	return &probe;
+      else
+	{
+	  pos += half + 1;
+	  len = len - (half + 1);
+	}
+    }
+  return NULL;
+}
+
+/* Likewise, given a macro location LOC, return the lmap interval it
+   resides in.   */
+
+const loc_spans::span *
+loc_spans::macro (location_t loc)
+{
+  unsigned len = spans.length ();
+  unsigned pos = 0;
+  while (len)
+    {
+      unsigned half = len / 2;
+      const span &probe = spans[pos + half];
+      if (loc >= probe.macro.second)
+	len = half;
+      else if (loc >= probe.macro.first)
+	return &probe;
+      else
+	{
+	  pos += half + 1;
+	  len = len - (half + 1);
+	}
+    }
+  return NULL;
+}
+
+/* Return the ordinary location closest to FROM.  */
+
+static location_t
+ordinary_loc_of (line_maps *lmaps, location_t from)
+{
+  while (!IS_ORDINARY_LOC (from))
+    {
+      if (IS_ADHOC_LOC (from))
+	from = get_location_from_adhoc_loc (lmaps, from);
+      if (IS_MACRO_LOC (from))
+	{
+	  /* Find the ordinary location nearest FROM.  */
+	  const line_map *map = linemap_lookup (lmaps, from);
+	  const line_map_macro *mac_map = linemap_check_macro (map);
+	  from = MACRO_MAP_EXPANSION_POINT_LOCATION (mac_map);
+	}
+    }
+  return from;
+}
+
+static module_state **
+get_module_slot (tree name, module_state *parent, bool partition, bool insert)
+{
+  module_state_hash::compare_type ct (name, uintptr_t (parent) | partition);
+  hashval_t hv = module_state_hash::hash (ct);
+
+  return modules_hash->find_slot_with_hash (ct, hv, insert ? INSERT : NO_INSERT);
+}
+
+static module_state *
+get_primary (module_state *parent)
+{
+  while (parent->is_partition ())
+    parent = parent->parent;
+
+  if (!parent->name)
+    // Implementation unit has null name
+    parent = parent->parent;
+
+  return parent;
+}
+
+/* Find or create module NAME & PARENT in the hash table.  */
+
+module_state *
+get_module (tree name, module_state *parent, bool partition)
+{
+  if (partition)
+    {
+      if (!parent)
+	parent = get_primary ((*modules)[0]);
+
+      if (!parent->is_partition () && !parent->flatname)
+	parent->set_flatname ();
+    }
+
+  module_state **slot = get_module_slot (name, parent, partition, true);
+  module_state *state = *slot;
+  if (!state)
+    {
+      state = (new (ggc_alloc<module_state> ())
+	       module_state (name, parent, partition));
+      *slot = state;
+    }
+  return state;
+}
+
+/* Process string name PTR into a module_state.  */
+
+static module_state *
+get_module (const char *ptr)
+{
+  if (ptr[0] == '.' ? IS_DIR_SEPARATOR (ptr[1]) : IS_ABSOLUTE_PATH (ptr))
+    /* A header name.  */
+    return get_module (build_string (strlen (ptr), ptr));
+
+  bool partition = false;
+  module_state *mod = NULL;
+
+  for (const char *probe = ptr;; probe++)
+    if (!*probe || *probe == '.' || *probe == ':')
+      {
+	if (probe == ptr)
+	  return NULL;
+
+	mod = get_module (get_identifier_with_length (ptr, probe - ptr),
+			  mod, partition);
+	ptr = probe;
+	if (*ptr == ':')
+	  {
+	    if (partition)
+	      return NULL;
+	    partition = true;
+	  }
+
+	if (!*ptr++)
+	  break;
+      }
+    else if (!(ISALPHA (*probe) || *probe == '_'
+	       || (probe != ptr && ISDIGIT (*probe))))
+      return NULL;
+
+  return mod;
+}
+
+/* Create a new mapper connecting to OPTION.  */
+
+module_client *
+make_mapper (location_t loc)
+{
+  timevar_start (TV_MODULE_MAPPER);
+  const char *option = module_mapper_name;
+  if (!option)
+    option = getenv ("CXX_MODULE_MAPPER");
+
+  mapper = module_client::open_module_client
+    (loc, option, &set_cmi_repo,
+     (save_decoded_options[0].opt_index == OPT_SPECIAL_program_name)
+     && save_decoded_options[0].arg != progname
+     ? save_decoded_options[0].arg : nullptr);
+
+  timevar_stop (TV_MODULE_MAPPER);
+
+  return mapper;
+}
+
+/* If THIS is the current purview, issue an import error and return false.  */
+
+bool
+module_state::check_not_purview (location_t from)
+{
+  module_state *imp = (*modules)[0];
+  if (imp && !imp->name)
+    imp = imp->parent;
+  if (imp == this)
+    {
+      /* Cannot import the current module.  */
+      error_at (from, "cannot import module in its own purview");
+      inform (loc, "module %qs declared here", get_flatname ());
+      return false;
+    }
+  return true;
+}
+
+/* Module name substitutions.  */
+static vec<module_state *,va_heap> substs;
+
+void
+module_state::mangle (bool include_partition)
+{
+  if (subst)
+    mangle_module_substitution (subst - 1);
+  else
+    {
+      if (parent)
+	parent->mangle (include_partition);
+      if (include_partition || !is_partition ())
+	{
+	  char p = 0;
+	  // Partitions are significant for global initializer functions
+	  if (is_partition () && !parent->is_partition ())
+	    p = 'P';
+	  substs.safe_push (this);
+	  subst = substs.length ();
+	  mangle_identifier (p, name);
+	}
+    }
+}
+
+void
+mangle_module (int mod, bool include_partition)
+{
+  module_state *imp = (*modules)[mod];
+
+  if (!imp->name)
+    /* Set when importing the primary module interface.  */
+    imp = imp->parent;
+
+  imp->mangle (include_partition);
+}
+
+/* Clean up substitutions.  */
+void
+mangle_module_fini ()
+{
+  while (substs.length ())
+    substs.pop ()->subst = 0;
+}
+
+/* Announce WHAT about the module.  */
+
+void
+module_state::announce (const char *what) const
+{
+  if (noisy_p ())
+    {
+      fprintf (stderr, " %s:%s", what, get_flatname ());
+      fflush (stderr);
+    }
+}
+
+/* A human-readable README section.  The contents of this section to
+   not contribute to the CRC, so the contents can change per
+   compilation.  That allows us to embed CWD, hostname, build time and
+   what not.  It is a STRTAB that may be extracted with:
+     readelf -pgnu.c++.README $(module).gcm */
+
+void
+module_state::write_readme (elf_out *to, const char *dialect,
+			    unsigned extensions)
+{
+  bytes_out readme (to);
+
+  readme.begin (false);
+
+  readme.printf ("GNU C++ %smodule%s%s",
+		 is_header () ? "header " : is_partition () ? "" : "primary ",
+		 is_header () ? ""
+		 : is_interface () ? " interface" : " implementation",
+		 is_partition () ? " partition" : "");
+
+  /* Compiler's version.  */
+  readme.printf ("compiler: %s", version_string);
+
+  /* Module format version.  */
+  verstr_t string;
+  version2string (MODULE_VERSION, string);
+  readme.printf ("version: %s", string);
+
+  /* Module information.  */
+  readme.printf ("module: %s", get_flatname ());
+  readme.printf ("source: %s", main_input_filename);
+  readme.printf ("dialect: %s", dialect);
+  if (extensions)
+    readme.printf ("extensions: %s",
+		   extensions & SE_OPENMP ? "-fopenmp" : "");
+
+  /* The following fields could be expected to change between
+     otherwise identical compilations.  Consider a distributed build
+     system.  */
+  if (char *cwd = getcwd (NULL, 0))
+    {
+      readme.printf ("cwd: %s", cwd);
+      free (cwd);
+    }
+  readme.printf ("repository: %s", cmi_repo ? cmi_repo : ".");
+#if NETWORKING
+  {
+    char hostname[64];
+    if (!gethostname (hostname, sizeof (hostname)))
+      readme.printf ("host: %s", hostname);
+  }
+#endif
+  {
+    /* This of course will change!  */
+    time_t now = time (NULL);
+    if (now != time_t (-1))
+      {
+	struct tm *time;
+
+	time = gmtime (&now);
+	readme.print_time ("build", time, "UTC");
+
+#if defined (__USE_MISC) || defined (__USE_BSD) /* Is there a better way?  */
+	time = localtime (&now);
+	readme.print_time ("local", time, time->tm_zone);
+#endif
+      }
+  }
+
+  /* Its direct imports.  */
+  for (unsigned ix = 1; ix < modules->length (); ix++)
+    {
+      module_state *state = (*modules)[ix];
+
+      if (state->is_direct ())
+	readme.printf ("%s: %s %s", state->exported_p ? "export" : "import",
+		       state->get_flatname (), state->filename);
+    }
+
+  readme.end (to, to->name (MOD_SNAME_PFX ".README"), NULL);
+}
+
+/* Sort environment var names in reverse order.  */
+
+static int
+env_var_cmp (const void *a_, const void *b_)
+{
+  const unsigned char *a = *(const unsigned char *const *)a_;
+  const unsigned char *b = *(const unsigned char *const *)b_;
+
+  for (unsigned ix = 0; ; ix++)
+    {
+      bool a_end = !a[ix] || a[ix] == '=';
+      if (a[ix] == b[ix])
+	{
+	  if (a_end)
+	    break;
+	}
+      else
+	{
+	  bool b_end = !b[ix] || b[ix] == '=';
+
+	  if (!a_end && !b_end)
+	    return a[ix] < b[ix] ? +1 : -1;
+	  if (a_end && b_end)
+	    break;
+	  return a_end ? +1 : -1;
+	}
+    }
+
+  return 0;
+}
+
+/* Write the environment. It is a STRTAB that may be extracted with:
+     readelf -pgnu.c++.ENV $(module).gcm */
+
+void
+module_state::write_env (elf_out *to)
+{
+  vec<const char *> vars;
+  vars.create (20);
+
+  extern char **environ;
+  while (const char *var = environ[vars.length ()])
+    vars.safe_push (var);
+  vars.qsort (env_var_cmp);
+
+  bytes_out env (to);
+  env.begin (false);
+  while (vars.length ())
+    env.printf ("%s", vars.pop ());
+  env.end (to, to->name (MOD_SNAME_PFX ".ENV"), NULL);
+
+  vars.release ();
+}
+
+/* Write the direct or indirect imports.
+   u:N
+   {
+     u:index
+     s:name
+     u32:crc
+     s:filename (direct)
+     u:exported (direct)
+   } imports[N]
+ */
+
+void
+module_state::write_imports (bytes_out &sec, bool direct)
+{
+  unsigned count = 0;
+
+  for (unsigned ix = 1; ix < modules->length (); ix++)
+    {
+      module_state *imp = (*modules)[ix];
+
+      if (imp->remap && imp->is_direct () == direct)
+	count++;
+    }
+
+  gcc_assert (!direct || count);
+
+  sec.u (count);
+  for (unsigned ix = 1; ix < modules->length (); ix++)
+    {
+      module_state *imp = (*modules)[ix];
+
+      if (imp->remap && imp->is_direct () == direct)
+	{
+	  dump () && dump ("Writing %simport:%u->%u %M (crc=%x)",
+			   !direct ? "indirect "
+			   : imp->exported_p ? "exported " : "",
+			   ix, imp->remap, imp, imp->crc);
+	  sec.u (imp->remap);
+	  sec.str (imp->get_flatname ());
+	  sec.u32 (imp->crc);
+	  if (direct)
+	    {
+	      write_location (sec, imp->imported_from ());
+	      sec.str (imp->filename);
+	      int exportedness = 0;
+	      if (imp->exported_p)
+		exportedness = +1;
+	      else if (!imp->is_purview_direct ())
+		exportedness = -1;
+	      sec.i (exportedness);
+	    }
+	}
+    }
+}
+
+/* READER, LMAPS  != NULL == direct imports,
+   == NUL == indirect imports.  */
+
+unsigned
+module_state::read_imports (bytes_in &sec, cpp_reader *reader, line_maps *lmaps)
+{
+  unsigned count = sec.u ();
+  unsigned loaded = 0;
+
+  while (count--)
+    {
+      unsigned ix = sec.u ();
+      if (ix >= slurp->remap->length () || !ix || (*slurp->remap)[ix])
+	{
+	  sec.set_overrun ();
+	  break;
+	}
+
+      const char *name = sec.str (NULL);
+      module_state *imp = get_module (name);
+      unsigned crc = sec.u32 ();
+      int exportedness = 0;
+
+      /* If the import is a partition, it must be the same primary
+	 module as this TU.  */
+      if (imp && imp->is_partition () &&
+	  (!named_module_p ()
+	   || (get_primary ((*modules)[0]) != get_primary (imp))))
+	imp = NULL;
+
+      if (!imp)
+	sec.set_overrun ();
+      if (sec.get_overrun ())
+	break;
+
+      if (lmaps)
+	{
+	  /* A direct import, maybe load it.  */
+	  location_t floc = read_location (sec);
+	  const char *fname = sec.str (NULL);
+	  exportedness = sec.i ();
+
+	  if (sec.get_overrun ())
+	    break;
+
+	  if (!imp->check_not_purview (loc))
+	    continue;
+
+	  if (imp->loadedness == ML_NONE)
+	    {
+	      imp->loc = floc;
+	      imp->crc = crc;
+	      if (!imp->get_flatname ())
+		imp->set_flatname ();
+
+	      unsigned n = dump.push (imp);
+
+	      if (!imp->filename && fname)
+		imp->filename = xstrdup (fname);
+
+	      if (imp->is_partition ())
+		dump () && dump ("Importing elided partition %M", imp);
+
+	      if (!imp->do_import (reader, false))
+		imp = NULL;
+	      dump.pop (n);
+	      if (!imp)
+		continue;
+	    }
+
+	  if (is_partition ())
+	    {
+	      if (!imp->is_direct ())
+		imp->directness = MD_PARTITION_DIRECT;
+	      if (exportedness > 0)
+		imp->exported_p = true;
+	    }
+	}
+      else
+	{
+	  /* An indirect import, find it, it should already be here.  */
+	  if (imp->loadedness == ML_NONE)
+	    {
+	      error_at (loc, "indirect import %qs is not already loaded", name);
+	      continue;
+	    }
+	}
+
+      if (imp->crc != crc)
+	error_at (loc, "import %qs has CRC mismatch", imp->get_flatname ());
+
+      (*slurp->remap)[ix] = (imp->mod << 1) | (lmaps != NULL);
+
+      if (lmaps && exportedness >= 0)
+	set_import (imp, bool (exportedness));
+      dump () && dump ("Found %simport:%u %M->%u", !lmaps ? "indirect "
+		       : exportedness > 0 ? "exported "
+		       : exportedness < 0 ? "gmf" : "", ix, imp,
+		       imp->mod);
+      loaded++;
+    }
+
+  return loaded;
+}
+
+/* Write the import table to MOD_SNAME_PFX.imp.  */
+
+void
+module_state::write_imports (elf_out *to, unsigned *crc_ptr)
+{
+  dump () && dump ("Writing imports");
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  write_imports (sec, true);
+  write_imports (sec, false);
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".imp"), crc_ptr);
+  dump.outdent ();
+}
+
+bool
+module_state::read_imports (cpp_reader *reader, line_maps *lmaps)
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".imp"))
+    return false;
+
+  dump () && dump ("Reading %u imports", slurp->remap->length () - 1);
+  dump.indent ();
+
+  /* Read the imports.  */
+  unsigned direct = read_imports (sec, reader, lmaps);
+  unsigned indirect = read_imports (sec, NULL, NULL);
+  if (direct + indirect + 1 != slurp->remap->length ())
+    from ()->set_error (elf::E_BAD_IMPORT);
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* We're the primary module interface, but have partitions.  Document
+   them so that non-partition module implementation units know which
+   have already been loaded.  */
+
+void
+module_state::write_partitions (elf_out *to, unsigned count, unsigned *crc_ptr)
+{
+  dump () && dump ("Writing %u elided partitions", count);
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  for (unsigned ix = 1; ix != modules->length (); ix++)
+    {
+      module_state *imp = (*modules)[ix];
+      if (imp->is_partition ())
+	{
+	  dump () && dump ("Writing elided partition %M (crc=%x)",
+			   imp, imp->crc);
+	  sec.str (imp->get_flatname ());
+	  sec.u32 (imp->crc);
+	  write_location (sec, imp->is_direct ()
+			  ? imp->imported_from () : UNKNOWN_LOCATION);
+	  sec.str (imp->filename);
+	}
+    }
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".prt"), crc_ptr);
+  dump.outdent ();
+}
+
+bool
+module_state::read_partitions (unsigned count)
+{
+  bytes_in sec;
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".prt"))
+    return false;
+
+  dump () && dump ("Reading %u elided partitions", count);
+  dump.indent ();
+
+  while (count--)
+    {
+      const char *name = sec.str (NULL);
+      unsigned crc = sec.u32 ();
+      location_t floc = read_location (sec);
+      const char *fname = sec.str (NULL);
+
+      if (sec.get_overrun ())
+	break;
+
+      dump () && dump ("Reading elided partition %s (crc=%x)", name, crc);
+
+      module_state *imp = get_module (name);
+      if (!imp || !imp->is_partition () || imp->is_rooted ()
+	  || get_primary (imp) != this)
+	{
+	  sec.set_overrun ();
+	  break;
+	}
+
+      /* Attach the partition without loading it.  We'll have to load
+	 for real if it's indirectly imported.  */
+      imp->loc = floc;
+      imp->crc = crc;
+      if (!imp->filename && fname[0])
+	imp->filename = xstrdup (fname);
+    }
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* Counter indices.  */
+enum module_state_counts
+{
+  MSC_sec_lwm,
+  MSC_sec_hwm,
+  MSC_pendings,
+  MSC_entities,
+  MSC_namespaces,
+  MSC_bindings,
+  MSC_macros,
+  MSC_inits,
+  MSC_HWM
+};
+
+/* Data for config reading and writing.  */
+struct module_state_config {
+  const char *dialect_str;
+  unsigned num_imports;
+  unsigned num_partitions;
+  unsigned ordinary_locs;
+  unsigned macro_locs;
+  unsigned ordinary_loc_align;
+
+public:
+  module_state_config ()
+    :dialect_str (get_dialect ()),
+     num_imports (0), num_partitions (0),
+     ordinary_locs (0), macro_locs (0), ordinary_loc_align (0)
+  {
+  }
+
+  static void release ()
+  {
+    XDELETEVEC (dialect);
+    dialect = NULL;
+  }
+
+private:
+  static const char *get_dialect ();
+  static char *dialect;
+};
+
+char *module_state_config::dialect;
+
+/* Generate a string of the significant compilation options.
+   Generally assume the user knows what they're doing, in the same way
+   that object files can be mixed.  */
+
+const char *
+module_state_config::get_dialect ()
+{
+  if (!dialect)
+    dialect = concat (get_cxx_dialect_name (cxx_dialect),
+		      /* C++ implies these, only show if disabled.  */
+		      flag_exceptions ? "" : "/no-exceptions",
+		      flag_rtti ? "" : "/no-rtti",
+		      flag_new_inheriting_ctors ? "" : "/old-inheriting-ctors",
+		      /* C++ 20 implies concepts.  */
+		      cxx_dialect < cxx20 && flag_concepts ? "/concepts" : "",
+		      flag_coroutines ? "/coroutines" : "",
+		      flag_module_implicit_inline ? "/implicit-inline" : "",
+		      NULL);
+
+  return dialect;
+}
+
+/* Contents of a cluster.  */
+enum cluster_tag {
+  ct_decl,	/* A decl.  */
+  ct_defn,	/* A definition.  */
+  ct_bind,	/* A binding.  */
+  ct_hwm
+};
+
+/* Binding modifiers.  */
+enum ct_bind_flags
+{
+  cbf_export = 0x1,	/* An exported decl.  */
+  cbf_hidden = 0x2,	/* A hidden (friend) decl.  */
+  cbf_using = 0x4,	/* A using decl.  */
+  cbf_wrapped = 0x8,  	/* ... that is wrapped.  */
+};
+
+/* Write the cluster of depsets in SCC[0-SIZE).  */
+
+unsigned
+module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
+			     depset::hash &table, unsigned *counts,
+			     unsigned *crc_ptr)
+{
+  dump () && dump ("Writing section:%u %u depsets", table.section, size);
+  dump.indent ();
+
+  trees_out sec (to, this, table, table.section);
+  sec.begin ();
+
+  /* Determine entity numbers, mark for writing.   */
+  dump (dumper::CLUSTER) && dump ("Cluster members:") && (dump.indent (), true);
+  for (unsigned ix = 0; ix != size; ix++)
+    {
+      depset *b = scc[ix];
+
+      switch (b->get_entity_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+
+	case depset::EK_BINDING:
+	  dump (dumper::CLUSTER)
+	    && dump ("[%u]=%s %P", ix, b->entity_kind_name (),
+		     b->get_entity (), b->get_name ());
+	  for (unsigned jx = b->deps.length (); jx--;)
+	    {
+	      depset *dep = b->deps[jx];
+	      if (jx)
+		gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING
+				     || TREE_VISITED (dep->get_entity ()));
+	      else
+		gcc_checking_assert (dep->get_entity_kind ()
+				     == depset::EK_NAMESPACE
+				     && dep->get_entity () == b->get_entity ());
+	    }
+	  break;
+
+	case depset::EK_DECL:
+	  if (b->is_member ())
+	    {
+	    case depset::EK_SPECIALIZATION:  /* Yowzer! */
+	      counts[MSC_pendings]++;
+	    }
+	  b->cluster = counts[MSC_entities]++;
+	  sec.mark_declaration (b->get_entity (), b->has_defn ());
+	  /* FALLTHROUGH  */
+
+	case depset::EK_USING:
+	  gcc_checking_assert (!b->is_import ()
+			       && !b->is_unreached ());
+	  dump (dumper::CLUSTER)
+	    && dump ("[%u]=%s %s %N", ix, b->entity_kind_name (),
+		     b->has_defn () ? "definition" : "declaration",
+		     b->get_entity ());
+	  break;
+	}
+    }
+  dump (dumper::CLUSTER) && (dump.outdent (), true);
+
+  /* Ensure every imported decl is referenced before we start
+     streaming.  This ensures that we never encounter the
+     situation where this cluster instantiates some implicit
+     member that importing some other decl causes to be
+     instantiated.  */
+  sec.set_importing (+1);
+  for (unsigned ix = 0; ix != size; ix++)
+    {
+      depset *b = scc[ix];
+      for (unsigned jx = (b->get_entity_kind () == depset::EK_BINDING
+			  || b->is_special ()) ? 1 : 0;
+	   jx != b->deps.length (); jx++)
+	{
+	  depset *dep = b->deps[jx];
+
+	  if (!dep->is_binding ()
+	      && dep->is_import () && !TREE_VISITED (dep->get_entity ()))
+	    {
+	      tree import = dep->get_entity ();
+
+	      sec.tree_node (import);
+	      dump (dumper::CLUSTER) && dump ("Seeded import %N", import);
+	    }
+	}
+    }
+  sec.tree_node (NULL_TREE);
+  /* We're done importing now.  */
+  sec.set_importing (-1);
+
+  /* Write non-definitions.  */
+  for (unsigned ix = 0; ix != size; ix++)
+    {
+      depset *b = scc[ix];
+      tree decl = b->get_entity ();
+      switch (b->get_entity_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	  break;
+
+	case depset::EK_BINDING:
+	  {
+	    gcc_assert (TREE_CODE (decl) == NAMESPACE_DECL);
+	    dump () && dump ("Depset:%u binding %C:%P", ix, TREE_CODE (decl),
+			     decl, b->get_name ());
+	    sec.u (ct_bind);
+	    sec.tree_node (decl);
+	    sec.tree_node (b->get_name ());
+
+	    /* Write in reverse order, so reading will see the exports
+	       first, thus building the overload chain will be
+	       optimized.  */
+	    for (unsigned jx = b->deps.length (); --jx;)
+	      {
+		depset *dep = b->deps[jx];
+		tree bound = dep->get_entity ();
+		unsigned flags = 0;
+		if (dep->get_entity_kind () == depset::EK_USING)
+		  {
+		    tree ovl = bound;
+		    bound = OVL_FUNCTION (bound);
+		    if (!(TREE_CODE (bound) == CONST_DECL
+			  && UNSCOPED_ENUM_P (TREE_TYPE (bound))
+			  && decl == TYPE_NAME (TREE_TYPE (bound))))
+		      {
+			/* An unscope enumerator in its enumeration's
+			   scope is not a using.  */
+			flags |= cbf_using;
+			if (OVL_USING_P (ovl))
+			  flags |= cbf_wrapped;
+		      }
+		    if (OVL_EXPORT_P (ovl))
+		      flags |= cbf_export;
+		  }
+		else
+		  {
+		    /* An implicit typedef must be at one.  */
+		    gcc_assert (!DECL_IMPLICIT_TYPEDEF_P (bound) || jx == 1);
+		    if (dep->is_hidden ())
+		      flags |= cbf_hidden;
+		    else if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (bound)))
+		      flags |= cbf_export;
+		  }
+
+		gcc_checking_assert (DECL_P (bound));
+
+		sec.i (flags);
+		sec.tree_node (bound);
+	      }
+
+	    /* Terminate the list.  */
+	    sec.i (-1);
+	  }
+	  break;
+
+	case depset::EK_USING:
+	  dump () && dump ("Depset:%u %s %C:%N", ix, b->entity_kind_name (),
+			   TREE_CODE (decl), decl);
+	  break;
+
+	case depset::EK_SPECIALIZATION:
+	case depset::EK_DECL:
+	  dump () && dump ("Depset:%u %s entity:%u %C:%N", ix,
+			   b->entity_kind_name (), b->cluster,
+			   TREE_CODE (decl), decl);
+
+	  sec.u (ct_decl);
+	  sec.tree_node (decl);
+
+	  dump () && dump ("Wrote declaration entity:%u %C:%N",
+			   b->cluster, TREE_CODE (decl), decl);
+	  break;
+	}
+    }
+
+  depset *namer = NULL;
+
+  /* Write out definitions  */
+  for (unsigned ix = 0; ix != size; ix++)
+    {
+      depset *b = scc[ix];
+      tree decl = b->get_entity ();
+      switch (b->get_entity_kind ())
+	{
+	default:
+	  break;
+
+	case depset::EK_SPECIALIZATION:
+	case depset::EK_DECL:
+	  if (!namer)
+	    namer = b;
+
+	  if (b->has_defn ())
+	    {
+	      sec.u (ct_defn);
+	      sec.tree_node (decl);
+	      dump () && dump ("Writing definition %N", decl);
+	      sec.write_definition (decl);
+
+	      if (!namer->has_defn ())
+		namer = b;
+	    }
+	  break;
+	}
+    }
+
+  /* We don't find the section by name.  Use depset's decl's name for
+     human friendliness.  */
+  unsigned name = 0;
+  tree naming_decl = NULL_TREE;
+  if (namer)
+    {
+      naming_decl = namer->get_entity ();
+      if (namer->get_entity_kind () == depset::EK_USING)
+	/* This unfortunately names the section from the target of the
+	   using decl.  But the name is only a guide, so Do Not Care.  */
+	naming_decl = OVL_FUNCTION (naming_decl);
+      if (DECL_IMPLICIT_TYPEDEF_P (naming_decl))
+	/* Lose any anonymousness.  */
+	naming_decl = TYPE_NAME (TREE_TYPE (naming_decl));
+      name = to->qualified_name (naming_decl, namer->has_defn ());
+    }
+
+  unsigned bytes = sec.pos;
+  unsigned snum = sec.end (to, name, crc_ptr);
+
+  for (unsigned ix = size; ix--;)
+    gcc_checking_assert (scc[ix]->section == snum);
+
+  dump.outdent ();
+  dump () && dump ("Wrote section:%u named-by:%N", table.section, naming_decl);
+
+  return bytes;
+}
+
+/* Read a cluster from section SNUM.  */
+
+bool
+module_state::read_cluster (unsigned snum)
+{
+  trees_in sec (this);
+
+  if (!sec.begin (loc, from (), snum))
+    return false;
+
+  dump () && dump ("Reading section:%u", snum);
+  dump.indent ();
+
+  /* We care about typename structural equality.  */
+  comparing_typenames++;
+
+  /* First seed the imports.  */
+  while (tree import = sec.tree_node ())
+    dump (dumper::CLUSTER) && dump ("Seeded import %N", import);
+
+  while (!sec.get_overrun () && sec.more_p ())
+    {
+      unsigned ct = sec.u ();
+      switch (ct)
+	{
+	default:
+	  sec.set_overrun ();
+	  break;
+
+	case ct_bind:
+	  /* A set of namespace bindings.  */
+	  {
+	    tree ns = sec.tree_node ();
+	    tree name = sec.tree_node ();
+	    tree decls = NULL_TREE;
+	    tree visible = NULL_TREE;
+	    tree type = NULL_TREE;
+	    bool dedup = false;
+
+	    /* We rely on the bindings being in the reverse order of
+	       the resulting overload set.  */
+	    for (;;)
+	      {
+		int flags = sec.i ();
+		if (flags < 0)
+		  break;
+
+		if ((flags & cbf_hidden)
+		    && (flags & (cbf_using | cbf_export)))
+		  sec.set_overrun ();
+
+		tree decl = sec.tree_node ();
+		if (sec.get_overrun ())
+		  break;
+
+		if (decls && TREE_CODE (decl) == TYPE_DECL)
+		  {
+		    /* Stat hack.  */
+		    if (type || !DECL_IMPLICIT_TYPEDEF_P (decl))
+		      sec.set_overrun ();
+		    type = decl;
+		  }
+		else
+		  {
+		    if (decls
+			|| (flags & (cbf_hidden | cbf_wrapped))
+			|| DECL_FUNCTION_TEMPLATE_P (decl))
+		      {
+			decls = ovl_make (decl, decls);
+			if (flags & cbf_using)
+			  {
+			    dedup = true;
+			    OVL_USING_P (decls) = true;
+			    if (flags & cbf_export)
+			      OVL_EXPORT_P (decls) = true;
+			  }
+
+			if (flags & cbf_hidden)
+			  OVL_HIDDEN_P (decls) = true;
+			else if (dedup)
+			  OVL_DEDUP_P (decls) = true;
+		      }
+		    else
+		      decls = decl;
+
+		    if (flags & cbf_export
+			|| (!(flags & cbf_hidden)
+			    && (is_module () || is_partition ())))
+		      visible = decls;
+		  }
+	      }
+
+	    if (!decls)
+	      sec.set_overrun ();
+
+	    if (sec.get_overrun ())
+	      break; /* Bail.  */
+
+	    dump () && dump ("Binding of %P", ns, name);
+	    if (!set_module_binding (ns, name, mod,
+				     is_header () ? -1
+				     : is_module () || is_partition () ? 1
+				     : 0,
+				     decls, type, visible))
+	      sec.set_overrun ();
+
+	    if (type
+		&& CP_DECL_CONTEXT (type) == ns
+		&& !sec.is_duplicate (type))
+	      add_module_decl (ns, name, type);
+
+	    for (ovl_iterator iter (decls); iter; ++iter)
+	      if (!iter.using_p ())
+		{
+		  tree decl = *iter;
+		  if (CP_DECL_CONTEXT (decl) == ns
+		      && !sec.is_duplicate (decl))
+		    add_module_decl (ns, name, decl);
+		}
+	  }
+	  break;
+
+	case ct_decl:
+	  /* A decl.  */
+	  {
+	    tree decl = sec.tree_node ();
+	    dump () && dump ("Read declaration of %N", decl);
+	  }
+	  break;
+
+	case ct_defn:
+	  {
+	    tree decl = sec.tree_node ();
+	    dump () && dump ("Reading definition of %N", decl);
+	    sec.read_definition (decl);
+	  }
+	  break;
+	}
+    }
+
+  /* When lazy loading is in effect, we can be in the middle of
+     parsing or instantiating a function.  Save it away.
+     push_function_context does too much work.   */
+  tree old_cfd = current_function_decl;
+  struct function *old_cfun = cfun;
+  while (tree decl = sec.post_process ())
+    {
+      bool abstract = false;
+      if (TREE_CODE (decl) == TEMPLATE_DECL)
+	{
+	  abstract = true;
+	  decl = DECL_TEMPLATE_RESULT (decl);
+	}
+
+      current_function_decl = decl;
+      allocate_struct_function (decl, abstract);
+      cfun->language = ggc_cleared_alloc<language_function> ();
+      cfun->language->base.x_stmt_tree.stmts_are_full_exprs_p = 1;
+
+      if (abstract)
+	;
+      else if (DECL_ABSTRACT_P (decl))
+	{
+	  bool cloned = maybe_clone_body (decl);
+	  if (!cloned)
+	    from ()->set_error ();
+	}
+      else
+	{
+	  bool aggr = aggregate_value_p (DECL_RESULT (decl), decl);
+#ifdef PCC_STATIC_STRUCT_RETURN
+	  cfun->returns_pcc_struct = aggr;
+#endif
+	  cfun->returns_struct = aggr;
+
+	  if (DECL_COMDAT (decl))
+	    // FIXME: Comdat grouping?
+	    comdat_linkage (decl);
+	  note_vague_linkage_fn (decl);
+	  cgraph_node::finalize_function (decl, true);
+	}
+
+    }
+  /* Look, function.c's interface to cfun does too much for us, we
+     just need to restore the old value.  I do not want to go
+     redesigning that API right now.  */
+#undef cfun
+  cfun = old_cfun;
+  current_function_decl = old_cfd;
+  comparing_typenames--;
+  dump.outdent ();
+  dump () && dump ("Read section:%u", snum);
+
+  loaded_clusters++;
+
+  if (!sec.end (from ()))
+    return false;
+
+  return true;
+}
+
+void
+module_state::write_namespace (bytes_out &sec, depset *dep)
+{
+  unsigned ns_num = dep->cluster;
+  unsigned ns_import = 0;
+
+  if (dep->is_import ())
+    ns_import = dep->section;
+  else if (dep->get_entity () != global_namespace)
+    ns_num++;
+
+  sec.u (ns_import);
+  sec.u (ns_num);
+}
+
+tree
+module_state::read_namespace (bytes_in &sec)
+{
+  unsigned ns_import = sec.u ();
+  unsigned ns_num = sec.u ();
+  tree ns = NULL_TREE;
+
+  if (ns_import || ns_num)
+    {
+      if (!ns_import)
+	ns_num--;
+
+      if (unsigned origin = slurp->remap_module (ns_import))
+	{
+	  module_state *from = (*modules)[origin];
+	  if (ns_num < from->entity_num)
+	    {
+	      mc_slot &slot = (*entity_ary)[from->entity_lwm + ns_num];
+
+	      if (!slot.is_lazy ())
+		ns = slot;
+	    }
+	}
+      else
+	sec.set_overrun ();
+    }
+  else
+    ns = global_namespace;
+
+  return ns;
+}
+
+/* SPACES is a sorted vector of namespaces.  Write out the namespaces
+   to MOD_SNAME_PFX.nms section.   */
+
+void
+module_state::write_namespaces (elf_out *to, vec<depset *> spaces,
+				unsigned num, unsigned *crc_p)
+{
+  dump () && dump ("Writing namespaces");
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  for (unsigned ix = 0; ix != num; ix++)
+    {
+      depset *b = spaces[ix];
+      tree ns = b->get_entity ();
+
+      gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
+
+      bool export_p = DECL_MODULE_EXPORT_P (ns);
+      bool inline_p = DECL_NAMESPACE_INLINE_P (ns);
+      bool public_p = TREE_PUBLIC (ns);
+
+      /* We should only be naming public namespaces, or our own
+	 private ones.  Internal linkage ones never get to be written
+	 out -- because that means something erroneously referred to a
+	 member.  However, Davis Herring's paper probably changes that
+	 by permitting them to be written out, but then an error if on
+	 touches them.  (Certain cases cannot be detected until that
+	 point.)  */ 
+      gcc_checking_assert (public_p || !DECL_MODULE_IMPORT_P (ns));
+      unsigned flags = 0;
+      if (export_p)
+	flags |= 1;
+      if (inline_p)
+	flags |= 2;
+      if (public_p)
+	flags |= 4;
+      dump () && dump ("Writing namespace:%u %N%s%s%s",
+		       b->cluster, ns, export_p ? ", export" : "",
+		       public_p ? ", public" : "",
+		       inline_p ? ", inline" : "");
+      sec.u (b->cluster);
+      sec.u (to->name (DECL_NAME (ns)));
+      write_namespace (sec, b->deps[0]);
+
+      /* Don't use bools, because this can be near the end of the
+	 section, and it won't save anything anyway.  */
+      sec.u (flags);
+      write_location (sec, DECL_SOURCE_LOCATION (ns));
+    }
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".nms"), crc_p);
+  dump.outdent ();
+}
+
+/* Read the namespace hierarchy from MOD_SNAME_PFX.namespace.  Fill in
+   SPACES from that data.  */
+
+bool
+module_state::read_namespaces (unsigned num)
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".nms"))
+    return false;
+
+  dump () && dump ("Reading namespaces");
+  dump.indent ();
+
+  for (unsigned ix = 0; ix != num; ix++)
+    {
+      unsigned entity_index = sec.u ();
+      unsigned name = sec.u ();
+
+      tree parent = read_namespace (sec);
+
+      /* See comment in write_namespace about why not bits.  */
+      unsigned flags = sec.u ();
+      location_t src_loc = read_location (sec);
+
+      if (entity_index >= entity_num || !parent)
+	sec.set_overrun ();
+      if (sec.get_overrun ())
+	break;
+
+      tree id = name ? get_identifier (from ()->name (name)) : NULL_TREE;
+      bool public_p = flags & 4;
+      bool inline_p = flags & 2;
+      bool export_p = flags & 1;
+
+      dump () && dump ("Read namespace:%u %P%s%s%s",
+		       entity_index, parent, id, export_p ? ", export" : "",
+		       public_p ? ", public" : "",
+		       inline_p ? ", inline" : "");
+      bool visible_p = (export_p
+			|| (public_p && (is_partition () || is_module ())));
+      tree inner = add_imported_namespace (parent, id, mod,
+					   src_loc, visible_p, inline_p);
+      if (export_p && is_partition ())
+	DECL_MODULE_EXPORT_P (inner) = true;
+
+      /* Install the namespace.  */
+      (*entity_ary)[entity_lwm + entity_index] = inner;
+      if (DECL_MODULE_IMPORT_P (inner))
+	{
+	  bool existed;
+	  unsigned *slot = &entity_map->get_or_insert
+	    (DECL_UID (inner), &existed);
+	  if (existed)
+	    /* If it existed, it should match.  */
+	    gcc_checking_assert (inner == (*entity_ary)[*slot]);
+	  else
+	    *slot = entity_lwm + entity_index;
+	}
+    }
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* Write the binding TABLE to MOD_SNAME_PFX.bnd   */
+
+unsigned
+module_state::write_bindings (elf_out *to, vec<depset *> sccs, unsigned *crc_p)
+{
+  dump () && dump ("Writing binding table");
+  dump.indent ();
+
+  unsigned num = 0;
+  bytes_out sec (to);
+  sec.begin ();
+
+  for (unsigned ix = 0; ix != sccs.length (); ix++)
+    {
+      depset *b = sccs[ix];
+      if (b->is_binding ())
+	{
+	  tree ns = b->get_entity ();
+	  dump () && dump ("Bindings %P section:%u", ns, b->get_name (),
+			   b->section);
+	  sec.u (to->name (b->get_name ()));
+	  write_namespace (sec, b->deps[0]);
+	  sec.u (b->section);
+	  num++;
+	}
+    }
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".bnd"), crc_p);
+  dump.outdent ();
+
+  return num;
+}
+
+/* Read the binding table from MOD_SNAME_PFX.bind.  */
+
+bool
+module_state::read_bindings (unsigned num, unsigned lwm, unsigned hwm)
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".bnd"))
+    return false;
+
+  dump () && dump ("Reading binding table");
+  dump.indent ();
+  for (; !sec.get_overrun () && num--;)
+    {
+      const char *name = from ()->name (sec.u ());
+      tree ns = read_namespace (sec);
+      unsigned snum = sec.u ();
+
+      if (!ns || !name || (snum - lwm) >= (hwm - lwm))
+	sec.set_overrun ();
+      if (!sec.get_overrun ())
+	{
+	  tree id = get_identifier (name);
+	  dump () && dump ("Bindings %P section:%u", ns, id, snum);
+	  if (mod && !import_module_binding (ns, id, mod, snum))
+	    break;
+	}
+    }
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* Write the entity table to MOD_SNAME_PFX.ent
+
+   Each entry is a section number.  */
+
+void
+module_state::write_entities (elf_out *to, vec<depset *> depsets,
+			      unsigned count, unsigned *crc_p)
+{
+  dump () && dump ("Writing entities");
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  unsigned current = 0;
+  for (unsigned ix = 0; ix < depsets.length (); ix++)
+    {
+      depset *d = depsets[ix];
+
+      switch (d->get_entity_kind ())
+	{
+	default:
+	  break;
+
+	case depset::EK_NAMESPACE:
+	  if (!d->is_import () && d->get_entity () != global_namespace)
+	    {
+	      gcc_checking_assert (d->cluster == current);
+	      current++;
+	      sec.u (0);
+	    }
+	  break;
+
+	case depset::EK_DECL:
+	case depset::EK_SPECIALIZATION:
+	  gcc_checking_assert (!d->is_unreached ()
+			       && !d->is_import ()
+			       && d->cluster == current
+			       && d->section);
+	  current++;
+	  sec.u (d->section);
+	  break;
+	}
+    }
+  gcc_assert (count == current);
+  sec.end (to, to->name (MOD_SNAME_PFX ".ent"), crc_p);
+  dump.outdent ();
+}
+
+bool
+module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm)
+{
+  trees_in sec (this);
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".ent"))
+    return false;
+
+  dump () && dump ("Reading entities");
+  dump.indent ();
+
+  vec_safe_reserve (entity_ary, count);
+  unsigned ix;
+  for (ix = 0; ix != count; ix++)
+    {
+      unsigned snum = sec.u ();
+      if (snum && (snum - lwm) >= (hwm - lwm))
+	sec.set_overrun ();
+      if (sec.get_overrun ())
+	break;
+
+      mc_slot slot;
+      slot.u.binding = NULL_TREE;
+      if (snum)
+	slot.set_lazy (snum << 2);
+      entity_ary->quick_push (slot);
+    }
+  entity_num = ix;
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* Write the pending table to MOD_SNAME_PFX.pnd
+
+   Specializations are keyed to their primary template.
+   Members are keyed to their context.
+
+   For specializations, primary templates are keyed to the (namespace
+   name) of their originating decl.  */
+
+void
+module_state::write_pendings (elf_out *to, vec<depset *> depsets,
+			      depset::hash &table,
+			      unsigned count, unsigned *crc_p)
+{
+  dump () && dump ("Writing %u pendings", count);
+  dump.indent ();
+
+  trees_out sec (to, this, table);
+  sec.begin ();
+
+  for (unsigned ix = 0; ix < depsets.length (); ix++)
+    {
+      depset *d = depsets[ix];
+      depset::entity_kind kind = d->get_entity_kind ();
+      tree key = NULL_TREE;
+      bool is_spec = kind == depset::EK_SPECIALIZATION;
+
+      if (is_spec)
+	key = reinterpret_cast <spec_entry *> (d->deps[0])->tmpl;
+      else if (kind == depset::EK_DECL && d->is_member ())
+	{
+	  tree ctx = DECL_CONTEXT (d->get_entity ());
+	  key = TYPE_NAME (ctx);
+	  if (tree ti = CLASSTYPE_TEMPLATE_INFO (ctx))
+	    if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == key)
+	      key = TI_TEMPLATE (ti);
+	}
+
+      // FIXME:OPTIMIZATION More than likely when there is one pending
+      // member, there will be others.  All written in the same
+      // section and keyed to the same class.  We only need to record
+      // one of them.  The same is not true for specializations
+
+      if (key)
+	{
+	  gcc_checking_assert (!d->is_import ());
+
+	  {
+	    /* Key the entity to its key.  */
+	    depset *key_dep = table.find_dependency (key);
+	    if (key_dep->get_entity_kind () == depset::EK_REDIRECT)
+	      key_dep = key_dep->deps[0];
+	    unsigned key_origin
+	      = key_dep->is_import () ? key_dep->section : 0;
+	    sec.u (key_origin);
+	    sec.u (key_dep->cluster);
+	    sec.u (d->cluster);
+	    dump () && dump ("%s %N entity:%u keyed to %M[%u] %N",
+			     is_spec ? "Specialization" : "Member",
+			     d->get_entity (),
+			     d->cluster, (*modules)[key_origin],
+			     key_dep->cluster, key);
+	  }
+
+	  if (is_spec)
+	    {
+	      /* Key the general template to the originating decl.  */
+	      tree origin = get_originating_module_decl (key);
+	      sec.tree_node (CP_DECL_CONTEXT (origin));
+	      sec.tree_node (DECL_NAME (origin));
+
+	      unsigned origin_ident = import_entity_index (origin);
+	      module_state *origin_from = this;
+	      if (!(origin_ident & ~(~0u>>1)))
+		origin_from = import_entity_module (origin_ident);
+	      sec.u (origin_from->remap);
+	    }
+	  else
+	    sec.tree_node (NULL);
+	  count--;
+	}
+      }
+  gcc_assert (!count);
+  sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p);
+  dump.outdent ();
+}
+
+bool
+module_state::read_pendings (unsigned count)
+{
+  trees_in sec (this);
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".pnd"))
+    return false;
+
+  dump () && dump ("Reading %u pendings", count);
+  dump.indent ();
+
+  for (unsigned ix = 0; ix != count; ix++)
+    {
+      unsigned key_origin = slurp->remap_module (sec.u ());
+      unsigned key_index = sec.u ();
+      unsigned ent_index = sec.u ();
+      module_state *from = (*modules)[key_origin];
+      tree ns = sec.tree_node ();
+
+      if (!key_origin
+	  || key_index >= from->entity_num || ent_index >= entity_num
+	  || (ns && TREE_CODE (ns) != NAMESPACE_DECL))
+	sec.set_overrun ();
+
+      if (sec.get_overrun ())
+	break;
+
+      bool loaded = false;
+      dump () && dump ("%s keyed to %M[%u] entity:%u",
+		       ns ? "Specialization" : "Member",
+		       from, key_index, ent_index);
+      unsigned key_ident = from->entity_lwm + key_index;
+      if (pending_table->add (ns ? key_ident : ~key_ident,
+			      ent_index + entity_lwm))
+	{
+	  mc_slot &slot = (*entity_ary)[key_ident];
+	  if (slot.is_lazy ())
+	    slot.or_lazy (ns ? 1 : 2);
+	  else
+	    {
+	      tree key = slot;
+
+	      loaded = true;
+	      if (ns)
+		{
+		  if (key && TREE_CODE (key) == TEMPLATE_DECL)
+		    DECL_MODULE_PENDING_SPECIALIZATIONS_P (key) = true;
+		  else
+		    sec.set_overrun ();
+		}
+	      else
+		{
+		  if (key && TREE_CODE (key) == TYPE_DECL)
+		    DECL_MODULE_PENDING_MEMBERS_P (key) = true;
+		  else
+		    sec.set_overrun ();
+		}
+	    }
+	}
+
+      if (ns)
+	{
+	  /* We also need to mark the namespace binding of the
+	     originating template, so we know to set its pending
+	     specializations flag, when we load it.  */
+	  tree name = sec.tree_node ();
+	  unsigned origin = slurp->remap_module (sec.u ());
+	  if (!origin || !name || TREE_CODE (name) != IDENTIFIER_NODE)
+	    sec.set_overrun ();
+	  if (sec.get_overrun ())
+	    break;
+
+	  module_state *origin_from = (*modules)[origin];
+	  if (!loaded
+	      && (origin_from->is_header ()
+		  || (origin_from->is_partition ()
+		      || origin_from->is_module ())))
+	    note_pending_specializations (ns, name, origin_from->is_header ());
+	}
+    }
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+/* Return true if module MOD cares about lazy specializations keyed to
+   possibly duplicated entity bindings.  */
+
+bool
+lazy_specializations_p (unsigned mod, bool header_p, bool partition_p)
+{
+  module_state *module = (*modules)[mod];
+
+  if (module->is_header ())
+    return header_p;
+
+  if (module->is_module () || module->is_partition ())
+    return partition_p;
+
+  return false;
+}
+
+/* Read & write locations.  */
+enum loc_kind {
+  LK_ORDINARY,
+  LK_MACRO,
+  LK_IMPORT_ORDINARY,
+  LK_IMPORT_MACRO,
+  LK_ADHOC,
+  LK_RESERVED,
+};
+
+static const module_state *
+module_for_ordinary_loc (location_t loc)
+{
+  unsigned pos = 1;
+  unsigned len = modules->length () - pos;
+
+  while (len)
+    {
+      unsigned half = len / 2;
+      module_state *probe = (*modules)[pos + half];
+      if (loc < probe->ordinary_locs.first)
+	len = half;
+      else if (loc < probe->ordinary_locs.second)
+	return probe;
+      else
+	{
+	  pos += half + 1;
+	  len = len - (half + 1);
+	}
+    }
+
+  return NULL;
+}
+
+static const module_state *
+module_for_macro_loc (location_t loc)
+{
+  unsigned pos = 1;
+  unsigned len = modules->length () - pos;
+
+  while (len)
+    {
+      unsigned half = len / 2;
+      module_state *probe = (*modules)[pos + half];
+      if (loc >= probe->macro_locs.second)
+	len = half;
+      else if (loc >= probe->macro_locs.first)
+	return probe;
+      else
+	{
+	  pos += half + 1;
+	  len = len - (half + 1);
+	}
+    }
+
+  return NULL;
+}
+
+location_t
+module_state::imported_from () const
+{
+  location_t from = loc;
+  line_map_ordinary const *fmap
+    = linemap_check_ordinary (linemap_lookup (line_table, from));
+
+  if (MAP_MODULE_P (fmap))
+    from = linemap_included_from (fmap);
+
+  return from;
+}
+
+/* If we're not streaming, record that we need location LOC.
+   Otherwise stream it.  */
+
+void
+module_state::write_location (bytes_out &sec, location_t loc)
+{
+  if (!sec.streaming_p ())
+    /* This is where we should note we use this location.  See comment
+       about write_ordinary_maps.  */
+    return;
+
+  if (loc < RESERVED_LOCATION_COUNT)
+    {
+      dump (dumper::LOCATION) && dump ("Reserved location %u", unsigned (loc));
+      sec.u (LK_RESERVED + loc);
+    }
+  else if (IS_ADHOC_LOC (loc))
+    {
+      dump (dumper::LOCATION) && dump ("Adhoc location");
+      sec.u (LK_ADHOC);
+      location_t locus = get_location_from_adhoc_loc (line_table, loc);
+      write_location (sec, locus);
+      source_range range = get_range_from_loc (line_table, loc);
+      if (range.m_start == locus)
+	/* Compress.  */
+	range.m_start = UNKNOWN_LOCATION;
+      write_location (sec, range.m_start);
+      write_location (sec, range.m_finish);
+    }
+  else if (IS_MACRO_LOC (loc))
+    {
+      if (const loc_spans::span *span = spans.macro (loc))
+	{
+	  unsigned off = MAX_LOCATION_T - loc;
+
+	  off -= span->macro_delta;
+
+	  sec.u (LK_MACRO);
+	  sec.u (off);
+	  dump (dumper::LOCATION)
+	    && dump ("Macro location %u output %u", loc, off);
+	}
+      else if (const module_state *import = module_for_macro_loc (loc))
+	{
+	  unsigned off = import->macro_locs.second - loc - 1;
+	  sec.u (LK_IMPORT_MACRO);
+	  sec.u (import->remap);
+	  sec.u (off);
+	  dump (dumper::LOCATION)
+	    && dump ("Imported macro location %u output %u:%u",
+		     loc, import->remap, off);
+	}
+      else
+	gcc_unreachable ();
+    }
+  else if (IS_ORDINARY_LOC (loc))
+    {
+      if (const loc_spans::span *span = spans.ordinary (loc))
+	{
+	  unsigned off = loc;
+
+	  off += span->ordinary_delta;
+	  sec.u (LK_ORDINARY);
+	  sec.u (off);
+
+	  dump (dumper::LOCATION)
+	    && dump ("Ordinary location %u output %u", loc, off);
+	}
+      else if (const module_state *import = module_for_ordinary_loc (loc))
+	{
+	  unsigned off = loc - import->ordinary_locs.first;
+	  sec.u (LK_IMPORT_ORDINARY);
+	  sec.u (import->remap);
+	  sec.u (off);
+	  dump (dumper::LOCATION)
+	    && dump ("Imported ordinary location %u output %u:%u",
+		     import->remap, import->remap, off);
+	}
+      else
+	gcc_unreachable ();
+    }
+  else
+    gcc_unreachable ();
+}
+
+location_t
+module_state::read_location (bytes_in &sec) const
+{
+  location_t locus = UNKNOWN_LOCATION;
+  unsigned kind = sec.u ();
+  switch (kind)
+     {
+    default:
+      {
+	if (kind < LK_RESERVED + RESERVED_LOCATION_COUNT)
+	  locus = location_t (kind - LK_RESERVED);
+	else
+	  sec.set_overrun ();
+	dump (dumper::LOCATION)
+	  && dump ("Reserved location %u", unsigned (locus));
+      }
+      break;
+
+     case LK_ADHOC:
+      {
+	dump (dumper::LOCATION) && dump ("Adhoc location");
+	locus = read_location (sec);
+	source_range range;
+	range.m_start = read_location (sec);
+	if (range.m_start == UNKNOWN_LOCATION)
+	  range.m_start = locus;
+	range.m_finish = read_location (sec);
+	if (locus != loc && range.m_start != loc && range.m_finish != loc)
+	  locus = get_combined_adhoc_loc (line_table, locus, range, NULL);
+      }
+      break;
+
+    case LK_MACRO:
+      {
+	unsigned off = sec.u ();
+
+	if (macro_locs.first)
+	  {
+	    location_t adjusted = MAX_LOCATION_T - off;
+	    adjusted -= slurp->loc_deltas.second;
+	    if (adjusted < macro_locs.first)
+	      sec.set_overrun ();
+	    else if (adjusted < macro_locs.second)
+	      locus = adjusted;
+	    else
+	      sec.set_overrun ();
+	  }
+	else
+	  locus = loc;
+	dump (dumper::LOCATION)
+	  && dump ("Macro %u becoming %u", off, locus);
+      }
+      break;
+
+    case LK_ORDINARY:
+      {
+	unsigned off = sec.u ();
+	if (ordinary_locs.second)
+	  {
+	    location_t adjusted = off;
+
+	    adjusted += slurp->loc_deltas.first;
+	    if (adjusted >= ordinary_locs.second)
+	      sec.set_overrun ();
+	    else if (adjusted >= ordinary_locs.first)
+	      locus = adjusted;
+	    else if (adjusted < spans.main_start ())
+	      locus = off;
+	  }
+	else
+	  locus = loc;
+
+	dump (dumper::LOCATION)
+	  && dump ("Ordinary location %u becoming %u", off, locus);
+      }
+      break;
+
+     case LK_IMPORT_MACRO:
+     case LK_IMPORT_ORDINARY:
+       {
+	 unsigned mod = sec.u ();
+	 unsigned off = sec.u ();
+	 const module_state *import = NULL;
+
+	 if (!mod && !slurp->remap)
+	   /* This is an early read of a partition location during the
+	      read of our ordinary location map.  */
+	   import = this;
+	 else
+	   {
+	     mod = slurp->remap_module (mod);
+	     if (!mod)
+	       sec.set_overrun ();
+	     else
+	       import = (*modules)[mod];
+	   }
+
+	 if (import)
+	   {
+	     if (kind == LK_IMPORT_MACRO)
+	       {
+		 if (!import->macro_locs.first)
+		   locus = import->loc;
+		 else if (off < import->macro_locs.second - macro_locs.first)
+		   locus = import->macro_locs.second - off - 1;
+		 else
+		   sec.set_overrun ();
+	       }
+	     else
+	       {
+		 if (!import->ordinary_locs.second)
+		   locus = import->loc;
+		 else if (off < (import->ordinary_locs.second
+			    - import->ordinary_locs.first))
+		   locus = import->ordinary_locs.first + off;
+		 else
+		   sec.set_overrun ();
+	       }
+	   }
+       }
+       break;
+    }
+
+  return locus;
+}
+
+/* Prepare the span adjustments.  */
+
+// FIXME:QOI I do not prune the unreachable locations.  Modules with
+// textually-large GMFs could well cause us to run out of locations.
+// Regular single-file modules could also be affected.  We should
+// determine which locations we need to represent, so that we do not
+// grab more locations than necessary.  An example is in
+// write_macro_maps where we work around macro expansions that are not
+// covering any locations -- the macro expands to nothing.  Perhaps we
+// should decompose locations so that we can have a more graceful
+// degradation upon running out?
+
+location_map_info
+module_state::write_prepare_maps (module_state_config *)
+{
+  dump () && dump ("Preparing locations");
+  dump.indent ();
+
+  dump () && dump ("Reserved locations [%u,%u) macro [%u,%u)",
+		   spans[loc_spans::SPAN_RESERVED].ordinary.first,
+		   spans[loc_spans::SPAN_RESERVED].ordinary.second,
+		   spans[loc_spans::SPAN_RESERVED].macro.first,
+		   spans[loc_spans::SPAN_RESERVED].macro.second);
+
+  location_map_info info;
+
+  info.num_maps.first = info.num_maps.second = 0;
+
+  /* Figure the alignment of ordinary location spans.  */
+  unsigned max_range = 0;
+  for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
+    {
+      loc_spans::span &span = spans[ix];
+      line_map_ordinary const *omap
+	= linemap_check_ordinary (linemap_lookup (line_table,
+						  span.ordinary.first));
+
+      /* We should exactly match up.  */
+      gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first);
+
+      line_map_ordinary const *fmap = omap;
+      for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+	{
+	  /* We should never find a module linemap in an interval.  */
+	  gcc_checking_assert (!MAP_MODULE_P (omap));
+
+	  if (max_range < omap->m_range_bits)
+	    max_range = omap->m_range_bits;
+	}
+
+      unsigned count = omap - fmap;
+      info.num_maps.first += count;
+
+      if (span.macro.first != span.macro.second)
+	{
+	  count = linemap_lookup_macro_index (line_table, span.macro.first) + 1;
+	  count -= linemap_lookup_macro_index (line_table,
+					       span.macro.second - 1);
+	  dump (dumper::LOCATION) && dump ("Span:%u %u macro maps", ix, count);
+	  info.num_maps.second += count;
+	}
+    }
+
+  /* Adjust the maps.  Ordinary ones ascend, and we must maintain
+     alignment.  Macro ones descend, but are unaligned.  */
+  location_t ord_off = spans[loc_spans::SPAN_FIRST].ordinary.first;
+  location_t mac_off = spans[loc_spans::SPAN_FIRST].macro.second;
+  location_t range_mask = (1u << max_range) - 1;
+
+  dump () && dump ("Ordinary maps range bits:%u, preserve:%x, zero:%u",
+		   max_range, ord_off & range_mask, ord_off & ~range_mask);
+
+  for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
+    {
+      loc_spans::span &span = spans[ix];
+
+      span.macro_delta = mac_off - span.macro.second;
+      mac_off -= span.macro.second - span.macro.first;
+      dump () && dump ("Macro span:%u [%u,%u):%u->%d(%u)", ix,
+		       span.macro.first, span.macro.second,
+		       span.macro.second - span.macro.first,
+		       span.macro_delta, span.macro.first + span.macro_delta);
+
+      line_map_ordinary const *omap
+	= linemap_check_ordinary (linemap_lookup (line_table,
+						  span.ordinary.first));
+      location_t base = MAP_START_LOCATION (omap);
+
+      /* Preserve the low MAX_RANGE bits of base by incrementing ORD_OFF.  */
+      unsigned low_bits = base & range_mask;
+      if ((ord_off & range_mask) > low_bits)
+	low_bits += range_mask + 1;
+      ord_off = (ord_off & ~range_mask) + low_bits;
+      span.ordinary_delta = ord_off - base;
+
+      for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+	{
+	  location_t start_loc = MAP_START_LOCATION (omap);
+	  unsigned to = start_loc + span.ordinary_delta;
+	  location_t end_loc = MAP_START_LOCATION (omap + 1);
+	  
+	  dump () && dump ("Ordinary span:%u [%u,%u):%u->%d(%u)", ix, start_loc,
+			   end_loc, end_loc - start_loc,
+			   span.ordinary_delta, to);
+
+	  /* There should be no change in the low order bits.  */
+	  gcc_checking_assert (((start_loc ^ to) & range_mask) == 0);
+	}
+      /* The ending serialized value.  */
+      ord_off = span.ordinary.second + span.ordinary_delta;
+    }
+
+  dump () && dump ("Ordinary hwm:%u macro lwm:%u", ord_off, mac_off);
+
+  dump.outdent ();
+
+  info.max_range = max_range;
+
+  return info;
+}
+
+bool
+module_state::read_prepare_maps (const module_state_config *cfg)
+{
+  location_t ordinary = line_table->highest_location + 1;
+  ordinary = ((ordinary + (1u << cfg->ordinary_loc_align))
+	      & ~((1u << cfg->ordinary_loc_align) - 1));
+  ordinary += cfg->ordinary_locs;
+
+  location_t macro = LINEMAPS_MACRO_LOWEST_LOCATION (line_table);
+  macro -= cfg->macro_locs;
+
+  if (ordinary < LINE_MAP_MAX_LOCATION_WITH_COLS
+      && macro >= LINE_MAP_MAX_LOCATION)
+    /* OK, we have enough locations.  */
+    return true;
+
+  ordinary_locs.first = ordinary_locs.second = 0;
+  macro_locs.first = macro_locs.second = 0;
+
+  static bool informed = false;
+  if (!informed)
+    {
+      /* Just give the notice once.  */
+      informed = true;
+      inform (loc, "unable to represent further imported source locations");
+    }
+
+  return false;
+}
+
+/* Write the location maps.  This also determines the shifts for the
+   location spans.  */
+
+void
+module_state::write_ordinary_maps (elf_out *to, location_map_info &info,
+				   module_state_config *cfg, bool has_partitions,
+				   unsigned *crc_p)
+{
+  dump () && dump ("Writing ordinary location maps");
+  dump.indent ();
+
+  vec<const char *> filenames;
+  filenames.create (20);
+
+  /* Determine the unique filenames.  */
+  // FIXME:QOI We should find the set of filenames when working out
+  // which locations we actually need.  See write_prepare_maps.
+  for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
+    {
+      loc_spans::span &span = spans[ix];
+      line_map_ordinary const *omap
+	= linemap_check_ordinary (linemap_lookup (line_table,
+						  span.ordinary.first));
+
+      /* We should exactly match up.  */
+      gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first);
+
+      for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+	{
+	  const char *fname = ORDINARY_MAP_FILE_NAME (omap);
+
+	  /* We should never find a module linemap in an interval.  */
+	  gcc_checking_assert (!MAP_MODULE_P (omap));
+
+	  /* We expect very few filenames, so just an array.  */
+	  for (unsigned jx = filenames.length (); jx--;)
+	    {
+	      const char *name = filenames[jx];
+	      if (0 == strcmp (name, fname))
+		{
+		  /* Reset the linemap's name, because for things like
+		     preprocessed input we could have multple
+		     instances of the same name, and we'd rather not
+		     percolate that.  */
+		  const_cast<line_map_ordinary *> (omap)->to_file = name;
+		  fname = NULL;
+		  break;
+		}
+	    }
+	  if (fname)
+	    filenames.safe_push (fname);
+	}
+    }
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  /* Write the filenames.  */
+  unsigned len = filenames.length ();
+  sec.u (len);
+  dump () && dump ("%u source file names", len);
+  for (unsigned ix = 0; ix != len; ix++)
+    {
+      const char *fname = filenames[ix];
+      dump (dumper::LOCATION) && dump ("Source file[%u]=%s", ix, fname);
+      sec.str (fname);
+    }
+
+  location_t offset = spans[loc_spans::SPAN_FIRST].ordinary.first;
+  location_t range_mask = (1u << info.max_range) - 1;
+
+  dump () && dump ("Ordinary maps:%u, range bits:%u, preserve:%x, zero:%u",
+		   info.num_maps.first, info.max_range, offset & range_mask,
+		   offset & ~range_mask);
+  sec.u (info.num_maps.first);	/* Num maps.  */
+  sec.u (info.max_range);		/* Maximum range bits  */
+  sec.u (offset & range_mask);	/* Bits to preserve.  */
+  sec.u (offset & ~range_mask);
+
+  for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
+    {
+      loc_spans::span &span = spans[ix];
+      line_map_ordinary const *omap
+	= linemap_check_ordinary (linemap_lookup (line_table,
+						  span.ordinary.first));
+      for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+	{
+	  location_t start_loc = MAP_START_LOCATION (omap);
+	  unsigned to = start_loc + span.ordinary_delta;
+
+	  dump (dumper::LOCATION)
+	    && dump ("Span:%u ordinary [%u,%u)->%u", ix, start_loc,
+		     MAP_START_LOCATION (omap + 1), to);
+
+	  /* There should be no change in the low order bits.  */
+	  gcc_checking_assert (((start_loc ^ to) & range_mask) == 0);
+	  sec.u (to);
+
+	  /* Making accessors just for here, seems excessive.  */
+	  sec.u (omap->reason);
+	  sec.u (omap->sysp);
+	  sec.u (omap->m_range_bits);
+	  sec.u (omap->m_column_and_range_bits - omap->m_range_bits);
+
+	  const char *fname = ORDINARY_MAP_FILE_NAME (omap);
+	  for (unsigned ix = 0; ix != filenames.length (); ix++)
+	    if (filenames[ix] == fname)
+	      {
+		sec.u (ix);
+		break;
+	      }
+	  sec.u (ORDINARY_MAP_STARTING_LINE_NUMBER (omap));
+
+	  /* Write the included from location, which means reading it
+	     while reading in the ordinary maps.  So we'd better not
+	     be getting ahead of ourselves.  */
+	  location_t from = linemap_included_from (omap);
+	  gcc_checking_assert (from < MAP_START_LOCATION (omap));
+	  if (from != UNKNOWN_LOCATION && has_partitions)
+	    {
+	      /* A partition's span will have a from pointing at a
+		 MODULE_INC.  Find that map's from.  */
+	      line_map_ordinary const *fmap
+		= linemap_check_ordinary (linemap_lookup (line_table, from));
+	      if (MAP_MODULE_P (fmap))
+		from = linemap_included_from (fmap);
+	    }
+	  write_location (sec, from);
+	}
+      /* The ending serialized value.  */
+      offset = MAP_START_LOCATION (omap) + span.ordinary_delta;
+    }
+  dump () && dump ("Ordinary location hwm:%u", offset);
+  sec.u (offset);
+
+  // Record number of locations and alignment.
+  cfg->ordinary_loc_align = info.max_range;
+  cfg->ordinary_locs = offset;
+
+  filenames.release ();
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".olm"), crc_p);
+  dump.outdent ();
+}
+
+void
+module_state::write_macro_maps (elf_out *to, location_map_info &info,
+				module_state_config *cfg, unsigned *crc_p)
+{
+  dump () && dump ("Writing macro location maps");
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  dump () && dump ("Macro maps:%u", info.num_maps.second);
+  sec.u (info.num_maps.second);
+
+  location_t offset = spans[loc_spans::SPAN_FIRST].macro.second;
+  sec.u (offset);
+
+  unsigned macro_num = 0;
+  for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
+    {
+      loc_spans::span &span = spans[ix];
+      if (span.macro.first == span.macro.second)
+	continue;
+
+      for (unsigned first
+	     = linemap_lookup_macro_index (line_table, span.macro.second - 1);
+	   first < LINEMAPS_MACRO_USED (line_table);
+	   first++)
+	{
+	  line_map_macro const *mmap
+	    = LINEMAPS_MACRO_MAP_AT (line_table, first);
+	  location_t start_loc = MAP_START_LOCATION (mmap);
+	  if (start_loc < span.macro.first)
+	    break;
+	  if (macro_num == info.num_maps.second)
+	    {
+	      /* We're ending on an empty macro expansion.  The
+		 preprocessor doesn't prune such things.  */
+	      // FIXME:QOI This is an example of the non-pruning of
+	      // locations.  See write_prepare_maps.
+	      gcc_checking_assert (!mmap->n_tokens);
+	      continue;
+	    }
+
+	  sec.u (offset);
+	  sec.u (mmap->n_tokens);
+	  sec.cpp_node (mmap->macro);
+	  write_location (sec, mmap->expansion);
+	  const location_t *locs = mmap->macro_locations;
+	  /* There are lots of identical runs.  */
+	  location_t prev = UNKNOWN_LOCATION;
+	  unsigned count = 0;
+	  unsigned runs = 0;
+	  for (unsigned jx = mmap->n_tokens * 2; jx--;)
+	    {
+	      location_t tok_loc = locs[jx];
+	      if (tok_loc == prev)
+		{
+		  count++;
+		  continue;
+		}
+	      runs++;
+	      sec.u (count);
+	      count = 1;
+	      prev = tok_loc;
+	      write_location (sec, tok_loc);
+	    }
+	  sec.u (count);
+	  dump (dumper::LOCATION)
+	    && dump ("Span:%u macro:%u %I %u/%u*2 locations [%u,%u)->%u",
+		     ix, macro_num, identifier (mmap->macro),
+		     runs, mmap->n_tokens,
+		     start_loc, start_loc + mmap->n_tokens,
+		     start_loc + span.macro_delta);
+	  macro_num++;
+	  offset -= mmap->n_tokens;
+	  gcc_checking_assert (offset == start_loc + span.macro_delta);
+	}
+    }
+  dump () && dump ("Macro location lwm:%u", offset);
+  sec.u (offset);
+  gcc_assert (macro_num == info.num_maps.second);
+
+  cfg->macro_locs = MAX_LOCATION_T + 1 - offset;
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".mlm"), crc_p);
+  dump.outdent ();
+}
+
+bool
+module_state::read_ordinary_maps ()
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".olm"))
+    return false;
+  dump () && dump ("Reading ordinary location maps");
+  dump.indent ();
+
+  /* Read the filename table.  */
+  unsigned len = sec.u ();
+  dump () && dump ("%u source file names", len);
+  vec<const char *> filenames;
+  filenames.create (len);
+  for (unsigned ix = 0; ix != len; ix++)
+    {
+      size_t l;
+      const char *buf = sec.str (&l);
+      char *fname = XNEWVEC (char, l + 1);
+      memcpy (fname, buf, l + 1);
+      dump (dumper::LOCATION) && dump ("Source file[%u]=%s", ix, fname);
+      /* We leak these names into the line-map table.  But it
+	 doesn't own them.  */
+      filenames.quick_push (fname);
+    }
+
+  unsigned num_ordinary = sec.u (); 
+  unsigned max_range = sec.u ();
+  unsigned low_bits = sec.u ();
+  location_t zero = sec.u ();
+  location_t range_mask = (1u << max_range) - 1;
+
+  dump () && dump ("Ordinary maps:%u, range bits:%u, preserve:%x, zero:%u",
+		   num_ordinary, max_range, low_bits, zero);
+
+  location_t offset = line_table->highest_location + 1;
+  /* Ensure offset doesn't go backwards at the start.  */
+  if ((offset & range_mask) > low_bits)
+    offset += range_mask + 1;
+  offset = (offset & ~range_mask);
+
+  bool propagated = spans.maybe_propagate (this, offset + low_bits);
+
+  line_map_ordinary *maps = static_cast<line_map_ordinary *>
+    (line_map_new_raw (line_table, false, num_ordinary));
+
+  location_t lwm = offset;
+  slurp->loc_deltas.first = offset - zero;
+  ordinary_locs.first = zero + low_bits + slurp->loc_deltas.first;
+  dump () && dump ("Ordinary loc delta %d", slurp->loc_deltas.first);
+
+  for (unsigned ix = 0; ix != num_ordinary && !sec.get_overrun (); ix++)
+    {
+      line_map_ordinary *map = &maps[ix];
+      unsigned hwm = sec.u ();
+
+      /* Record the current HWM so that the below read_location is
+	 ok.  */
+      ordinary_locs.second = hwm + slurp->loc_deltas.first;
+      map->start_location = hwm + (offset - zero);
+      if (map->start_location < lwm)
+	sec.set_overrun ();
+      lwm = map->start_location;
+      dump (dumper::LOCATION) && dump ("Map:%u %u->%u", ix, hwm, lwm);
+      map->reason = lc_reason (sec.u ());
+      map->sysp = sec.u ();
+      map->m_range_bits = sec.u ();
+      map->m_column_and_range_bits = map->m_range_bits + sec.u ();
+
+      unsigned fnum = sec.u ();
+      map->to_file = (fnum < filenames.length () ? filenames[fnum] : "");
+      map->to_line = sec.u ();
+
+      /* Root the outermost map at our location.  */
+      location_t from = read_location (sec);
+      map->included_from = from != UNKNOWN_LOCATION ? from : loc;
+    }
+
+  location_t hwm = sec.u ();
+  ordinary_locs.second = hwm + slurp->loc_deltas.first;
+
+  /* highest_location is the one handed out, not the next one to
+     hand out.  */
+  line_table->highest_location = ordinary_locs.second - 1;
+
+  if (line_table->highest_location >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    /* We shouldn't run out of locations, as we checked before
+       starting.  */
+    sec.set_overrun ();
+  dump () && dump ("Ordinary location hwm:%u", ordinary_locs.second);
+
+  if (propagated)
+    spans.close ();
+
+  filenames.release ();
+  
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+
+  return true;
+}
+
+bool
+module_state::read_macro_maps ()
+{
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".mlm"))
+    return false;
+  dump () && dump ("Reading macro location maps");
+  dump.indent ();
+
+  unsigned num_macros = sec.u ();
+  location_t zero = sec.u ();
+  dump () && dump ("Macro maps:%u zero:%u", num_macros, zero);
+
+  bool propagated = spans.maybe_propagate (this);
+
+  location_t offset = LINEMAPS_MACRO_LOWEST_LOCATION (line_table);
+  slurp->loc_deltas.second = zero - offset;
+  macro_locs.second = zero - slurp->loc_deltas.second;
+  dump () && dump ("Macro loc delta %d", slurp->loc_deltas.second);
+
+  for (unsigned ix = 0; ix != num_macros && !sec.get_overrun (); ix++)
+    {
+      unsigned lwm = sec.u ();
+      /* Record the current LWM so that the below read_location is
+	 ok.  */
+      macro_locs.first = lwm - slurp->loc_deltas.second;
+
+      unsigned n_tokens = sec.u ();
+      cpp_hashnode *node = sec.cpp_node ();
+      location_t exp_loc = read_location (sec);
+
+      const line_map_macro *macro
+	= linemap_enter_macro (line_table, node, exp_loc, n_tokens);
+      if (!macro)
+	/* We shouldn't run out of locations, as we checked that we
+	   had enough before starting.  */
+	break;
+
+      location_t *locs = macro->macro_locations;
+      location_t tok_loc = UNKNOWN_LOCATION;
+      unsigned count = sec.u ();
+      unsigned runs = 0;
+      for (unsigned jx = macro->n_tokens * 2; jx-- && !sec.get_overrun ();)
+	{
+	  while (!count-- && !sec.get_overrun ())
+	    {
+	      runs++;
+	      tok_loc = read_location (sec);
+	      count = sec.u ();
+	    }
+	  locs[jx] = tok_loc;
+	}
+      if (count)
+	sec.set_overrun ();
+      dump (dumper::LOCATION)
+	&& dump ("Macro:%u %I %u/%u*2 locations [%u,%u)",
+		 ix, identifier (node), runs, n_tokens,
+		 MAP_START_LOCATION (macro),
+		 MAP_START_LOCATION (macro) + n_tokens);
+    }
+  location_t lwm = sec.u ();
+  macro_locs.first = lwm - slurp->loc_deltas.second;
+
+  dump () && dump ("Macro location lwm:%u", macro_locs.first);
+
+  if (propagated)
+    spans.close ();
+
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+
+  return true;
+}
+
+/* Serialize the definition of MACRO.  */
+
+void
+module_state::write_define (bytes_out &sec, const cpp_macro *macro, bool located)
+{
+  sec.u (macro->count);
+
+  sec.b (macro->fun_like);
+  sec.b (macro->variadic);
+  sec.b (macro->syshdr);
+  sec.bflush ();
+
+  if (located)
+    write_location (sec, macro->line);
+  if (macro->fun_like)
+    {
+      sec.u (macro->paramc);
+      const cpp_hashnode *const *parms = macro->parm.params;
+      for (unsigned ix = 0; ix != macro->paramc; ix++)
+	sec.cpp_node (parms[ix]);
+    }
+
+  unsigned len = 0;
+  for (unsigned ix = 0; ix != macro->count; ix++)
+    {
+      const cpp_token *token = &macro->exp.tokens[ix];
+      if (located)
+	write_location (sec, token->src_loc);
+      sec.u (token->type);
+      sec.u (token->flags);
+      switch (cpp_token_val_index (token))
+	{
+	default:
+	  gcc_unreachable ();
+
+	case CPP_TOKEN_FLD_ARG_NO:
+	  /* An argument reference.  */
+	  sec.u (token->val.macro_arg.arg_no);
+	  sec.cpp_node (token->val.macro_arg.spelling);
+	  break;
+
+	case CPP_TOKEN_FLD_NODE:
+	  /* An identifier.  */
+	  sec.cpp_node (token->val.node.node);
+	  if (token->val.node.spelling == token->val.node.node)
+	    /* The spelling will usually be the same.  so optimize
+	       that.  */
+	    sec.str (NULL, 0);
+	  else
+	    sec.cpp_node (token->val.node.spelling);
+	  break;
+
+	case CPP_TOKEN_FLD_NONE:
+	  break;
+
+	case CPP_TOKEN_FLD_STR:
+	  /* A string, number or comment.  Not always NUL terminated,
+	     we stream out in a single contatenation with embedded
+	     NULs as that's a safe default.  */
+	  len += token->val.str.len + 1;
+	  sec.u (token->val.str.len);
+	  break;
+
+	case CPP_TOKEN_FLD_SOURCE:
+	case CPP_TOKEN_FLD_TOKEN_NO:
+	case CPP_TOKEN_FLD_PRAGMA:
+	  /* These do not occur inside a macro itself.  */
+	  gcc_unreachable ();
+	}
+    }
+
+  if (len)
+    {
+      char *ptr = reinterpret_cast<char *> (sec.buf (len));
+      len = 0;
+      for (unsigned ix = 0; ix != macro->count; ix++)
+	{
+	  const cpp_token *token = &macro->exp.tokens[ix];
+	  if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR)
+	    {
+	      memcpy (ptr + len, token->val.str.text,
+		      token->val.str.len);
+	      len += token->val.str.len;
+	      ptr[len++] = 0;
+	    }
+	}
+    }
+}
+
+/* Read a macro definition.  */
+
+cpp_macro *
+module_state::read_define (bytes_in &sec, cpp_reader *reader, bool located) const
+{
+  unsigned count = sec.u ();
+  /* We rely on knowing cpp_reader's hash table is ident_hash, and
+     it's subobject allocator is stringpool_ggc_alloc and that is just
+     a wrapper for ggc_alloc_atomic.  */
+  cpp_macro *macro
+    = (cpp_macro *)ggc_alloc_atomic (sizeof (cpp_macro)
+				     + sizeof (cpp_token) * (count - !!count));
+  memset (macro, 0, sizeof (cpp_macro) + sizeof (cpp_token) * (count - !!count));
+
+  macro->count = count;
+  macro->kind = cmk_macro;
+  macro->imported = true;
+
+  macro->fun_like = sec.b ();
+  macro->variadic = sec.b ();
+  macro->syshdr = sec.b ();
+  sec.bflush ();
+
+  macro->line = located ? read_location (sec) : loc;
+
+  if (macro->fun_like)
+    {
+      unsigned paramc = sec.u ();
+      cpp_hashnode **params
+	= (cpp_hashnode **)ggc_alloc_atomic (sizeof (cpp_hashnode *) * paramc);
+      macro->paramc = paramc;
+      macro->parm.params = params;
+      for (unsigned ix = 0; ix != paramc; ix++)
+	params[ix] = sec.cpp_node ();
+    }
+
+  unsigned len = 0;
+  for (unsigned ix = 0; ix != count && !sec.get_overrun (); ix++)
+    {
+      cpp_token *token = &macro->exp.tokens[ix];
+      token->src_loc = located ? read_location (sec) : loc;
+      token->type = cpp_ttype (sec.u ());
+      token->flags = sec.u ();
+      switch (cpp_token_val_index (token))
+	{
+	default:
+	  sec.set_overrun ();
+	  break;
+
+	case CPP_TOKEN_FLD_ARG_NO:
+	  /* An argument reference.  */
+	  {
+	    unsigned arg_no = sec.u ();
+	    if (arg_no - 1 >= macro->paramc)
+	      sec.set_overrun ();
+	    token->val.macro_arg.arg_no = arg_no;
+	    token->val.macro_arg.spelling = sec.cpp_node ();
+	  }
+	  break;
+
+	case CPP_TOKEN_FLD_NODE:
+	  /* An identifier.  */
+	  token->val.node.node = sec.cpp_node ();
+	  token->val.node.spelling = sec.cpp_node ();
+	  if (!token->val.node.spelling)
+	    token->val.node.spelling = token->val.node.node;
+	  break;
+
+	case CPP_TOKEN_FLD_NONE:
+	  break;
+
+	case CPP_TOKEN_FLD_STR:
+	  /* A string, number or comment.  */
+	  token->val.str.len = sec.u ();
+	  len += token->val.str.len + 1;
+	  break;
+	}
+    }
+
+  if (len)
+    if (const char *ptr = reinterpret_cast<const char *> (sec.buf (len)))
+      {
+	/* There should be a final NUL.  */
+	if (ptr[len-1])
+	  sec.set_overrun ();
+	/* cpp_alloc_token_string will add a final NUL.  */
+	const unsigned char *buf
+	  = cpp_alloc_token_string (reader, (const unsigned char *)ptr, len - 1);
+	len = 0;
+	for (unsigned ix = 0; ix != count && !sec.get_overrun (); ix++)
+	  {
+	    cpp_token *token = &macro->exp.tokens[ix];
+	    if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR)
+	      {
+		token->val.str.text = buf + len;
+		len += token->val.str.len;
+		if (buf[len++])
+		  sec.set_overrun ();
+	      }
+	  }
+      }
+
+  if (sec.get_overrun ())
+    return NULL;
+  return macro;
+}
+
+/* Exported macro data.  */
+struct macro_export {
+  cpp_macro *def;
+  location_t undef_loc;
+
+  macro_export ()
+    :def (NULL), undef_loc (UNKNOWN_LOCATION)
+  {
+  }
+};
+
+/* Imported macro data.  */
+class macro_import {
+public:
+  struct slot {
+#if defined (WORDS_BIGENDIAN) && SIZEOF_VOID_P == 8
+    int offset;
+#endif
+    /* We need to ensure we don't use the LSB for representation, as
+       that's the union discriminator below.  */
+    unsigned bits;
+
+#if !(defined (WORDS_BIGENDIAN) && SIZEOF_VOID_P == 8)
+    int offset;
+#endif
+
+  public:
+    enum Layout {
+      L_DEF = 1,
+      L_UNDEF = 2,
+      L_BOTH = 3,
+      L_MODULE_SHIFT = 2
+    };
+
+  public:
+    /* Not a regular ctor, because we put it in a union, and that's
+       not allowed in C++ 98.  */
+    static slot ctor (unsigned module, unsigned defness)
+    {
+      gcc_checking_assert (defness);
+      slot s;
+      s.bits = defness | (module << L_MODULE_SHIFT);
+      s.offset = -1;
+      return s;
+    }
+
+  public:
+    unsigned get_defness () const
+    {
+      return bits & L_BOTH;
+    }
+    unsigned get_module () const
+    {
+      return bits >> L_MODULE_SHIFT;
+    }
+    void become_undef ()
+    {
+      bits &= ~unsigned (L_DEF);
+      bits |= unsigned (L_UNDEF);
+    }
+  };
+
+private:
+  typedef vec<slot, va_heap, vl_embed> ary_t;
+  union either {
+    /* Discriminated by bits 0|1 != 0.  The expected case is that
+       there will be exactly one slot per macro, hence the effort of
+       packing that.  */
+    ary_t *ary;
+    slot single;
+  } u;
+
+public:
+  macro_import ()
+  {
+    u.ary = NULL;
+  }
+
+private:
+  bool single_p () const
+  {
+    return u.single.bits & slot::L_BOTH;
+  }
+  bool occupied_p () const
+  {
+    return u.ary != NULL;
+  }
+
+public:
+  unsigned length () const
+  {
+    gcc_checking_assert (occupied_p ());
+    return single_p () ? 1 : u.ary->length ();
+  }
+  slot &operator[] (unsigned ix)
+  {
+    gcc_checking_assert (occupied_p ());
+    if (single_p ())
+      {
+	gcc_checking_assert (!ix);
+	return u.single;
+      }
+    else
+      return (*u.ary)[ix];
+  }
+
+public:
+  slot &exported ();
+  slot &append (unsigned module, unsigned defness);
+};
+
+/* O is a new import to append to the list for.  If we're an empty
+   set, initialize us.  */
+
+macro_import::slot &
+macro_import::append (unsigned module, unsigned defness)
+{
+  if (!occupied_p ())
+    {
+      u.single = slot::ctor (module, defness);
+      return u.single;
+    }
+  else
+    {
+      bool single = single_p ();
+      ary_t *m = single ? NULL : u.ary;
+      vec_safe_reserve (m, 1 + single);
+      if (single)
+	m->quick_push (u.single);
+      u.ary = m;
+      return *u.ary->quick_push (slot::ctor (module, defness));
+    }
+}
+
+/* We're going to export something.  Make sure the first import slot
+   is us.  */
+
+macro_import::slot &
+macro_import::exported ()
+{
+  if (occupied_p () && !(*this)[0].get_module ())
+    {
+      slot &res = (*this)[0];
+      res.bits |= slot::L_DEF;
+      return res;
+    }
+
+  slot *a = &append (0, slot::L_DEF);
+  if (!single_p ())
+    {
+      slot &f = (*this)[0];
+      std::swap (f, *a);
+      a = &f;
+    }
+  return *a;
+}
+
+/* The import (&exported) macros.  cpp_hasnode's deferred field
+   indexes this array (offset by 1, so zero means 'not present'.  */
+
+static vec<macro_import, va_heap, vl_embed> *macro_imports;
+
+/* The exported macros.  A macro_import slot's zeroth element's offset
+   indexes this array.  If the zeroth slot is not for module zero,
+   there is no export.  */
+
+static vec<macro_export, va_heap, vl_embed> *macro_exports;
+
+/* The reachable set of header imports from this TU.  */
+
+static GTY(()) bitmap headers;
+
+/* Get the (possibly empty) macro imports for NODE.  */
+
+static macro_import &
+get_macro_imports (cpp_hashnode *node)
+{
+  if (node->deferred)
+    return (*macro_imports)[node->deferred - 1];
+
+  vec_safe_reserve (macro_imports, 1);
+  node->deferred = macro_imports->length () + 1;
+  return *vec_safe_push (macro_imports, macro_import ());
+}
+
+/* Get the macro export for export EXP of NODE.  */
+
+static macro_export &
+get_macro_export (macro_import::slot &slot)
+{
+  if (slot.offset >= 0)
+    return (*macro_exports)[slot.offset];
+
+  vec_safe_reserve (macro_exports, 1);
+  slot.offset = macro_exports->length ();
+  return *macro_exports->quick_push (macro_export ());
+}
+
+/* If NODE is an exportable macro, add it to the export set.  */
+
+static int
+maybe_add_macro (cpp_reader *, cpp_hashnode *node, void *data_)
+{
+  bool exporting = false;
+
+  if (cpp_user_macro_p (node))
+    if (cpp_macro *macro = node->value.macro)
+      /* Ignore imported, builtins, command line and forced header macros.  */
+      if (!macro->imported && !macro->lazy && macro->line >= spans.main_start ())
+	{
+	  gcc_checking_assert (macro->kind == cmk_macro);
+	  /* I don't want to deal with this corner case, that I suspect is
+	     a devil's advocate reading of the standard.  */
+	  gcc_checking_assert (!macro->extra_tokens);
+
+	  macro_import::slot &slot = get_macro_imports (node).exported ();
+	  macro_export &exp = get_macro_export (slot);
+	  exp.def = macro;
+	  exporting = true;
+	}
+
+  if (!exporting && node->deferred)
+    {
+      macro_import &imports = (*macro_imports)[node->deferred - 1];
+      macro_import::slot &slot = imports[0];
+      if (!slot.get_module ())
+	{
+	  gcc_checking_assert (slot.get_defness ());
+	  exporting = true;
+	}
+    }
+
+  if (exporting)
+    static_cast<vec<cpp_hashnode *> *> (data_)->safe_push (node);
+
+  return 1; /* Don't stop.  */
+}
+
+/* Order cpp_hashnodes A_ and B_ by their exported macro locations.  */
+
+static int
+macro_loc_cmp (const void *a_, const void *b_)
+{
+  const cpp_hashnode *node_a = *(const cpp_hashnode *const *)a_;
+  macro_import &import_a = (*macro_imports)[node_a->deferred - 1];
+  const macro_export &export_a = (*macro_exports)[import_a[0].offset];
+  location_t loc_a = export_a.def ? export_a.def->line : export_a.undef_loc;
+
+  const cpp_hashnode *node_b = *(const cpp_hashnode *const *)b_;
+  macro_import &import_b = (*macro_imports)[node_b->deferred - 1];
+  const macro_export &export_b = (*macro_exports)[import_b[0].offset];
+  location_t loc_b = export_b.def ? export_b.def->line : export_b.undef_loc;
+
+  if (loc_a < loc_b)
+    return +1;
+  else if (loc_a > loc_b)
+    return -1;
+  else
+    return 0;
+}
+
+/* Write out the exported defines.  This is two sections, one
+   containing the definitions, the other a table of node names.  */
+
+unsigned
+module_state::write_macros (elf_out *to, cpp_reader *reader, unsigned *crc_p)
+{
+  dump () && dump ("Writing macros");
+  dump.indent ();
+
+  vec<cpp_hashnode *> macros;
+  macros.create (100);
+  cpp_forall_identifiers (reader, maybe_add_macro, &macros);
+
+  dump (dumper::MACRO) && dump ("No more than %u macros", macros.length ());
+
+  macros.qsort (macro_loc_cmp);
+
+  /* Write the defs */
+  bytes_out sec (to);
+  sec.begin ();
+
+  unsigned count = 0;
+  for (unsigned ix = macros.length (); ix--;)
+    {
+      cpp_hashnode *node = macros[ix];
+      macro_import::slot &slot = (*macro_imports)[node->deferred - 1][0];
+      gcc_assert (!slot.get_module () && slot.get_defness ());
+
+      macro_export &mac = (*macro_exports)[slot.offset];
+      gcc_assert (!!(slot.get_defness () & macro_import::slot::L_UNDEF)
+		  == (mac.undef_loc != UNKNOWN_LOCATION)
+		  && !!(slot.get_defness () & macro_import::slot::L_DEF)
+		  == (mac.def != NULL));
+
+      if (IDENTIFIER_KEYWORD_P (identifier (node)))
+	{
+	  warning_at (mac.def->line, 0,
+		      "not exporting %<#define %E%> as it is a keyword",
+		      identifier (node));
+	  slot.offset = 0;
+	  continue;
+	}
+
+      count++;
+      slot.offset = sec.pos;
+      dump (dumper::MACRO)
+	&& dump ("Writing macro %s%s%s %I at %u",
+		 slot.get_defness () & macro_import::slot::L_UNDEF
+		 ? "#undef" : "",
+		 slot.get_defness () == macro_import::slot::L_BOTH
+		 ? " & " : "",
+		 slot.get_defness () & macro_import::slot::L_DEF
+		 ? "#define" : "",
+		 identifier (node), slot.offset);
+      if (mac.undef_loc != UNKNOWN_LOCATION)
+	write_location (sec, mac.undef_loc);
+      if (mac.def)
+	write_define (sec, mac.def);
+    }
+  sec.end (to, to->name (MOD_SNAME_PFX ".def"), crc_p);
+
+  if (count)
+    {
+      /* Write the table.  */
+      bytes_out sec (to);
+      sec.begin ();
+      sec.u (count);
+
+      for (unsigned ix = macros.length (); ix--;)
+	{
+	  const cpp_hashnode *node = macros[ix];
+	  macro_import::slot &slot = (*macro_imports)[node->deferred - 1][0];
+
+	  if (slot.offset)
+	    {
+	      sec.cpp_node (node);
+	      sec.u (slot.get_defness ());
+	      sec.u (slot.offset);
+	    }
+	}
+      sec.end (to, to->name (MOD_SNAME_PFX ".mac"), crc_p);
+    }
+
+  macros.release ();
+  dump.outdent ();
+  return count;
+}
+
+bool
+module_state::read_macros ()
+{
+  /* Get the def section.  */
+  if (!slurp->macro_defs.begin (loc, from (), MOD_SNAME_PFX ".def"))
+    return false;
+
+  /* Get the tbl section, if there are defs. */
+  if (slurp->macro_defs.more_p ()
+      && !slurp->macro_tbl.begin (loc, from (), MOD_SNAME_PFX ".mac"))
+    return false;
+
+  return true;
+}
+
+/* Install the macro name table.  */
+
+void
+module_state::install_macros ()
+{
+  bytes_in &sec = slurp->macro_tbl;
+  if (!sec.size)
+    return;
+
+  dump () && dump ("Reading macro table %M", this);
+  dump.indent ();
+
+  unsigned count = sec.u ();
+  dump () && dump ("%u macros", count);
+  while (count--)
+    {
+      cpp_hashnode *node = sec.cpp_node ();
+      macro_import &imp = get_macro_imports (node);
+      unsigned flags = sec.u () & macro_import::slot::L_BOTH;
+      if (!flags)
+	sec.set_overrun ();
+
+      if (sec.get_overrun ())
+	break;
+
+      macro_import::slot &slot = imp.append (mod, flags);
+      slot.offset = sec.u ();
+
+      dump (dumper::MACRO)
+	&& dump ("Read %s macro %s%s%s %I at %u",
+		 imp.length () > 1 ? "add" : "new",
+		 flags & macro_import::slot::L_UNDEF ? "#undef" : "",
+		 flags == macro_import::slot::L_BOTH ? " & " : "",
+		 flags & macro_import::slot::L_DEF ? "#define" : "",
+		 identifier (node), slot.offset);
+
+      /* We'll leak an imported definition's TOKEN_FLD_STR's data
+	 here.  But that only happens when we've had to resolve the
+	 deferred macro before this import -- why are you doing
+	 that?  */
+      if (cpp_macro *cur = cpp_set_deferred_macro (node))
+	if (!cur->imported)
+	  {
+	    macro_import::slot &slot = imp.exported ();
+	    macro_export &exp = get_macro_export (slot);
+	    exp.def = cur;
+	    dump (dumper::MACRO)
+	      && dump ("Saving current #define %I", identifier (node));
+	  }
+    }
+
+  /* We're now done with the table.  */
+  elf_in::release (slurp->from, sec);
+
+  dump.outdent ();
+}
+
+/* Import the transitive macros.  */
+
+void
+module_state::import_macros ()
+{
+  bitmap_ior_into (headers, slurp->headers);
+
+  bitmap_iterator bititer;
+  unsigned bitnum;
+  EXECUTE_IF_SET_IN_BITMAP (slurp->headers, 0, bitnum, bititer)
+    (*modules)[bitnum]->install_macros ();
+}
+
+/* NODE is being undefined at LOC.  Record it in the export table, if
+   necessary.  */
+
+void
+module_state::undef_macro (cpp_reader *, location_t loc, cpp_hashnode *node)
+{
+  if (!node->deferred)
+    /* The macro is not imported, so our undef is irrelevant.  */
+    return;
+
+  unsigned n = dump.push (NULL);
+
+  macro_import::slot &slot = (*macro_imports)[node->deferred - 1].exported ();
+  macro_export &exp = get_macro_export (slot);
+
+  exp.undef_loc = loc;
+  slot.become_undef ();
+  exp.def = NULL;
+
+  dump (dumper::MACRO) && dump ("Recording macro #undef %I", identifier (node));
+
+  dump.pop (n);
+}
+
+/* NODE is a deferred macro node.  Determine the definition and return
+   it, with NULL if undefined.  May issue diagnostics.
+
+   This can leak memory, when merging declarations -- the string
+   contents (TOKEN_FLD_STR) of each definition are allocated in
+   unreclaimable cpp objstack.  Only one will win.  However, I do not
+   expect this to be common -- mostly macros have a single point of
+   definition.  Perhaps we could restore the objstack to its position
+   after the first imported definition (if that wins)?  The macros
+   themselves are GC'd.  */
+
+cpp_macro *
+module_state::deferred_macro (cpp_reader *reader, location_t loc,
+			      cpp_hashnode *node)
+{
+  macro_import &imports = (*macro_imports)[node->deferred - 1];
+
+  unsigned n = dump.push (NULL);
+  dump (dumper::MACRO) && dump ("Deferred macro %I", identifier (node));
+
+  bitmap visible (BITMAP_GGC_ALLOC ());
+
+  if (!((imports[0].get_defness () & macro_import::slot::L_UNDEF)
+	&& !imports[0].get_module ()))
+    {
+      /* Calculate the set of visible header imports.  */
+      bitmap_copy (visible, headers);
+      for (unsigned ix = imports.length (); ix--;)
+	{
+	  const macro_import::slot &slot = imports[ix];
+	  unsigned mod = slot.get_module ();
+	  if ((slot.get_defness () & macro_import::slot::L_UNDEF)
+	      && bitmap_bit_p (visible, mod))
+	    {
+	      bitmap arg = mod ? (*modules)[mod]->slurp->headers : headers;
+	      bitmap_and_compl_into (visible, arg);
+	      bitmap_set_bit (visible, mod);
+	    }
+	}
+    }
+  bitmap_set_bit (visible, 0);
+
+  /* Now find the macros that are still visible.  */
+  bool failed = false;
+  cpp_macro *def = NULL;
+  vec<macro_export> defs;
+  defs.create (imports.length ());
+  for (unsigned ix = imports.length (); ix--;)
+    {
+      const macro_import::slot &slot = imports[ix];
+      unsigned mod = slot.get_module ();
+      if (bitmap_bit_p (visible, mod))
+	{
+	  macro_export *pushed = NULL;
+	  if (mod)
+	    {
+	      const module_state *imp = (*modules)[mod];
+	      bytes_in &sec = imp->slurp->macro_defs;
+	      if (!sec.get_overrun ())
+		{
+		  dump (dumper::MACRO)
+		    && dump ("Reading macro %s%s%s %I module %M at %u",
+			     slot.get_defness () & macro_import::slot::L_UNDEF
+			     ? "#undef" : "",
+			     slot.get_defness () == macro_import::slot::L_BOTH
+			     ? " & " : "",
+			     slot.get_defness () & macro_import::slot::L_DEF
+			     ? "#define" : "",
+			     identifier (node), imp, slot.offset);
+		  sec.random_access (slot.offset);
+
+		  macro_export exp;
+		  if (slot.get_defness () & macro_import::slot::L_UNDEF)
+		    exp.undef_loc = imp->read_location (sec);
+		  if (slot.get_defness () & macro_import::slot::L_DEF)
+		    exp.def = imp->read_define (sec, reader);
+		  if (sec.get_overrun ())
+		    error_at (loc, "macro definitions of %qE corrupted",
+			      imp->name);
+		  else
+		    pushed = defs.quick_push (exp);
+		}
+	    }
+	  else
+	    pushed = defs.quick_push ((*macro_exports)[slot.offset]);
+	  if (pushed && pushed->def)
+	    {
+	      if (!def)
+		def = pushed->def;
+	      else if (cpp_compare_macros (def, pushed->def))
+		failed = true;
+	    }
+	}
+    }
+
+  if (failed)
+    {
+      /* If LOC is the first loc, this is the end of file check, which
+	 is a warning.  */
+      if (loc == MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0)))
+	warning_at (loc, OPT_Winvalid_imported_macros,
+		    "inconsistent imported macro definition %qE",
+		    identifier (node));
+      else
+	error_at (loc, "inconsistent imported macro definition %qE",
+		  identifier (node));
+      for (unsigned ix = defs.length (); ix--;)
+	{
+	  macro_export &exp = defs[ix];
+	  if (exp.undef_loc)
+	    inform (exp.undef_loc, "%<#undef %E%>", identifier (node));
+	  if (exp.def)
+	    inform (exp.def->line, "%<#define %s%>",
+		    cpp_macro_definition (reader, node, exp.def));
+	}
+      def = NULL;
+    }
+
+  defs.release ();
+
+  dump.pop (n);
+
+  return def;
+}
+
+/* Stream the static aggregates.  Sadly some headers (ahem:
+   iostream) contain static vars, and rely on them to run global
+   ctors.  */
+unsigned
+module_state::write_inits (elf_out *to, depset::hash &table, unsigned *crc_ptr)
+{
+  if (!static_aggregates && !tls_aggregates)
+    return 0;
+
+  dump () && dump ("Writing initializers");
+  dump.indent ();
+
+  static_aggregates = nreverse (static_aggregates);
+  tls_aggregates = nreverse (tls_aggregates);
+
+  unsigned count = 0;
+  trees_out sec (to, this, table, ~0u);
+  sec.begin ();
+
+  tree list = static_aggregates;
+  for (int passes = 0; passes != 2; passes++)
+    {
+      for (tree init = list; init; init = TREE_CHAIN (init), count++)
+	if (TREE_LANG_FLAG_0 (init))
+	  {
+	    tree decl = TREE_VALUE (init);
+
+	    dump ("Initializer:%u for %N", count, decl);
+	    sec.tree_node (decl);
+	  }
+
+      list = tls_aggregates;
+    }
+  
+  sec.end (to, to->name (MOD_SNAME_PFX ".ini"), crc_ptr);
+  dump.outdent ();
+
+  return count;
+}
+
+bool
+module_state::read_inits (unsigned count)
+{
+  trees_in sec (this);
+  if (!sec.begin (loc, from (), from ()->find (MOD_SNAME_PFX ".ini")))
+    return false;
+  dump () && dump ("Reading %u initializers", count);
+  dump.indent ();
+
+  for (unsigned ix = 0; ix != count; ix++)
+    {
+      /* Merely referencing the decl causes its initializer to be read
+	 and added to the correct list.  */
+      tree decl = sec.tree_node ();
+
+      if (sec.get_overrun ())
+	break;
+      if (decl)
+	dump ("Initializer:%u for %N", count, decl);
+    }
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;  
+  return true;
+}
+
+void
+module_state::write_counts (elf_out *to, unsigned counts[MSC_HWM],
+			    unsigned *crc_ptr)
+{
+  bytes_out cfg (to);
+
+  cfg.begin ();
+
+  for (unsigned ix = MSC_HWM; ix--;)
+    cfg.u (counts[ix]);
+
+  if (dump ())
+    {
+      dump ("Cluster sections are [%u,%u)",
+	    counts[MSC_sec_lwm], counts[MSC_sec_hwm]);
+      dump ("Bindings %u", counts[MSC_bindings]);
+      dump ("Pendings %u", counts[MSC_pendings]);
+      dump ("Entities %u", counts[MSC_entities]);
+      dump ("Namespaces %u", counts[MSC_namespaces]);
+      dump ("Macros %u", counts[MSC_macros]);
+      dump ("Initializers %u", counts[MSC_inits]);
+    }
+
+  cfg.end (to, to->name (MOD_SNAME_PFX ".cnt"), crc_ptr);
+}
+
+bool
+module_state::read_counts (unsigned counts[MSC_HWM])
+{
+  bytes_in cfg;
+
+  if (!cfg.begin (loc, from (), MOD_SNAME_PFX ".cnt"))
+    return false;
+
+  for (unsigned ix = MSC_HWM; ix--;)
+    counts[ix] = cfg.u ();
+
+  if (dump ())
+    {
+      dump ("Declaration sections are [%u,%u)",
+	    counts[MSC_sec_lwm], counts[MSC_sec_hwm]);
+      dump ("Bindings %u", counts[MSC_bindings]);
+      dump ("Pendings %u", counts[MSC_pendings]);
+      dump ("Entities %u", counts[MSC_entities]);
+      dump ("Namespaces %u", counts[MSC_namespaces]);
+      dump ("Macros %u", counts[MSC_macros]);
+      dump ("Initializers %u", counts[MSC_inits]);
+    }
+
+  return cfg.end (from ());
+}
+
+/* Tool configuration:  MOD_SNAME_PFX .config
+
+   This is data that confirms current state (or fails).  */
+
+void
+module_state::write_config (elf_out *to, module_state_config &config,
+			    unsigned inner_crc)
+{
+  bytes_out cfg (to);
+
+  cfg.begin ();
+
+  /* Write version and inner crc as u32 values, for easier
+     debug inspection.  */
+  dump () && dump ("Writing version=%V, inner_crc=%x",
+		   MODULE_VERSION, inner_crc);
+  cfg.u32 (unsigned (MODULE_VERSION));
+  cfg.u32 (inner_crc);
+
+  cfg.u (to->name (is_header () ? "" : get_flatname ()));
+
+  /* Configuration. */
+  dump () && dump ("Writing target='%s', host='%s'",
+		   TARGET_MACHINE, HOST_MACHINE);
+  unsigned target = to->name (TARGET_MACHINE);
+  unsigned host = (!strcmp (TARGET_MACHINE, HOST_MACHINE)
+		   ? target : to->name (HOST_MACHINE));
+  cfg.u (target);
+  cfg.u (host);
+
+  cfg.str (config.dialect_str);
+  cfg.u (extensions);
+
+  /* Global tree information.  We write the globals crc separately,
+     rather than mix it directly into the overall crc, as it is used
+     to ensure data match between instances of the compiler, not
+     integrity of the file.  */
+  dump () && dump ("Writing globals=%u, crc=%x",
+		   fixed_trees->length (), global_crc);
+  cfg.u (fixed_trees->length ());
+  cfg.u32 (global_crc);
+
+  if (is_partition ())
+    cfg.u (is_interface ());
+
+  cfg.u (config.num_imports);
+  cfg.u (config.num_partitions);
+
+  cfg.u (config.ordinary_locs);
+  cfg.u (config.macro_locs);
+  cfg.u (config.ordinary_loc_align);  
+
+  /* Now generate CRC, we'll have incorporated the inner CRC because
+     of its serialization above.  */
+  cfg.end (to, to->name (MOD_SNAME_PFX ".cfg"), &crc);
+  dump () && dump ("Writing CRC=%x", crc);
+}
+
+void
+module_state::note_cmi_name ()
+{
+  if (!cmi_noted_p && filename)
+    {
+      cmi_noted_p = true;
+      inform (loc, "compiled module file is %qs",
+	      maybe_add_cmi_prefix (filename));
+    }
+}
+
+bool
+module_state::read_config (module_state_config &config)
+{
+  bytes_in cfg;
+
+  if (!cfg.begin (loc, from (), MOD_SNAME_PFX ".cfg"))
+    return false;
+
+  /* Check version.  */
+  unsigned my_ver = MODULE_VERSION;
+  unsigned their_ver = cfg.u32 ();
+  dump () && dump  (my_ver == their_ver ? "Version %V"
+		    : "Expecting %V found %V", my_ver, their_ver);
+  if (their_ver != my_ver)
+    {
+      /* The compiler versions differ.  Close enough? */
+      verstr_t my_string, their_string;
+
+      version2string (my_ver, my_string);
+      version2string (their_ver, their_string);
+
+      /* Reject when either is non-experimental or when experimental
+	 major versions differ.  */
+      bool reject_p = ((!IS_EXPERIMENTAL (my_ver)
+			|| !IS_EXPERIMENTAL (their_ver)
+			|| MODULE_MAJOR (my_ver) != MODULE_MAJOR (their_ver))
+		       /* The 'I know what I'm doing' switch.  */
+		       && !flag_module_version_ignore);
+      bool inform_p = true;
+      if (reject_p)
+	{
+	  cfg.set_overrun ();
+	  error_at (loc, "compiled module is %sversion %s",
+		    IS_EXPERIMENTAL (their_ver) ? "experimental " : "",
+		    their_string);
+	}
+      else
+	inform_p = warning_at (loc, 0, "compiled module is %sversion %s",
+			     IS_EXPERIMENTAL (their_ver) ? "experimental " : "",
+			     their_string);
+
+      if (inform_p)
+	{
+	  inform (loc, "compiler is %sversion %s%s%s",
+		  IS_EXPERIMENTAL (my_ver) ? "experimental " : "",
+		  my_string,
+		  reject_p ? "" : flag_module_version_ignore
+		  ? ", be it on your own head!" : ", close enough?",
+		  reject_p ? "" : " \xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf");
+	  note_cmi_name ();
+	}
+
+      if (reject_p)
+	goto done;
+    }
+
+  /*  We wrote the inner crc merely to merge it, so simply read it
+      back and forget it.  */
+  cfg.u32 ();
+
+  /* Check module name.  */
+  {
+    const char *their_name = from ()->name (cfg.u ());
+    const char *our_name = "";
+
+    if (!is_header ())
+      our_name = get_flatname ();
+
+    /* Header units can be aliased, so name checking is
+       inappropriate.  */
+    if (0 != strcmp (their_name, our_name))
+      {
+	error_at (loc,
+		  their_name[0] && our_name[0] ? G_("module %qs found")
+		  : their_name[0]
+		  ? G_("header module expected, module %qs found")
+		  : G_("module %qs expected, header module found"),
+		  their_name[0] ? their_name : our_name);
+	cfg.set_overrun ();
+	goto done;
+      }
+  }
+
+  /* Check the CRC after the above sanity checks, so that the user is
+     clued in.  */
+  {
+    unsigned e_crc = crc;
+    crc = cfg.get_crc ();
+    dump () && dump ("Reading CRC=%x", crc);
+    if (!is_direct () && crc != e_crc)
+      {
+	error_at (loc, "module %qs CRC mismatch", get_flatname ());
+	cfg.set_overrun ();
+	goto done;
+      }
+  }
+
+  /* Check target & host.  */
+  {
+    const char *their_target = from ()->name (cfg.u ());
+    const char *their_host = from ()->name (cfg.u ());
+    dump () && dump ("Read target='%s', host='%s'", their_target, their_host);
+    if (strcmp (their_target, TARGET_MACHINE)
+	|| strcmp (their_host, HOST_MACHINE))
+      {
+	error_at (loc, "target & host is %qs:%qs, expected %qs:%qs",
+		  their_target, TARGET_MACHINE, their_host, HOST_MACHINE);
+	cfg.set_overrun ();
+	goto done;
+      }
+  }
+
+  /* Check compilation dialect.  This must match.  */
+  {
+    const char *their_dialect = cfg.str ();
+    if (strcmp (their_dialect, config.dialect_str))
+      {
+	error_at (loc, "language dialect differs %qs, expected %qs",
+		  their_dialect, config.dialect_str);
+	cfg.set_overrun ();
+	goto done;
+      }
+  }
+
+  /* Check for extensions.  If they set any, we must have them set
+     too.  */
+  {
+    unsigned ext = cfg.u ();
+    unsigned allowed = (flag_openmp ? SE_OPENMP : 0);
+
+    if (unsigned bad = ext & ~allowed)
+      {
+	if (bad & SE_OPENMP)
+	  error_at (loc, "module contains OpenMP, use %<-fopenmp%> to enable");
+	cfg.set_overrun ();
+	goto done;
+      }
+    extensions = ext;
+  }
+
+  /* Check global trees.  */
+  {
+    unsigned their_fixed_length = cfg.u ();
+    unsigned their_fixed_crc = cfg.u32 ();
+    dump () && dump ("Read globals=%u, crc=%x",
+		     their_fixed_length, their_fixed_crc);
+    if (!flag_preprocess_only
+	&& (their_fixed_length != fixed_trees->length ()
+	    || their_fixed_crc != global_crc))
+      {
+	error_at (loc, "fixed tree mismatch");
+	cfg.set_overrun ();
+	goto done;
+      }
+  }
+
+  /* All non-partitions are interfaces.  */
+  interface_p = !is_partition () || cfg.u ();
+
+  config.num_imports = cfg.u ();
+  config.num_partitions = cfg.u ();
+
+  config.ordinary_locs = cfg.u ();
+  config.macro_locs = cfg.u ();
+  config.ordinary_loc_align = cfg.u ();
+
+ done:
+  return cfg.end (from ());
+}
+
+/* Use ELROND format to record the following sections:
+     qualified-names	    : binding value(s)
+     MOD_SNAME_PFX.README   : human readable, strings
+     MOD_SNAME_PFX.ENV      : environment strings, strings
+     MOD_SNAME_PFX.nms 	    : namespace hierarchy
+     MOD_SNAME_PFX.bnd      : binding table
+     MOD_SNAME_PFX.spc      : specialization table
+     MOD_SNAME_PFX.imp      : import table
+     MOD_SNAME_PFX.ent      : entity table
+     MOD_SNAME_PFX.prt      : partitions table
+     MOD_SNAME_PFX.olm      : ordinary line maps
+     MOD_SNAME_PFX.mlm      : macro line maps
+     MOD_SNAME_PFX.def      : macro definitions
+     MOD_SNAME_PFX.mac      : macro index
+     MOD_SNAME_PFX.ini      : inits
+     MOD_SNAME_PFX.cnt      : counts
+     MOD_SNAME_PFX.cfg      : config data
+*/
+
+void
+module_state::write (elf_out *to, cpp_reader *reader)
+{
+  /* Figure out remapped module numbers, which might elide
+     partitions.  */
+  bitmap partitions = NULL;
+  if (!is_header () && !is_partition ())
+    partitions = BITMAP_GGC_ALLOC ();
+
+  unsigned mod_hwm = 1;
+  for (unsigned ix = 1; ix != modules->length (); ix++)
+    {
+      module_state *imp = (*modules)[ix];
+
+      /* Promote any non-partition direct import from a partition, unless
+	 we're a partition.  */
+      if (!is_partition () && !imp->is_partition ()
+	  && imp->is_partition_direct ())
+	imp->directness = MD_PURVIEW_DIRECT;
+
+      /* Write any import that is not a partition, unless we're a
+	 partition.  */
+      if (!partitions || !imp->is_partition ())
+	imp->remap = mod_hwm++;
+      else
+	{
+	  dump () && dump ("Partition %M %u", imp, ix);
+	  bitmap_set_bit (partitions, ix);
+	  imp->remap = 0;
+	  /* All interface partitions must be exported.  */
+	  if (imp->is_interface () && !bitmap_bit_p (exports, imp->mod))
+	    {
+	      error_at (imp->loc, "interface partition is not exported");
+	      bitmap_set_bit (exports, imp->mod);
+	    }
+
+	  /* All the partition entities should have been loaded when
+	     loading the partition.  */
+	  if (CHECKING_P)
+	    for (unsigned jx = 0; jx != imp->entity_num; jx++)
+	      {
+		mc_slot *slot = &(*entity_ary)[imp->entity_lwm + jx];
+		gcc_checking_assert (!slot->is_lazy ());
+	      }
+	}
+    }
+
+  if (partitions && bitmap_empty_p (partitions))
+    /* No partitions present.  */
+    partitions = nullptr;
+
+  /* Find the set of decls we must write out.  */
+  depset::hash table (DECL_NAMESPACE_BINDINGS (global_namespace)->size () * 8);
+  /* Add the specializations before the writables, so that we can
+     detect injected friend specializations.  */
+  table.add_specializations (true);
+  table.add_specializations (false);
+  table.add_namespace_entities (global_namespace, partitions);
+  if (class_members)
+    {
+      table.add_class_entities (class_members);
+      class_members = NULL;
+    }
+
+  /* Now join everything up.  */
+  table.find_dependencies ();
+
+  if (!table.finalize_dependencies ())
+    {
+      to->set_error ();
+      return;
+    }
+
+#if CHECKING_P
+  /* We're done verifying at-most once reading, reset to verify
+     at-most once writing.  */
+  note_defs = note_defs_table_t::create_ggc (1000);
+#endif
+
+  /* Determine Strongy Connected Components.  */
+  vec<depset *> sccs = table.connect ();
+
+  unsigned crc = 0;
+  module_state_config config;
+  location_map_info map_info = write_prepare_maps (&config);
+  unsigned counts[MSC_HWM];
+
+  config.num_imports = mod_hwm;
+  config.num_partitions = modules->length () - mod_hwm;
+  memset (counts, 0, sizeof (counts));
+
+  /* depset::cluster is the cluster number,
+     depset::section is unspecified scratch value.
+
+     The following loops make use of the tarjan property that
+     dependencies will be earlier in the SCCS array.  */
+
+  /* This first loop determines the number of depsets in each SCC, and
+     also the number of namespaces we're dealing with.  During the
+     loop, the meaning of a couple of depset fields now change:
+
+     depset::cluster -> size_of cluster, if first of cluster & !namespace
+     depset::section -> section number of cluster (if !namespace). */
+
+  unsigned n_spaces = 0;
+  counts[MSC_sec_lwm] = counts[MSC_sec_hwm] = to->get_section_limit ();
+  for (unsigned size, ix = 0; ix < sccs.length (); ix += size)
+    {
+      depset **base = &sccs[ix];
+
+      if (base[0]->get_entity_kind () == depset::EK_NAMESPACE)
+	{
+	  n_spaces++;
+	  size = 1;
+	}
+      else
+	{
+	  /* Count the members in this cluster.  */
+	  for (size = 1; ix + size < sccs.length (); size++)
+	    if (base[size]->cluster != base[0]->cluster)
+	      break;
+
+	  for (unsigned jx = 0; jx != size; jx++)
+	    {
+	      /* Set the section number.  */
+	      base[jx]->cluster = ~(~0u >> 1); /* A bad value.  */
+	      base[jx]->section = counts[MSC_sec_hwm];
+	    }
+
+	  /* Save the size in the first member's cluster slot.  */
+	  base[0]->cluster = size;
+
+	  counts[MSC_sec_hwm]++;
+	}
+    }
+
+  /* Write the clusters.  Namespace decls are put in the spaces array.
+     The meaning of depset::cluster changes to provide the
+     unnamed-decl count of the depset's decl (and remains zero for
+     non-decls and non-unnamed).  */
+  unsigned bytes = 0;
+  vec<depset *> spaces;
+  spaces.create (n_spaces);
+
+  for (unsigned size, ix = 0; ix < sccs.length (); ix += size)
+    {
+      depset **base = &sccs[ix];
+
+      if (base[0]->get_entity_kind () == depset::EK_NAMESPACE)
+	{
+	  tree decl = base[0]->get_entity ();
+	  if (decl == global_namespace)
+	    base[0]->cluster = 0;
+	  else if (!base[0]->is_import ())
+	    {
+	      base[0]->cluster = counts[MSC_entities]++;
+	      spaces.quick_push (base[0]);
+	      counts[MSC_namespaces]++;
+	      if (CHECKING_P)
+		{
+		  /* Add it to the entity map, such that we can tell it is
+		     part of us.  */
+		  bool existed;
+		  unsigned *slot = &entity_map->get_or_insert
+		    (DECL_UID (decl), &existed);
+		  if (existed)
+		    /* It must have come from a partition.  */
+		    gcc_checking_assert
+		      (import_entity_module (*slot)->is_partition ());
+		  *slot = ~base[0]->cluster;
+		}
+	      dump (dumper::CLUSTER) && dump ("Cluster namespace %N", decl);
+	    }
+	  size = 1;
+	}
+      else
+	{
+	  size = base[0]->cluster;
+
+	  /* Cluster is now used to number entities.  */
+	  base[0]->cluster = ~(~0u >> 1); /* A bad value.  */
+
+	  sort_cluster (&table, base, size);
+
+	  /* Record the section for consistency checking during stream
+	     out -- we don't want to start writing decls in different
+	     sections.  */
+	  table.section = base[0]->section;
+	  bytes += write_cluster (to, base, size, table, counts, &crc);
+	  table.section = 0;
+	}
+    }
+
+  /* We'd better have written as many sections and found as many
+     namespaces as we predicted.  */
+  gcc_assert (counts[MSC_sec_hwm] == to->get_section_limit ()
+	      && spaces.length () == counts[MSC_namespaces]);
+
+  /* Write the entitites.  None happens if we contain namespaces or
+     nothing. */
+  if (counts[MSC_entities])
+    write_entities (to, sccs, counts[MSC_entities], &crc);
+
+  /* Write the namespaces.  */
+  if (counts[MSC_namespaces])
+    write_namespaces (to, spaces, counts[MSC_namespaces], &crc);
+
+  /* Write the bindings themselves.  */
+  counts[MSC_bindings] = write_bindings (to, sccs, &crc);
+
+  /* Write the unnamed.  */
+  if (counts[MSC_pendings])
+    write_pendings (to, sccs, table, counts[MSC_pendings], &crc);
+
+  /* Write the import table.  */
+  if (config.num_imports > 1)
+    write_imports (to, &crc);
+
+  /* Write elided partition table.  */
+  if (config.num_partitions)
+    write_partitions (to, config.num_partitions, &crc);
+
+  /* Write the line maps.  */
+  write_ordinary_maps (to, map_info, &config, config.num_partitions, &crc);
+  write_macro_maps (to, map_info, &config, &crc);
+
+  if (is_header ())
+    {
+      counts[MSC_macros] = write_macros (to, reader, &crc);
+      counts[MSC_inits] = write_inits (to, table, &crc);
+    }
+
+  unsigned clusters = counts[MSC_sec_hwm] - counts[MSC_sec_lwm];
+  dump () && dump ("Wrote %u clusters, average %u bytes/cluster",
+		   clusters, (bytes + clusters / 2) / (clusters + !clusters));
+
+  write_counts (to, counts, &crc);
+
+  /* And finish up.  */
+  write_config (to, config, crc);
+
+  spaces.release ();
+  sccs.release ();
+
+  /* Human-readable info.  */
+  write_readme (to, config.dialect_str, extensions);
+
+  // FIXME:QOI:  Have a command line switch to control more detailed
+  // information (which might leak data you do not want to leak).
+  // Perhaps (some of) the write_readme contents should also be
+  // so-controlled.
+  if (false)
+    write_env (to);
+
+  trees_out::instrument ();
+  dump () && dump ("Wrote %u sections", to->get_section_limit ());
+}
+
+/* Initial read of a CMI.  Checks config, loads up imports and line
+   maps.  */
+
+bool
+module_state::read_initial (cpp_reader *reader)
+{
+  module_state_config config;
+  bool ok = true;
+
+  if (ok && !from ()->begin (loc))
+    ok = false;
+
+  if (ok && !read_config (config))
+    ok = false;
+
+  bool have_locs = ok && read_prepare_maps (&config);
+
+  /* Ordinary maps before the imports.  */
+  if (have_locs && !read_ordinary_maps ())
+    ok = false;
+
+  /* Allocate the REMAP vector.  */
+  slurp->alloc_remap (config.num_imports);
+
+  if (ok)
+    {
+      /* Read the import table.  Decrement current to stop this CMI
+	 from being evicted during the import. */
+      slurp->current--;
+      if (config.num_imports > 1 && !read_imports (reader, line_table))
+	ok = false;
+      slurp->current++;
+    }
+
+  /* Read the elided partition table, if we're the primary partition.  */
+  if (ok && config.num_partitions && is_module ()
+      && !read_partitions (config.num_partitions))
+    ok = false;
+
+  /* Determine the module's number.  */
+  gcc_checking_assert (mod == MODULE_UNKNOWN);
+  gcc_checking_assert (this != (*modules)[0]);
+
+  /* We'll run out of other resources before we run out of module
+     indices.  */
+  mod = modules->length ();
+  vec_safe_push (modules, this);
+
+  /* We always import and export ourselves. */
+  bitmap_set_bit (imports, mod);
+  bitmap_set_bit (exports, mod);
+
+  if (ok)
+    (*slurp->remap)[0] = mod << 1;
+  dump () && dump ("Assigning %M module number %u", this, mod);
+
+  /* We should not have been frozen during the importing done by
+     read_config.  */
+  gcc_assert (!from ()->is_frozen ());
+
+  /* Macro maps after the imports.  */
+  if (ok && have_locs && !read_macro_maps ())
+    ok = false;
+
+  gcc_assert (slurp->current == ~0u);
+  return ok;
+}
+
+/* Read a preprocessor state.  */
+
+bool
+module_state::read_preprocessor (bool outermost)
+{
+  gcc_checking_assert (is_header () && slurp
+		       && slurp->remap_module (0) == mod);
+
+  if (loadedness == ML_PREPROCESSOR)
+    return !(from () && from ()->get_error ());
+
+  bool ok = true;
+
+  /* Read direct header imports.  */
+  unsigned len = slurp->remap->length ();
+  for (unsigned ix = 1; ok && ix != len; ix++)
+    {
+      unsigned map = (*slurp->remap)[ix];
+      if (map & 1)
+	{
+	  module_state *import = (*modules)[map >> 1];
+	  if (import->is_header ())
+	    {
+	      ok = import->read_preprocessor (false);
+	      bitmap_ior_into (slurp->headers, import->slurp->headers);
+	    }
+	}
+    }
+
+  /* Record as a direct header.  */
+  if (ok)
+    bitmap_set_bit (slurp->headers, mod);
+
+  if (ok && !read_macros ())
+    ok = false;
+
+  loadedness = ML_PREPROCESSOR;
+  announce ("macros");
+
+  if (flag_preprocess_only)
+    /* We're done with the string table.  */
+    from ()->release ();
+
+  return check_read (outermost, ok);
+}
+
+static unsigned lazy_snum;
+
+static bool
+recursive_lazy (unsigned snum = ~0u)
+{
+  if (lazy_snum)
+    {
+      error_at (input_location, "recursive lazy load");
+      return true;
+    }
+
+  lazy_snum = snum;
+  return false;
+}
+
+/* Read language state.  */
+
+bool
+module_state::read_language (bool outermost)
+{
+  gcc_checking_assert (!lazy_snum);
+
+  if (loadedness == ML_LANGUAGE)
+    return !(slurp && from () && from ()->get_error ());
+
+  gcc_checking_assert (slurp && slurp->current == ~0u
+		       && slurp->remap_module (0) == mod);
+
+  bool ok = true;
+
+  /* Read direct imports.  */
+  unsigned len = slurp->remap->length ();
+  for (unsigned ix = 1; ok && ix != len; ix++)
+    {
+      unsigned map = (*slurp->remap)[ix];
+      if (map & 1)
+	{
+	  module_state *import = (*modules)[map >> 1];
+	  if (!import->read_language (false))
+	    ok = false;
+	}
+    }
+
+  unsigned counts[MSC_HWM];
+
+  if (ok && !read_counts (counts))
+    ok = false;
+
+  function_depth++; /* Prevent unexpected GCs.  */
+
+  /* Read the entity table.  */
+  entity_lwm = vec_safe_length (entity_ary);
+  if (ok && counts[MSC_entities]
+      && !read_entities (counts[MSC_entities],
+			 counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
+    ok = false;
+
+  /* Read the namespace hierarchy. */
+  if (ok && counts[MSC_namespaces]
+      && !read_namespaces (counts[MSC_namespaces]))
+    ok = false;
+
+  if (ok && !read_bindings (counts[MSC_bindings],
+			    counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
+    ok = false;
+
+  /* And unnamed.  */
+  if (ok && counts[MSC_pendings] && !read_pendings (counts[MSC_pendings]))
+    ok = false;
+
+  if (ok)
+    {
+      slurp->remaining = counts[MSC_sec_hwm] - counts[MSC_sec_lwm];
+      available_clusters += counts[MSC_sec_hwm] - counts[MSC_sec_lwm];
+    }
+
+  if (!flag_module_lazy
+      || (is_partition ()
+	  && module_interface_p ()
+	  && !module_partition_p ()))
+    {
+      /* Read the sections in forward order, so that dependencies are read
+	 first.  See note about tarjan_connect.  */
+      ggc_collect ();
+
+      lazy_snum = ~0u;
+
+      unsigned hwm = counts[MSC_sec_hwm];
+      for (unsigned ix = counts[MSC_sec_lwm]; ok && ix != hwm; ix++)
+	{
+	  if (!load_section (ix, NULL))
+	    {
+	      ok = false;
+	      break;
+	    }
+	  ggc_collect ();
+	}
+
+      lazy_snum = 0;
+
+      if (ok && CHECKING_P)
+	for (unsigned ix = 0; ix != entity_num; ix++)
+	  gcc_assert (!(*entity_ary)[ix + entity_lwm].is_lazy ());
+    }
+
+  // If the import is a header-unit, we need to register initializers
+  // of any static objects it contains (looking at you _Ioinit).
+  // Notice, the ordering of these initializers will be that of a
+  // dynamic initializer at this point in the current TU.  (Other
+  // instances of these objects in other TUs will be initialized as
+  // part of that TU's global initializers.)
+  if (ok && counts[MSC_inits] && !read_inits (counts[MSC_inits]))
+    ok = false;
+
+  function_depth--;
+  
+  announce (flag_module_lazy ? "lazy" : "imported");
+  loadedness = ML_LANGUAGE;
+
+  gcc_assert (slurp->current == ~0u);
+
+  /* We're done with the string table.  */
+  from ()->release ();
+
+  return check_read (outermost, ok);
+}
+
+bool
+module_state::maybe_defrost ()
+{
+  bool ok = true;
+  if (from ()->is_frozen ())
+    {
+      if (lazy_open >= lazy_limit)
+	freeze_an_elf ();
+      dump () && dump ("Defrosting '%s'", filename);
+      ok = from ()->defrost (maybe_add_cmi_prefix (filename));
+      lazy_open++;
+    }
+
+  return ok;
+}
+
+/* Load section SNUM, dealing with laziness.  It doesn't matter if we
+   have multiple concurrent loads, because we do not use TREE_VISITED
+   when reading back in.  */
+
+bool
+module_state::load_section (unsigned snum, mc_slot *mslot)
+{
+  if (from ()->get_error ())
+    return false;
+
+  if (snum >= slurp->current)
+    from ()->set_error (elf::E_BAD_LAZY);
+  else if (maybe_defrost ())
+    {
+      unsigned old_current = slurp->current;
+      slurp->current = snum;
+      slurp->lru = 0;  /* Do not swap out.  */
+      slurp->remaining--;
+      read_cluster (snum);
+      slurp->lru = ++lazy_lru;
+      slurp->current = old_current;
+    }
+  
+  if (mslot && mslot->is_lazy ())
+    {
+      /* Oops, the section didn't set this slot.  */
+      from ()->set_error (elf::E_BAD_DATA);
+      *mslot = NULL_TREE;
+    }
+
+  bool ok = !from ()->get_error ();
+  if (!ok)
+    {
+      error_at (loc, "failed to read compiled module cluster %u: %s",
+		snum, from ()->get_error (filename));
+      note_cmi_name ();
+    }
+
+  maybe_completed_reading ();
+
+  return ok;
+}
+
+void
+module_state::maybe_completed_reading ()
+{
+  if (loadedness == ML_LANGUAGE && slurp->current == ~0u && !slurp->remaining)
+    {
+      lazy_open--;
+      /* We no longer need the macros, all tokenizing has been done.  */
+      slurp->release_macros ();
+
+      from ()->end ();
+      slurp->close ();
+      slurped ();
+    }
+}
+
+/* After a reading operation, make sure things are still ok.  If not,
+   emit an error and clean up.  */
+
+bool
+module_state::check_read (bool outermost, bool ok)
+{
+  gcc_checking_assert (!outermost || slurp->current == ~0u);
+
+  if (!ok)
+    from ()->set_error ();
+
+  if (int e = from ()->get_error ())
+    {
+      error_at (loc, "failed to read compiled module: %s",
+		from ()->get_error (filename));
+      note_cmi_name ();
+
+      if (e == EMFILE
+	  || e == ENFILE
+#if MAPPED_READING
+	  || e == ENOMEM
+#endif
+	  || false)
+	inform (loc, "consider using %<-fno-module-lazy%>,"
+		" increasing %<-param-lazy-modules=%u%> value,"
+		" or increasing the per-process file descriptor limit",
+		param_lazy_modules);
+      else if (e == ENOENT)
+	inform (loc, "imports must be built before being imported");
+
+      if (outermost)
+	fatal_error (loc, "returning to the gate for a mechanical issue");
+
+      ok = false;
+    }
+
+  maybe_completed_reading ();
+
+  return ok;
+}
+
+/* Return the IDENTIFIER_NODE naming module IX.  This is the name
+   including dots.  */
+
+char const *
+module_name (unsigned ix, bool header_ok)
+{
+  if (modules)
+    {
+      module_state *imp = (*modules)[ix];
+
+      if (ix && !imp->name)
+	imp = imp->parent;
+
+      if (header_ok || !imp->is_header ())
+	return imp->get_flatname ();
+    }
+
+  return NULL;
+}
+
+/* Return the bitmap describing what modules are imported.  Remember,
+   we always import ourselves.  */
+
+bitmap
+get_import_bitmap ()
+{
+  return (*modules)[0]->imports;
+}
+
+/* Return the visible imports and path of instantiation for an
+   instantiation at TINST.  If TINST is nullptr, we're not in an
+   instantiation, and thus will return the visible imports of the
+   current TU (and NULL *PATH_MAP_P).   We cache the information on
+   the tinst level itself.  */
+
+static bitmap
+path_of_instantiation (tinst_level *tinst,  bitmap *path_map_p)
+{
+  gcc_checking_assert (modules_p ());
+
+  if (!tinst)
+    {
+      /* Not inside an instantiation, just the regular case.  */
+      *path_map_p = nullptr;
+      return get_import_bitmap ();
+    }
+
+  if (!tinst->path)
+    {
+      /* Calculate.  */
+      bitmap visible = path_of_instantiation (tinst->next, path_map_p);
+      bitmap path_map = *path_map_p;
+
+      if (!path_map)
+	{
+	  path_map = BITMAP_GGC_ALLOC ();
+	  bitmap_set_bit (path_map, 0);
+	}
+
+      tree decl = tinst->tldcl;
+      if (TREE_CODE (decl) == TREE_LIST)
+	decl = TREE_PURPOSE (decl);
+      if (TYPE_P (decl))
+	decl = TYPE_NAME (decl);
+
+      if (unsigned mod = get_originating_module (decl))
+	if (!bitmap_bit_p (path_map, mod))
+	  {
+	    /* This is brand new information!  */
+	    bitmap new_path = BITMAP_GGC_ALLOC ();
+	    bitmap_copy (new_path, path_map);
+	    bitmap_set_bit (new_path, mod);
+	    path_map = new_path;
+
+	    bitmap imports = (*modules)[mod]->imports;
+	    if (bitmap_intersect_compl_p (imports, visible))
+	      {
+		/* IMPORTS contains additional modules to VISIBLE.  */
+		bitmap new_visible = BITMAP_GGC_ALLOC ();
+
+		bitmap_ior (new_visible, visible, imports);
+		visible = new_visible;
+	      }
+	  }
+
+      tinst->path = path_map;
+      tinst->visible = visible;
+    }
+
+  *path_map_p = tinst->path;
+  return tinst->visible;
+}
+
+/* Return the bitmap describing what modules are visible along the
+   path of instantiation.  If we're not an instantiation, this will be
+   the visible imports of the TU.  *PATH_MAP_P is filled in with the
+   modules owning the instantiation path -- we see the module-linkage
+   entities of those modules.  */
+
+bitmap
+module_visible_instantiation_path (bitmap *path_map_p)
+{
+  if (!modules_p ())
+    return NULL;
+
+  return path_of_instantiation (current_instantiation (), path_map_p);
+}
+
+/* We've just directly imported IMPORT.  Update our import/export
+   bitmaps.  IS_EXPORT is true if we're reexporting the OTHER.  */
+
+void
+module_state::set_import (module_state const *import, bool is_export)
+{
+  gcc_checking_assert (this != import);
+
+  /* We see IMPORT's exports (which includes IMPORT).  If IMPORT is
+     the primary interface or a partition we'll see its imports.  */
+  bitmap_ior_into (imports, import->is_module () || import->is_partition ()
+		   ? import->imports : import->exports);
+
+  if (is_export)
+    /* We'll export OTHER's exports.  */
+    bitmap_ior_into (exports, import->exports);
+}
+
+/* Return the declaring entity of DECL.  That is the decl determining
+   how to decorate DECL with module information.  Returns NULL_TREE if
+   it's the global module.  */
+
+tree
+get_originating_module_decl (tree decl)
+{
+  /* An enumeration constant.  */
+  if (TREE_CODE (decl) == CONST_DECL
+      && DECL_CONTEXT (decl)
+      && (TREE_CODE (DECL_CONTEXT (decl)) == ENUMERAL_TYPE))
+    decl = TYPE_NAME (DECL_CONTEXT (decl));
+  else if (TREE_CODE (decl) == FIELD_DECL
+	   || TREE_CODE (decl) == USING_DECL)
+    {
+      decl = DECL_CONTEXT (decl);
+      if (TREE_CODE (decl) != FUNCTION_DECL)
+	decl = TYPE_NAME (decl);
+    }
+
+  gcc_checking_assert (TREE_CODE (decl) == TEMPLATE_DECL
+		       || TREE_CODE (decl) == FUNCTION_DECL
+		       || TREE_CODE (decl) == TYPE_DECL
+		       || TREE_CODE (decl) == VAR_DECL
+		       || TREE_CODE (decl) == CONCEPT_DECL
+		       || TREE_CODE (decl) == NAMESPACE_DECL);
+
+  for (;;)
+    {
+      /* Uninstantiated template friends are owned by the befriending
+	 class -- not their context.  */
+      if (TREE_CODE (decl) == TEMPLATE_DECL
+	  && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
+	decl = TYPE_NAME (DECL_CHAIN (decl));
+
+      int use;
+      if (tree ti = node_template_info (decl, use))
+	{
+	  decl = TI_TEMPLATE (ti);
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    {
+	      /* A friend template specialization.  */
+	      gcc_checking_assert (OVL_P (decl));
+	      return global_namespace;
+	    }
+	}
+      else
+	{
+	  tree ctx = CP_DECL_CONTEXT (decl);
+	  if (TREE_CODE (ctx) == NAMESPACE_DECL)
+	    break;
+
+	  if (TYPE_P (ctx))
+	    {
+	      ctx = TYPE_NAME (ctx);
+	      if (!ctx)
+		{
+		  /* Some kind of internal type.  */
+		  gcc_checking_assert (DECL_ARTIFICIAL (decl));
+		  return global_namespace;
+		}
+	    }
+	  decl = ctx;
+	}
+    }
+
+  return decl;
+}
+
+int
+get_originating_module (tree decl, bool for_mangle)
+{
+  tree owner = get_originating_module_decl (decl);
+
+  if (!DECL_LANG_SPECIFIC (owner))
+    return for_mangle ? -1 : 0;
+
+  if (for_mangle
+      && (DECL_MODULE_EXPORT_P (owner) || !DECL_MODULE_PURVIEW_P (owner)))
+    return -1;
+
+  if (!DECL_MODULE_IMPORT_P (owner))
+    return 0;
+
+  return get_importing_module (owner);
+}
+
+unsigned
+get_importing_module (tree decl, bool flexible)
+{
+  unsigned index = import_entity_index (decl, flexible);
+  if (index == ~(~0u >> 1))
+    return -1;
+  module_state *module = import_entity_module (index);
+
+  return module->mod;
+}
+
+/* Is it permissible to redeclare DECL.  */
+
+bool
+module_may_redeclare (tree decl)
+{
+  module_state *me = (*modules)[0];
+  module_state *them = me;
+  if (DECL_LANG_SPECIFIC (decl) && DECL_MODULE_IMPORT_P (decl))
+    {
+      /* We can be given the TEMPLATE_RESULT.  We want the
+	 TEMPLATE_DECL.  */
+      int use_tpl = -1;
+      if (tree ti = node_template_info (decl, use_tpl))
+	{
+	  tree tmpl = TI_TEMPLATE (ti);
+	  if (DECL_TEMPLATE_RESULT (tmpl) == decl)
+	    decl = tmpl;
+	  // FIXME: What about partial specializations?  We need to
+	  // look at the specialization list in that case.  Unless our
+	  // caller's given us the right thing.  An alternative would
+	  // be to put both the template and the result into the
+	  // entity hash, but that seems expensive?
+	}
+      unsigned index = import_entity_index (decl);
+      them = import_entity_module (index);
+    }
+
+  if (them->is_header ())
+    {
+      if (!header_module_p ())
+	return !module_purview_p ();
+
+      if (DECL_SOURCE_LOCATION (decl) == BUILTINS_LOCATION)
+	/* This is a builtin, being declared in header-unit.  We
+	   now need to mark it as an export.  */
+	DECL_MODULE_EXPORT_P (decl) = true;
+
+      /* If it came from a header, it's in the global module.  */
+      return true;
+    }
+
+  if (me == them)
+    return ((DECL_LANG_SPECIFIC (decl) && DECL_MODULE_PURVIEW_P (decl))
+	    == module_purview_p ());
+
+  if (!me->name)
+    me = me->parent;
+
+  /* We can't have found a GMF entity from a named module.  */
+  gcc_checking_assert (DECL_LANG_SPECIFIC (decl)
+		       && DECL_MODULE_PURVIEW_P (decl));
+
+  return me && get_primary (them) == get_primary (me);
+}
+
+/* DECL is being created by this TU.  Record it came from here.  We
+   record module purview, so we can see if partial or explicit
+   specialization needs to be written out, even though its purviewness
+   comes from the most general template.  */
+
+void
+set_instantiating_module (tree decl)
+{
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL
+	      || TREE_CODE (decl) == VAR_DECL
+	      || TREE_CODE (decl) == TYPE_DECL
+	      || TREE_CODE (decl) == CONCEPT_DECL
+	      || TREE_CODE (decl) == TEMPLATE_DECL
+	      || (TREE_CODE (decl) == NAMESPACE_DECL
+		  && DECL_NAMESPACE_ALIAS (decl)));
+
+  if (!modules_p ())
+    return;
+
+  if (!DECL_LANG_SPECIFIC (decl) && module_purview_p ())
+    retrofit_lang_decl (decl);
+  if (DECL_LANG_SPECIFIC (decl))
+    {
+      DECL_MODULE_PURVIEW_P (decl) = module_purview_p ();
+      /* If this was imported, we'll still be in the entity_hash.  */
+      DECL_MODULE_IMPORT_P (decl) = false;
+      if (TREE_CODE (decl) == TEMPLATE_DECL)
+	{
+	  tree res = DECL_TEMPLATE_RESULT (decl);
+	  retrofit_lang_decl (res);
+	  DECL_MODULE_PURVIEW_P (res) = DECL_MODULE_PURVIEW_P (decl);
+	  DECL_MODULE_IMPORT_P (res) = false;
+	}
+    }
+}
+
+/* If DECL is a class member, whose class is not defined in this TU
+   (it was imported), remember this decl.  */
+
+void
+set_defining_module (tree decl)
+{
+  gcc_checking_assert (!DECL_LANG_SPECIFIC (decl)
+		       || !DECL_MODULE_IMPORT_P (decl));
+
+  if (module_has_cmi_p ())
+    {
+      tree ctx = DECL_CONTEXT (decl);
+      if (ctx
+	  && (TREE_CODE (ctx) == RECORD_TYPE || TREE_CODE (ctx) == UNION_TYPE)
+	  && DECL_LANG_SPECIFIC (TYPE_NAME (ctx))
+	  && DECL_MODULE_IMPORT_P (TYPE_NAME (ctx)))
+	{
+	  /* This entity's context is from an import.  We may need to
+	     record this entity to make sure we emit it in the CMI.
+	     Template specializations are in the template hash tables,
+	     so we don't need to record them here as well.  */
+	  int use_tpl = -1;
+	  tree ti = node_template_info (decl, use_tpl);
+	  if (use_tpl <= 0)
+	    {
+	      if (ti)
+		{
+		  gcc_checking_assert (!use_tpl);
+		  /* Get to the TEMPLATE_DECL.  */
+		  decl = TI_TEMPLATE (ti);
+		}
+
+	      /* Record it on the class_members list.  */
+	      vec_safe_push (class_members, decl);
+	    }
+	}
+    }
+}
+
+void
+set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNUSED)
+{
+  set_instantiating_module (decl);
+
+  if (TREE_CODE (CP_DECL_CONTEXT (decl)) != NAMESPACE_DECL)
+    return;
+
+  gcc_checking_assert (friend_p || decl == get_originating_module_decl (decl));
+
+  if (!module_exporting_p ())
+    return;
+
+  // FIXME: Check ill-formed linkage
+  DECL_MODULE_EXPORT_P (decl) = true;
+}
+
+/* DECL is attached to ROOT for odr purposes.  */
+
+void
+maybe_attach_decl (tree ctx, tree decl)
+{
+  if (!flag_modules)
+    return;
+
+  // FIXME: For now just deal with lambdas attached to var decls.
+  // This might be sufficient?
+  if (TREE_CODE (ctx) != VAR_DECL)
+    return;
+
+  gcc_checking_assert (DECL_NAMESPACE_SCOPE_P (ctx));
+
+ if (!attached_table)
+    attached_table = new attachset::hash (EXPERIMENT (1, 400));
+
+  if (attached_table->add (DECL_UID (ctx), decl))
+    {
+      retrofit_lang_decl (ctx);
+      DECL_ATTACHED_DECLS_P (ctx) = true;
+    }
+}
+
+/* Create the flat name string.  It is simplest to have it handy.  */
+
+void
+module_state::set_flatname ()
+{
+  gcc_checking_assert (!flatname);
+  if (parent)
+    {
+      auto_vec<tree,5> ids;
+      size_t len = 0;
+      char const *primary = NULL;
+      size_t pfx_len = 0;
+
+      for (module_state *probe = this;
+	   probe;
+	   probe = probe->parent)
+	if (is_partition () && !probe->is_partition ())
+	  {
+	    primary = probe->get_flatname ();
+	    pfx_len = strlen (primary);
+	    break;
+	  }
+	else
+	  {
+	    ids.safe_push (probe->name);
+	    len += IDENTIFIER_LENGTH (probe->name) + 1;
+	  }
+
+      char *flat = XNEWVEC (char, pfx_len + len + is_partition ());
+      flatname = flat;
+
+      if (primary)
+	{
+	  memcpy (flat, primary, pfx_len);
+	  flat += pfx_len;
+	  *flat++ = ':';
+	}
+
+      for (unsigned len = 0; ids.length ();)
+	{
+	  if (len)
+	    flat[len++] = '.';
+	  tree elt = ids.pop ();
+	  unsigned l = IDENTIFIER_LENGTH (elt);
+	  memcpy (flat + len, IDENTIFIER_POINTER (elt), l + 1);
+	  len += l;
+	}
+    }
+  else if (is_header ())
+    flatname = TREE_STRING_POINTER (name);
+  else
+    flatname = IDENTIFIER_POINTER (name);
+}
+
+/* Read the CMI file for a module.  */
+
+bool
+module_state::do_import (cpp_reader *reader, bool outermost)
+{
+  gcc_assert (global_namespace == current_scope () && loadedness == ML_NONE);
+
+  loc = linemap_module_loc (line_table, loc, get_flatname ());
+
+  if (lazy_open >= lazy_limit)
+    freeze_an_elf ();
+
+  int fd = -1;
+  int e = ENOENT;
+  if (filename)
+    {
+      const char *file = maybe_add_cmi_prefix (filename);
+      dump () && dump ("CMI is %s", file);
+      fd = open (file, O_RDONLY | O_CLOEXEC);
+      e = errno;
+    }
+
+  gcc_checking_assert (!slurp);
+  slurp = new slurping (new elf_in (fd, e));
+
+  bool ok = true;
+  if (!from ()->get_error ())
+    {
+      announce ("importing");
+      loadedness = ML_CONFIG;
+      lazy_open++;
+      ok = read_initial (reader);
+      slurp->lru = ++lazy_lru;
+    }
+
+  gcc_assert (slurp->current == ~0u);
+
+  return check_read (outermost, ok);
+}
+
+/* Attempt to increase the file descriptor limit.  */
+
+static bool
+try_increase_lazy (unsigned want)
+{
+  gcc_checking_assert (lazy_open >= lazy_limit);
+
+  /* If we're increasing, saturate at hard limit.  */
+  if (want > lazy_hard_limit && lazy_limit < lazy_hard_limit)
+    want = lazy_hard_limit;
+
+#if HAVE_SETRLIMIT
+  if ((!lazy_limit || !param_lazy_modules)
+      && lazy_hard_limit
+      && want <= lazy_hard_limit)
+    {
+      struct rlimit rlimit;
+      rlimit.rlim_cur = want + LAZY_HEADROOM;
+      rlimit.rlim_max = lazy_hard_limit + LAZY_HEADROOM;
+      if (!setrlimit (RLIMIT_NOFILE, &rlimit))
+	lazy_limit = want;
+    }
+#endif
+
+  return lazy_open < lazy_limit;
+}
+
+/* Pick a victim module to freeze its reader.  */
+
+void
+module_state::freeze_an_elf ()
+{
+  if (try_increase_lazy (lazy_open * 2))
+    return;
+
+  module_state *victim = NULL;
+  for (unsigned ix = modules->length (); ix--;)
+    {
+      module_state *candidate = (*modules)[ix];
+      if (candidate && candidate->slurp && candidate->slurp->lru
+	  && candidate->from ()->is_freezable ()
+	  && (!victim || victim->slurp->lru > candidate->slurp->lru))
+	victim = candidate;
+    }
+
+  if (victim)
+    {
+      dump () && dump ("Freezing '%s'", victim->filename);
+      if (victim->slurp->macro_defs.size)
+	/* Save the macro definitions to a buffer.  */
+	victim->from ()->preserve (victim->slurp->macro_defs);
+      if (victim->slurp->macro_tbl.size)
+	/* Save the macro definitions to a buffer.  */
+	victim->from ()->preserve (victim->slurp->macro_tbl);
+      victim->from ()->freeze ();
+      lazy_open--;
+    }
+  else
+    dump () && dump ("No module available for freezing");
+}
+
+/* Load the lazy slot *MSLOT, INDEX'th slot of the module.  */
+
+bool
+module_state::lazy_load (unsigned index, mc_slot *mslot)
+{
+  unsigned n = dump.push (this);
+
+  gcc_checking_assert (function_depth);
+
+  unsigned cookie = mslot->get_lazy ();
+  unsigned snum = cookie >> 2;
+  dump () && dump ("Loading entity %M[%u] section:%u", this, index, snum);
+
+  bool ok = load_section (snum, mslot);
+ 
+  dump.pop (n);
+
+  return ok;
+}
+
+/* Load MOD's binding for NS::ID into *MSLOT.  *MSLOT contains the
+   lazy cookie.  OUTER is true if this is the outermost lazy, (used
+   for diagnostics).  */
+
+void
+lazy_load_binding (unsigned mod, tree ns, tree id, mc_slot *mslot)
+{
+  int count = errorcount + warningcount;
+
+  timevar_start (TV_MODULE_IMPORT);
+
+  /* Stop GC happening, even in outermost loads (because our caller
+     could well be building up a lookup set).  */
+  function_depth++;
+
+  gcc_checking_assert (mod);
+  module_state *module = (*modules)[mod];
+  unsigned n = dump.push (module);
+
+  unsigned snum = mslot->get_lazy ();
+  dump () && dump ("Lazily binding %P@%N section:%u", ns, id,
+		   module->name, snum);
+
+  bool ok = !recursive_lazy (snum);
+  if (ok)
+    {
+      ok = module->load_section (snum, mslot);
+      lazy_snum = 0;
+    }
+
+  dump.pop (n);
+
+  function_depth--;
+
+  timevar_stop (TV_MODULE_IMPORT);
+
+  if (!ok)
+    fatal_error (input_location,
+		 module->is_header ()
+		 ? G_("failed to load binding %<%E%s%E%>")
+		 : G_("failed to load binding %<%E%s%E@%s%>"),
+		 ns, &"::"[ns == global_namespace ? 2 : 0], id,
+		 module->get_flatname ());
+
+  if (count != errorcount + warningcount)
+    inform (input_location,
+	    module->is_header ()
+	    ? G_("during load of binding %<%E%s%E%>")
+	    : G_("during load of binding %<%E%s%E@%s%>"),
+	    ns, &"::"[ns == global_namespace ? 2 : 0], id,
+	    module->get_flatname ());
+}
+
+/* Load any pending specializations of TMPL.  Called just before
+   instantiating TMPL.  */
+
+void
+lazy_load_specializations (tree tmpl)
+{
+  gcc_checking_assert (DECL_MODULE_PENDING_SPECIALIZATIONS_P (tmpl)
+		       && DECL_MODULE_ENTITY_P (tmpl));
+
+  int count = errorcount + warningcount;
+
+  timevar_start (TV_MODULE_IMPORT);
+  bool ok = !recursive_lazy ();
+  if (ok)
+    {
+      unsigned ident = import_entity_index (tmpl);
+      if (pendset *set = pending_table->get (ident, true))
+	{
+	  function_depth++; /* Prevent GC */
+	  unsigned n = dump.push (NULL);
+	  dump ()
+	    && dump ("Reading %u pending specializations keyed to %M[%u] %N",
+		     set->num, import_entity_module (ident),
+		     ident - import_entity_module (ident)->entity_lwm, tmpl);
+	  if (!pendset_lazy_load (set, true))
+	    ok = false;
+	  dump.pop (n);
+
+	  function_depth--;
+	}
+      lazy_snum = 0;
+    }
+
+  timevar_stop (TV_MODULE_IMPORT);
+
+  if (!ok)
+    fatal_error (input_location, "failed to load specializations keyed to %qD",
+		 tmpl);
+
+  if (count != errorcount + warningcount)
+    inform (input_location,
+	    "during load of specializations keyed to %qD", tmpl);
+}
+
+void
+lazy_load_members (tree decl)
+{
+  gcc_checking_assert (DECL_MODULE_PENDING_MEMBERS_P (decl));
+  if (!DECL_MODULE_ENTITY_P (decl))
+    {
+      // FIXME: I can't help feeling that DECL_TEMPLATE_RESULT should
+      // be inserted into the entity map, or perhaps have the same
+      // DECL_UID as the template, so I don't have to do this dance
+      // here and elsewhere.  It also simplifies when DECL is a
+      // partial specialization.  (also noted elsewhere as an issue)
+      tree ti = CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl));
+      tree tmpl = TI_TEMPLATE (ti);
+      gcc_checking_assert (DECL_TEMPLATE_RESULT (tmpl) == decl);
+      decl = tmpl;
+    }
+
+  timevar_start (TV_MODULE_IMPORT);
+  unsigned ident = import_entity_index (decl);
+  if (pendset *set = pending_table->get (~ident, true))
+    {
+      function_depth++; /* Prevent GC */
+      unsigned n = dump.push (NULL);
+      dump () && dump ("Reading %u pending members keyed to %M[%u] %N",
+		       set->num, import_entity_module (ident),
+		       ident - import_entity_module (ident)->entity_lwm, decl);
+      pendset_lazy_load (set, false);
+      dump.pop (n);
+
+      function_depth--;
+    }
+  timevar_stop (TV_MODULE_IMPORT);
+}
+
+static void
+direct_import (module_state *import, cpp_reader *reader)
+{
+  timevar_start (TV_MODULE_IMPORT);
+  unsigned n = dump.push (import);
+
+  gcc_checking_assert (import->is_direct () && import->is_rooted ());
+  if (import->loadedness == ML_NONE)
+    if (!import->do_import (reader, true))
+      gcc_unreachable ();
+
+  if (import->loadedness < ML_LANGUAGE)
+    {
+      if (!attached_table)
+	attached_table = new attachset::hash (EXPERIMENT (1, 400));
+      import->read_language (true);
+    }
+
+  (*modules)[0]->set_import (import, import->exported_p);
+
+  dump.pop (n);
+  timevar_stop (TV_MODULE_IMPORT);
+}
+
+/* Import module IMPORT.  */
+
+void
+import_module (module_state *import, location_t from_loc, bool exporting_p,
+	       tree, cpp_reader *reader)
+{
+  if (!import->check_not_purview (from_loc))
+    return;
+
+  if (!import->is_header () && current_lang_depth ())
+    /* Only header units should appear inside language
+       specifications.  The std doesn't specify this, but I think
+       that's an error in resolving US 033, because language linkage
+       is also our escape clause to getting things into the global
+       module, so we don't want to confuse things by having to think
+       about whether 'extern "C++" { import foo; }' puts foo's
+       contents into the global module all of a sudden.  */
+    warning (0, "import of named module %qs inside language-linkage block",
+	     import->get_flatname ());
+
+  if (exporting_p || module_exporting_p ())
+    import->exported_p = true;
+
+  if (import->loadedness != ML_NONE)
+    {
+      from_loc = ordinary_loc_of (line_table, from_loc);
+      linemap_module_reparent (line_table, import->loc, from_loc);
+    }
+  gcc_checking_assert (!import->module_p);
+  gcc_checking_assert (import->is_direct () && import->is_rooted ());
+
+  direct_import (import, reader);
+}
+
+/* Declare the name of the current module to be NAME.  EXPORTING_p is
+   true if this TU is the exporting module unit.  */
+
+void
+declare_module (module_state *module, location_t from_loc, bool exporting_p,
+		tree, cpp_reader *reader)
+{
+  gcc_assert (global_namespace == current_scope ());
+
+  module_state *current = (*modules)[0];
+  if (module_purview_p () || module->loadedness != ML_NONE)
+    {
+      error_at (from_loc, module_purview_p ()
+		? G_("module already declared")
+		: G_("module already imported"));
+      if (module_purview_p ())
+	module = current;
+      inform (module->loc, module_purview_p ()
+	      ? G_("module %qs declared here")
+	      : G_("module %qs imported here"),
+	      module->get_flatname ());
+      return;
+    }
+
+  gcc_checking_assert (module->module_p);
+  gcc_checking_assert (module->is_direct () && module->is_rooted ());
+
+  /* Yer a module, 'arry.  */
+  module_kind &= ~MK_GLOBAL;
+  module_kind |= MK_MODULE;
+
+  if (module->is_partition () || exporting_p)
+    {
+      gcc_checking_assert (module->get_flatname ());
+
+      if (module->is_partition ())
+	module_kind |= MK_PARTITION;
+
+      if (exporting_p)
+	{
+	  module->interface_p = true;
+	  module_kind |= MK_INTERFACE;
+	}
+
+      if (module->is_header ())
+	module_kind |= MK_GLOBAL | MK_EXPORTING;
+
+      /* Copy the importing information we may have already done.  We
+	 do not need to separate out the imports that only happen in
+	 the GMF, inspite of what the literal wording of the std
+	 might imply.  See p2191, the core list had a discussion
+	 where the module implementors agreed that the GMF of a named
+	 module is invisible to importers.  */
+      module->imports = current->imports;
+
+      module->mod = 0;
+      (*modules)[0] = module;
+    }
+  else
+    {
+      module->interface_p = true;
+      current->parent = module; /* So mangler knows module identity. */
+      direct_import (module, reader);
+    }
+}
+
+/* +1, we're the primary or a partition.  Therefore emitting a
+   globally-callable idemportent initializer function.
+   -1, we have direct imports.  Therefore emitting calls to their
+   initializers.  */
+
+int
+module_initializer_kind ()
+{
+  int result = 0;
+
+  if (module_has_cmi_p () && !header_module_p ())
+    result = +1;
+  else if (num_init_calls_needed)
+    result = -1;
+
+  return result;
+}
+
+/* Emit calls to each direct import's global initializer.  Including
+   direct imports of directly imported header units.  The initializers
+   of (static) entities in header units will be called by their
+   importing modules (for the instance contained within that), or by
+   the current TU (for the instances we've brought in).  Of course
+   such header unit behaviour is evil, but iostream went through that
+   door some time ago.  */
+
+void
+module_add_import_initializers ()
+{
+  unsigned calls = 0;
+  if (modules)
+    {
+      tree fntype = build_function_type (void_type_node, void_list_node);
+      vec<tree, va_gc> *args = NULL;
+      
+      for (unsigned ix = modules->length (); --ix;)
+	{
+	  module_state *import = (*modules)[ix];
+	  if (import->call_init_p)
+	    {
+	      tree name = mangle_module_global_init (ix);
+	      tree fndecl = build_lang_decl (FUNCTION_DECL, name, fntype);
+
+	      DECL_CONTEXT (fndecl) = FROB_CONTEXT (global_namespace);
+	      SET_DECL_ASSEMBLER_NAME (fndecl, name);
+	      TREE_PUBLIC (fndecl) = true;
+	      determine_visibility (fndecl);
+
+	      tree call = cp_build_function_call_vec (fndecl, &args,
+						      tf_warning_or_error);
+	      finish_expr_stmt (call);
+	      
+	      calls++;
+	    }
+	}
+    }
+
+  gcc_checking_assert (calls == num_init_calls_needed);
+}
+
+/* Track if NODE undefs an imported macro.  */
+
+void
+module_cpp_undef (cpp_reader *reader, location_t loc, cpp_hashnode *node)
+{
+  if (!flag_header_unit)
+    {
+      /* Turn us off.  */
+      struct cpp_callbacks *cb = cpp_get_callbacks (reader);
+      if (cb->undef == lang_hooks.preprocess_undef)
+	{
+	  cb->undef = NULL;
+	  lang_hooks.preprocess_undef = NULL;
+	}
+    }
+  if (lang_hooks.preprocess_undef)
+    module_state::undef_macro (reader, loc, node);
+}
+
+cpp_macro *
+module_cpp_deferred_macro (cpp_reader *reader, location_t loc,
+			   cpp_hashnode *node)
+{
+  return module_state::deferred_macro (reader, loc, node);
+}
+
+/* NAME & LEN are a preprocessed header name, possibly including the
+   surrounding "" or <> characters.  Return the raw string name of the
+   module to which it refers.  This will be an absolute path, or begin
+   with ./, so it is immediately distinguishable from a (non-header
+   unit) module name.  If READER is non-null, ask the preprocessor to
+   locate the header to which it refers using the appropriate include
+   path.  Note that we do never do \ processing of the string, as that
+   matches the preprocessor's behaviour.  */
+
+static const char *
+canonicalize_header_name (cpp_reader *reader, location_t loc, bool unquoted,
+			  const char *str, size_t &len_r)
+{
+  size_t len = len_r;
+  static char *buf = 0;
+  static size_t alloc = 0;
+
+  if (!unquoted)
+    {
+      gcc_checking_assert (len >= 2
+			   && ((reader && str[0] == '<' && str[len-1] == '>')
+			       || (str[0] == '"' && str[len-1] == '"')));
+      str += 1;
+      len -= 2;
+    }
+
+  if (reader)
+    {
+      gcc_assert (!unquoted);
+
+      if (len >= alloc)
+	{
+	  alloc = len + 1;
+	  buf = XRESIZEVEC (char, buf, alloc);
+	}
+      memcpy (buf, str, len);
+      buf[len] = 0;
+
+      if (const char *hdr
+	  = cpp_find_header_unit (reader, buf, str[-1] == '<', loc))
+	{
+	  len = strlen (hdr);
+	  str = hdr;
+	}
+      else
+	str = buf;
+    }
+
+  if (!(str[0] == '.' ? IS_DIR_SEPARATOR (str[1]) : IS_ABSOLUTE_PATH (str)))
+    {
+      /* Prepend './'  */
+      if (len + 3 > alloc)
+	{
+	  alloc = len + 3;
+	  buf = XRESIZEVEC (char, buf, alloc);
+	}
+
+      buf[0] = '.';
+      buf[1] = DIR_SEPARATOR;
+      memmove (buf + 2, str, len);
+      len += 2;
+      buf[len] = 0;
+      str = buf;
+    }
+
+  len_r = len;
+  return str;
+}
+
+/* Set the CMI name from a cody packet.  Issue an error if
+   ill-formed.  */
+
+void module_state::set_filename (const Cody::Packet &packet)
+{
+  gcc_checking_assert (!filename);
+  if (packet.GetCode () == Cody::Client::PC_PATHNAME)
+    filename = xstrdup (packet.GetString ().c_str ());
+  else
+    {
+      gcc_checking_assert (packet.GetCode () == Cody::Client::PC_ERROR);
+      error_at (loc, "unknown Compiled Module Interface: %s",
+		packet.GetString ().c_str ());
+    }
+}
+
+/* Figure out whether to treat HEADER as an include or an import.  */
+
+static char *
+maybe_translate_include (cpp_reader *reader, line_maps *lmaps, location_t loc,
+			 const char *path)
+{
+  if (!modules_p ())
+    {
+      /* Turn off.  */
+      cpp_get_callbacks (reader)->translate_include = NULL;
+      return nullptr;
+    }
+
+  if (!spans.init_p ())
+    /* Before the main file, don't divert.  */
+    return nullptr;
+
+  dump.push (NULL);
+
+  dump () && dump ("Checking include translation '%s'", path);
+  auto *mapper = get_mapper (cpp_main_loc (reader));
+
+  size_t len = strlen (path);
+  path = canonicalize_header_name (NULL, loc, true, path, len);
+  auto packet = mapper->IncludeTranslate (path, len);
+  int xlate = false;
+  if (packet.GetCode () == Cody::Client::PC_BOOL)
+    xlate = packet.GetInteger ();
+  else if (packet.GetCode () == Cody::Client::PC_PATHNAME)
+    {
+      /* Record the CMI name for when we do the import.  */
+      module_state *import = get_module (build_string (len, path));
+      import->set_filename (packet);
+      xlate = true;
+    }
+  else
+    {
+      gcc_checking_assert (packet.GetCode () == Cody::Client::PC_ERROR);
+      error_at (loc, "cannot determine %<#include%> translation of %s: %s",
+		path, packet.GetString ().c_str ());
+    }
+
+  bool note = false;
+  if (note_include_translate && xlate)
+    note = true;
+  else if (note_includes)
+    {
+      /* We do not expect the note_includes vector to be large, so O(N)
+	 iteration.  */
+      for (unsigned ix = note_includes->length (); !note && ix--;)
+	{
+	  const char *hdr = (*note_includes)[ix];
+	  size_t hdr_len = strlen (hdr);
+	  if ((hdr_len == len
+	       || (hdr_len < len && IS_DIR_SEPARATOR (path[len - hdr_len - 1])))
+	      && !memcmp (hdr, path + len - hdr_len, hdr_len))
+	    note = true;
+	}
+    }
+
+  if (note)
+    inform (loc, xlate
+	    ? G_("include %qs translated to import")
+	    : G_("include %qs processed textually") , path);
+
+  dump () && dump (xlate ? "Translating include to import"
+		   : "Keeping include as include");
+  dump.pop (0);
+
+  if (!xlate)
+    return nullptr;
+  
+  /* Create the translation text.  */
+  loc = ordinary_loc_of (lmaps, loc);
+  const line_map_ordinary *map
+    = linemap_check_ordinary (linemap_lookup (lmaps, loc));
+  unsigned col = SOURCE_COLUMN (map, loc);
+  col -= (col != 0); /* Columns are 1-based.  */
+
+  unsigned alloc = len + col + 60;
+  char *res = XNEWVEC (char, alloc);
+
+  strcpy (res, "__import");
+  unsigned actual = 8;
+  if (col > actual)
+    {
+      /* Pad out so the filename appears at the same position.  */
+      memset (res + actual, ' ', col - actual);
+      actual = col;
+    }
+  /* No need to encode characters, that's not how header names are
+     handled.  */
+  actual += snprintf (res + actual, alloc - actual,
+		      "\"%s\" [[__translated]];\n", path);
+  gcc_checking_assert (actual < alloc);
+
+  /* cpplib will delete the buffer.  */
+  return res;
+}
+
+static void
+begin_header_unit (cpp_reader *reader)
+{
+  /* Set the module header name from the main_input_filename.  */
+  const char *main = main_input_filename;
+  size_t len = strlen (main);
+  main = canonicalize_header_name (NULL, 0, true, main, len);
+  module_state *module = get_module (build_string (len, main));
+
+  preprocess_module (module, cpp_main_loc (reader), false, false, true, reader);
+}
+
+/* We've just properly entered the main source file.  I.e. after the
+   command line, builtins and forced headers.  Record the line map and
+   location of this map.  Note we may be called more than once.  The
+   first call sticks.  */
+
+void
+module_begin_main_file (cpp_reader *reader, line_maps *lmaps,
+		       const line_map_ordinary *map)
+{
+  gcc_checking_assert (lmaps == line_table);
+  if (modules_p () && !spans.init_p ())
+    {
+      unsigned n = dump.push (NULL);
+      spans.init (lmaps, map);
+      dump.pop (n);
+      if (flag_header_unit && !cpp_get_options (reader)->preprocessed)
+	{
+	  /* Tell the preprocessor this is an include file.  */
+	  cpp_retrofit_as_include (reader);
+	  begin_header_unit (reader);
+	}
+    }
+}
+
+/* We've just lexed a module-specific control line for MODULE.  Mark
+   the module as a direct import, and possibly load up its macro
+   state.  Returns the primary module, if this is a module
+   declaration.  */
+/* Perhaps we should offer a preprocessing mode where we read the
+   directives from the header unit, rather than require the header's
+   CMI.  */
+
+module_state *
+preprocess_module (module_state *module, location_t from_loc,
+		   bool in_purview, bool is_import, bool is_export,
+		   cpp_reader *reader)
+{
+  if (!is_import)
+    {
+      if (module->loc)
+	/* It's already been mentioned, so ignore its module-ness.  */
+	is_import = true;
+      else
+	{
+	  /* Record it is the module.  */
+	  module->module_p = true;
+	  if (is_export)
+	    {
+	      module->exported_p = true;
+	      module->interface_p = true;
+	    }
+	}
+    }
+
+  if (module->directness < MD_DIRECT + in_purview)
+    {
+      /* Mark as a direct import.  */
+      module->directness = module_directness (MD_DIRECT + in_purview);
+
+      /* Set the location to be most informative for users.  */
+      from_loc = ordinary_loc_of (line_table, from_loc);
+      if (module->loadedness != ML_NONE)
+	linemap_module_reparent (line_table, module->loc, from_loc);
+      else
+	{
+	  module->loc = from_loc;
+	  if (!module->flatname)
+	    module->set_flatname ();
+	}
+    }
+
+  if (is_import
+      && !module->is_module () && module->is_header ()
+      && module->loadedness < ML_PREPROCESSOR
+      && (!cpp_get_options (reader)->preprocessed
+	  || cpp_get_options (reader)->directives_only))
+    {
+      timevar_start (TV_MODULE_IMPORT);
+      unsigned n = dump.push (module);
+
+      if (module->loadedness == ML_NONE)
+	{
+	  unsigned pre_hwm = 0;
+
+	  /* Preserve the state of the line-map.  */
+	  pre_hwm = LINEMAPS_ORDINARY_USED (line_table);
+	  /* We only need to close the span, if we're going to emit a
+	     CMI.  But that's a little tricky -- our token scanner
+	     needs to be smarter -- and this isn't much state.
+	     Remember, we've not parsed anything at this point, so
+	     our module state flags are inadequate.  */
+	  spans.maybe_init ();
+	  spans.close ();
+
+	  if (!module->filename)
+	    {
+	      auto *mapper = get_mapper (cpp_main_loc (reader));
+	      auto packet = mapper->ModuleImport (module->get_flatname ());
+	      module->set_filename (packet);
+	    }
+	  module->do_import (reader, true);
+
+	  /* Restore the line-map state.  */
+	  linemap_module_restore (line_table, pre_hwm);
+	  spans.open ();
+	}
+
+      if (module->loadedness < ML_PREPROCESSOR)
+	if (module->read_preprocessor (true))
+	  module->import_macros ();
+
+      dump.pop (n);
+      timevar_stop (TV_MODULE_IMPORT);
+    }
+
+  return is_import ? NULL : get_primary (module);
+}
+
+/* We've completed phase-4 translation.  Emit any dependency
+   information for the direct imports, and fill in their file names.  */
+
+void
+preprocessed_module (cpp_reader *reader)
+{
+  auto *mapper = get_mapper (cpp_main_loc (reader));
+
+  spans.maybe_init ();
+  spans.close ();
+
+  /* Stupid GTY doesn't grok a typedef here.  And using type = is, too
+     modern.  */
+#define iterator hash_table<module_state_hash>::iterator
+  /* using iterator = hash_table<module_state_hash>::iterator;  */
+
+  /* Walk the module hash, asking for the names of all unknown
+     direct imports.  */
+  timevar_start (TV_MODULE_MAPPER);
+
+  dump.push (NULL);
+  dump () && dump ("Resolving direct import names");
+
+  mapper->Cork ();
+  iterator end = modules_hash->end ();
+  for (iterator iter = modules_hash->begin (); iter != end; ++iter)
+    {
+      module_state *module = *iter;
+      if (module->is_direct () && !module->filename)
+	{
+	  if (module->module_p
+	      && (module->is_partition () || module->exported_p))
+	    mapper->ModuleExport (module->get_flatname ());
+	  else
+	    mapper->ModuleImport (module->get_flatname ());
+	}
+    }
+
+  auto response = mapper->Uncork ();
+  auto r_iter = response.begin ();
+  for (iterator iter = modules_hash->begin (); iter != end; ++iter)
+    {
+      module_state *module = *iter;
+      
+      if (module->is_direct () && !module->filename)
+	{
+	  Cody::Packet const &p = *r_iter;
+	  ++r_iter;
+
+	  module->set_filename (p);
+	}
+    }
+
+  dump.pop (0);
+
+  timevar_stop (TV_MODULE_MAPPER);
+
+  if (mkdeps *deps = cpp_get_deps (reader))
+    {
+      /* Walk the module hash, informing the dependency machinery.  */
+      iterator end = modules_hash->end ();
+      for (iterator iter = modules_hash->begin (); iter != end; ++iter)
+	{
+	  module_state *module = *iter;
+
+	  if (module->is_direct ())
+	    {
+	      const char *path = NULL;
+	      if (module->is_module ()
+		  && (module->is_interface () || module->is_partition ()))
+		path = maybe_add_cmi_prefix (module->filename);
+	      deps_add_module (deps, module->get_flatname (),
+			       path, module->is_header());
+	    }
+	}
+    }
+
+  if (flag_header_unit && !flag_preprocess_only)
+    {
+      iterator end = modules_hash->end ();
+      for (iterator iter = modules_hash->begin (); iter != end; ++iter)
+	{
+	  module_state *module = *iter;
+	  if (module->is_module ())
+	    {
+	      declare_module (module, cpp_main_loc (reader), true, NULL, reader);
+	      break;
+	    }
+	}
+    }
+#undef iterator
+}
+
+/* VAL is a global tree, add it to the global vec if it is
+   interesting.  Add some of its targets, if they too are
+   interesting.  We do not add identifiers, as they can be re-found
+   via the identifier hash table.  There is a cost to the number of
+   global trees.  */
+
+static int
+maybe_add_global (tree val, unsigned &crc)
+{
+  int v = 0;
+
+  if (val && !(identifier_p (val) || TREE_VISITED (val)))
+    {
+      TREE_VISITED (val) = true;
+      crc = crc32_unsigned (crc, fixed_trees->length ());
+      vec_safe_push (fixed_trees, val);
+      v++;
+
+      if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPED))
+	v += maybe_add_global (TREE_TYPE (val), crc);
+      if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPE_COMMON))
+	v += maybe_add_global (TYPE_NAME (val), crc);
+    }
+
+  return v;
+}
+
+/* Initialize module state.  Create the hash table, determine the
+   global trees.  Create the module for current TU.  */
+
+void
+init_modules (cpp_reader *reader)
+{
+  /* PCH should not be reachable because of lang-specs, but the
+     user could have overriden that.  */
+  if (pch_file)
+    fatal_error (input_location,
+		 "C++ modules are incompatible with precompiled headers");
+
+  if (cpp_get_options (reader)->traditional)
+    fatal_error (input_location,
+		 "C++ modules are incompatible with traditional preprocessing");
+
+  if (flag_preprocess_only)
+    {
+      cpp_options *cpp_opts = cpp_get_options (reader);
+      if (flag_no_output
+	  || (cpp_opts->deps.style != DEPS_NONE
+	      && !cpp_opts->deps.need_preprocessor_output))
+	{
+	  warning (0, flag_dump_macros == 'M'
+		   ? G_("macro debug output may be incomplete with modules")
+		   : G_("module dependencies require preprocessing"));
+	  if (cpp_opts->deps.style != DEPS_NONE)
+	    inform (input_location, "you should use the %<-%s%> option",
+		    cpp_opts->deps.style == DEPS_SYSTEM ? "MD" : "MMD");
+	}
+    }
+
+  /* :: is always exported.  */
+  DECL_MODULE_EXPORT_P (global_namespace) = true;
+
+  modules_hash = hash_table<module_state_hash>::create_ggc (31);
+  vec_safe_reserve (modules, 20);
+
+  /* Create module for current TU.  */
+  module_state *current
+    = new (ggc_alloc<module_state> ()) module_state (NULL_TREE, NULL, false);
+  current->mod = 0;
+  bitmap_set_bit (current->imports, 0);
+  modules->quick_push (current);
+
+  gcc_checking_assert (!fixed_trees);
+
+  headers = BITMAP_GGC_ALLOC ();
+
+  if (note_includes)
+    for (unsigned ix = 0; ix != note_includes->length (); ix++)
+      {
+	const char *hdr = (*note_includes)[ix];
+	size_t len = strlen (hdr);
+
+	bool system = hdr[0] == '<';
+	bool user = hdr[0] == '"';
+	bool delimed = system || user;
+
+	if (len <= (delimed ? 2 : 0)
+	    || (delimed && hdr[len-1] != (system ? '>' : '"')))
+	  error ("invalid header name %qs", hdr);
+
+	hdr = canonicalize_header_name (delimed ? reader : NULL,
+					0, !delimed, hdr, len);
+	char *path = XNEWVEC (char, len + 1);
+	memcpy (path, hdr, len);
+	path[len+1] = 0;
+
+	(*note_includes)[ix] = path;
+      }
+
+  dump.push (NULL);
+
+  /* Determine lazy handle bound.  */
+  {
+    unsigned limit = 1000;
+#if HAVE_GETRLIMIT
+    struct rlimit rlimit;
+    if (!getrlimit (RLIMIT_NOFILE, &rlimit))
+      {
+	lazy_hard_limit = (rlimit.rlim_max < 1000000
+			   ? unsigned (rlimit.rlim_max) : 1000000);
+	lazy_hard_limit = (lazy_hard_limit > LAZY_HEADROOM
+			   ? lazy_hard_limit - LAZY_HEADROOM : 0);
+	if (rlimit.rlim_cur < limit)
+	  limit = unsigned (rlimit.rlim_cur);
+      }
+#endif
+    limit = limit > LAZY_HEADROOM ? limit - LAZY_HEADROOM : 1;
+
+    if (unsigned parm = param_lazy_modules)
+      {
+	if (parm <= limit || !lazy_hard_limit || !try_increase_lazy (parm))
+	  lazy_limit = parm;
+      }
+    else
+      lazy_limit = limit;
+  }
+
+  if (dump ())
+    {
+      verstr_t ver;
+      version2string (MODULE_VERSION, ver);
+      dump ("Source: %s", main_input_filename);
+      dump ("Compiler: %s", version_string);
+      dump ("Modules: %s", ver);
+      dump ("Checking: %s",
+#if CHECKING_P
+	    "checking"
+#elif ENABLE_ASSERT_CHECKING
+	    "asserting"
+#else
+	    "release"
+#endif
+	    );
+      dump ("Compiled by: "
+#ifdef __GNUC__
+	    "GCC %d.%d, %s", __GNUC__, __GNUC_MINOR__,
+#ifdef __OPTIMIZE__
+	    "optimizing"
+#else
+	    "not optimizing"
+#endif
+#else
+	    "not GCC"
+#endif
+	    );
+      dump ("Reading: %s", MAPPED_READING ? "mmap" : "fileio");
+      dump ("Writing: %s", MAPPED_WRITING ? "mmap" : "fileio");
+      dump ("Lazy limit: %u", lazy_limit);
+      dump ("Lazy hard limit: %u", lazy_hard_limit);
+      dump ("");
+    }
+
+  /* Construct the global tree array.  This is an array of unique
+     global trees (& types).  Do this now, rather than lazily, as
+     some global trees are lazily created and we don't want that to
+     mess with our syndrome of fixed trees.  */
+  unsigned crc = 0;
+  vec_alloc (fixed_trees, 200);
+
+  dump () && dump ("+Creating globals");
+  /* Insert the TRANSLATION_UNIT_DECL.  */
+  TREE_VISITED (DECL_CONTEXT (global_namespace)) = true;
+  fixed_trees->quick_push (DECL_CONTEXT (global_namespace));
+  for (unsigned jx = 0; global_tree_arys[jx].first; jx++)
+    {
+      const tree *ptr = global_tree_arys[jx].first;
+      unsigned limit = global_tree_arys[jx].second;
+
+      for (unsigned ix = 0; ix != limit; ix++, ptr++)
+	{
+	  !(ix & 31) && dump ("") && dump ("+\t%u:%u:", jx, ix);
+	  unsigned v = maybe_add_global (*ptr, crc);
+	  dump () && dump ("+%u", v);
+	}
+    }
+  global_crc = crc32_unsigned (crc, fixed_trees->length ());
+  dump ("") && dump ("Created %u unique globals, crc=%x",
+		     fixed_trees->length (), global_crc);
+  for (unsigned ix = fixed_trees->length (); ix--;)
+    TREE_VISITED ((*fixed_trees)[ix]) = false;
+
+  dump.pop (0);
+
+  if (!flag_module_lazy)
+    /* Get the mapper now, if we're not being lazy.  */
+    get_mapper (cpp_main_loc (reader));
+
+  if (!flag_preprocess_only)
+    {
+      pending_table = new pendset::hash (EXPERIMENT (1, 400));
+
+      entity_map = new entity_map_t (EXPERIMENT (1, 400));
+      vec_safe_reserve (entity_ary, EXPERIMENT (1, 400));
+    }
+
+#if CHECKING_P
+  note_defs = note_defs_table_t::create_ggc (1000);
+#endif
+
+  if (flag_header_unit && cpp_get_options (reader)->preprocessed)
+    begin_header_unit (reader);
+
+  /* Collect here to make sure things are tagged correctly (when
+     aggressively GC'd).  */
+  ggc_collect ();
+}
+
+/* If NODE is a deferred macro, load it.  */
+
+static int
+load_macros (cpp_reader *reader, cpp_hashnode *node, void *)
+{
+  location_t main_loc
+    = MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0));
+
+  if (cpp_user_macro_p (node)
+      && !node->value.macro)
+    {
+      cpp_macro *macro = cpp_get_deferred_macro (reader, node, main_loc);
+      dump () && dump ("Loaded macro #%s %I",
+		       macro ? "define" : "undef", identifier (node));
+    }
+
+  return 1;
+}
+
+/* At the end of tokenizing, we no longer need the macro tables of
+   imports.  But the user might have requested some checking.  */
+
+void
+maybe_check_all_macros (cpp_reader *reader)
+{
+  if (!warn_imported_macros)
+    return;
+
+  /* Force loading of any remaining deferred macros.  This will
+     produce diagnostics if they are ill-formed.  */
+  unsigned n = dump.push (NULL);
+  cpp_forall_identifiers (reader, load_macros, NULL);
+  dump.pop (n);
+}
+
+/* Write the CMI, if we're a module interface.  */
+
+void
+finish_module_processing (cpp_reader *reader)
+{
+  if (header_module_p ())
+    module_kind &= ~MK_EXPORTING;
+
+  if (!modules || !(*modules)[0]->name)
+    {
+      if (flag_module_only)
+	warning (0, "%<-fmodule-only%> used for non-interface");
+    }
+  else if (!flag_syntax_only)
+    {
+      int fd = -1;
+      int e = ENOENT;
+
+      timevar_start (TV_MODULE_EXPORT);
+
+      /* Force a valid but empty line map at the end.  This simplifies
+	 the line table preparation and writing logic.  */
+      linemap_add (line_table, LC_ENTER, false, "", 0);
+
+      /* We write to a tmpname, and then atomically rename.  */
+      const char *path = NULL;
+      char *tmp_name = NULL;
+      module_state *state = (*modules)[0];
+
+      unsigned n = dump.push (state);
+      state->announce ("creating");
+      if (state->filename)
+	{
+	  size_t len = 0;
+	  path = maybe_add_cmi_prefix (state->filename, &len);
+	  tmp_name = XNEWVEC (char, len + 3);
+	  memcpy (tmp_name, path, len);
+	  strcpy (&tmp_name[len], "~");
+
+	  if (!errorcount)
+	    for (unsigned again = 2; ; again--)
+	      {
+		fd = open (tmp_name, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC,
+			   S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+		e = errno;
+		if (fd >= 0 || !again || e != ENOENT)
+		  break;
+		create_dirs (tmp_name);
+	      }
+	  dump () && dump ("CMI is %s", path);
+	}
+
+      if (errorcount)
+	warning_at (state->loc, 0, "not writing module %qs due to errors",
+		    state->get_flatname ());
+      else
+	{
+	  elf_out to (fd, e);
+	  if (to.begin ())
+	    state->write (&to, reader);
+	  if (to.end ())
+	    if (rename (tmp_name, path))
+	      to.set_error (errno);
+
+	  if (to.get_error ())
+	    {
+	      error_at (state->loc, "failed to write compiled module: %s",
+			to.get_error (state->filename));
+	      state->note_cmi_name ();
+	    }
+	}
+
+      if (!errorcount)
+	{
+	  auto *mapper = get_mapper (cpp_main_loc (reader));
+
+	  mapper->ModuleCompiled (state->get_flatname ());
+	}
+      else if (path)
+	{
+	  /* We failed, attempt to erase all evidence we even tried.  */
+	  unlink (tmp_name);
+	  unlink (path);
+	  XDELETEVEC (tmp_name);
+	}
+
+      dump.pop (n);
+      timevar_stop (TV_MODULE_EXPORT);
+
+      ggc_collect ();
+    }
+
+  if (modules)
+    {
+      unsigned n = dump.push (NULL);
+      dump () && dump ("Imported %u modules", modules->length () - 1);
+      dump () && dump ("Containing %u clusters", available_clusters);
+      dump () && dump ("Loaded %u clusters (%u%%)", loaded_clusters,
+		       (loaded_clusters * 100 + available_clusters / 2) /
+		       (available_clusters + !available_clusters));
+      dump.pop (n);
+    }
+
+  if (modules && !header_module_p ())
+    {
+      /* Determine call_init_p.  We need the same bitmap allocation
+         scheme as for the imports member.  */
+      function_depth++; /* Disable GC.  */
+      bitmap indirect_imports (BITMAP_GGC_ALLOC ());
+
+      /* Because indirect imports are before their direct import, and
+	 we're scanning the array backwards, we only need one pass!  */
+      for (unsigned ix = modules->length (); --ix;)
+	{
+	  module_state *import = (*modules)[ix];
+
+	  if (!import->is_header ()
+	      && !bitmap_bit_p (indirect_imports, ix))
+	    {
+	      /* Everything this imports is therefore indirectly
+		 imported.  */
+	      bitmap_ior_into (indirect_imports, import->imports);
+	      /* We don't have to worry about the self-import bit,
+		 because of the single pass.  */
+
+	      import->call_init_p = true;
+	      num_init_calls_needed++;
+	    }
+	}
+      function_depth--;
+    }
+}
+
+void
+fini_modules ()
+{
+  /* We're done with the macro tables now.  */
+  vec_free (macro_exports);
+  vec_free (macro_imports);
+  headers = NULL;
+
+  /* We're now done with everything but the module names.  */
+  set_cmi_repo (NULL);
+  if (mapper)
+    {
+      timevar_start (TV_MODULE_MAPPER);
+      module_client::close_module_client (0, mapper);
+      mapper = nullptr;
+      timevar_stop (TV_MODULE_MAPPER);
+    }
+  module_state_config::release ();
+
+#if CHECKING_P
+  note_defs = NULL;
+#endif
+
+  if (modules)
+    for (unsigned ix = modules->length (); --ix;)
+      if (module_state *state = (*modules)[ix])
+	state->release ();
+
+  /* No need to lookup modules anymore.  */
+  modules_hash = NULL;
+
+  /* Or entity array.  We still need the entity map to find import numbers.  */
+  delete entity_ary;
+  entity_ary = NULL;
+
+  /* Or remember any pending entities.  */
+  delete pending_table;
+  pending_table = NULL;
+
+  /* Or any attachments -- Let it go!  */
+  delete attached_table;
+  attached_table = NULL;
+
+  /* Allow a GC, we've possibly made much data unreachable.  */
+  ggc_collect ();
+}
+
+/* If CODE is a module option, handle it & return true.  Otherwise
+   return false.  For unknown reasons I cannot get the option
+   generation machinery to set fmodule-mapper or -fmodule-header to
+   make a string type option variable.  */
+
+bool
+handle_module_option (unsigned code, const char *str, int)
+{
+  switch (opt_code (code))
+    {
+    case OPT_fmodule_mapper_:
+      module_mapper_name = str;
+      return true;
+
+    case OPT_fmodule_header_:
+      {
+	/* Look away.  Look away now.  */
+	extern cpp_options *cpp_opts;
+	if (!strcmp (str, "user"))
+	  cpp_opts->main_search = CMS_user;
+	else if (!strcmp (str, "system"))
+	  cpp_opts->main_search = CMS_system;
+	else
+	  error ("unknown header kind %qs", str);
+      }
+      /* Fallthrough.  */
+
+    case OPT_fmodule_header:
+      flag_header_unit = 1;
+      flag_modules = 1;
+      return true;
+
+    case OPT_flang_info_include_translate_:
+      vec_safe_push (note_includes, str);
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Set preprocessor callbacks and options for modules.  */
+
+void
+module_preprocess_options (cpp_reader *reader)
+{
+  if (flag_modules)
+    {
+      cpp_get_callbacks (reader)->translate_include = maybe_translate_include;
+      cpp_get_options (reader)->module_directives = true;
+    }
+}
+
+#include "gt-cp-module.h"
diff --git c/gcc/cp/config-lang.in w/gcc/cp/config-lang.in
index da70b358413..4f5f8eca130 100644
--- c/gcc/cp/config-lang.in
+++ w/gcc/cp/config-lang.in
@@ -47,7 +47,7 @@ gtfiles="\
 \$(srcdir)/cp/friend.c \
 \$(srcdir)/cp/init.c \
 \$(srcdir)/cp/lambda.c \$(srcdir)/cp/lex.c \$(srcdir)/cp/logic.cc \
-\$(srcdir)/cp/mangle.c \$(srcdir)/cp/method.c \
+\$(srcdir)/cp/mangle.c \$(srcdir)/cp/method.c \$(srcdir)/cp/module.cc \
 \$(srcdir)/cp/name-lookup.c \
 \$(srcdir)/cp/parser.c \$(srcdir)/cp/pt.c \
 \$(srcdir)/cp/rtti.c \


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

* [26/33] name-lookup
       [not found]                                               ` <3d138aa4-df19-df5d-54c6-ec7299749f0f@acm.org>
@ 2020-11-03 21:17                                                 ` Nathan Sidwell
       [not found]                                                 ` <df8d76cf-b00f-6b4c-3b8c-132843c15d62@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

This is the name-lookup changes.

Namespace-scope name-lookup now has to contend with merging the bindings 
from multiple visible modules, and ensuring said bindings are loaded (we 
load them lazily)


nathan
-- 
Nathan Sidwell


[-- Attachment #2: 26-c++-name-lookup.diff --]
[-- Type: text/x-patch, Size: 88499 bytes --]

diff --git c/gcc/cp/name-lookup.c w/gcc/cp/name-lookup.c
index 6a88e68c346..fc2323de95c 100644
--- c/gcc/cp/name-lookup.c
+++ w/gcc/cp/name-lookup.c
@@ -35,6 +35,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/name-hint.h"
 #include "c-family/known-headers.h"
 #include "c-family/c-spellcheck.h"
+#include "bitmap.h"
+#include "intl.h"
 
 static cxx_binding *cxx_binding_make (tree value, tree type);
 static cp_binding_level *innermost_nonclass_level (void);
@@ -46,16 +48,28 @@ static name_hint maybe_suggest_missing_std_header (location_t location,
 static name_hint suggest_alternatives_for_1 (location_t location, tree name,
 					     bool suggest_misspellings);
 
+/* Slots in MODULE_VEC.  */
+#define MODULE_SLOT_CURRENT 0	/* Slot for current TU.  */
+#define MODULE_SLOT_GLOBAL 1	/* Slot for merged global module. */
+#define MODULE_SLOT_PARTITION 2 /* Slot for merged partition entities
+				   (optional).  */
+#define MODULE_SLOTS_FIXED 2	/* Number of always-allocated slots.  */
+
 /* Create an overload suitable for recording an artificial TYPE_DECL
    and another decl.  We use this machanism to implement the struct
-   stat hack within a namespace.  It'd be nice to use it everywhere.  */
+   stat hack.  */
 
 #define STAT_HACK_P(N) ((N) && TREE_CODE (N) == OVERLOAD && OVL_LOOKUP_P (N))
+#define STAT_TYPE_VISIBLE_P(N) TREE_USED (OVERLOAD_CHECK (N))
 #define STAT_TYPE(N) TREE_TYPE (N)
 #define STAT_DECL(N) OVL_FUNCTION (N)
+#define STAT_VISIBLE(N) OVL_CHAIN (N)
 #define MAYBE_STAT_DECL(N) (STAT_HACK_P (N) ? STAT_DECL (N) : N)
 #define MAYBE_STAT_TYPE(N) (STAT_HACK_P (N) ? STAT_TYPE (N) : NULL_TREE)
 
+/* When a STAT_HACK_P is true, OVL_USING_P and OVL_EXPORT_P are valid
+   and apply to the hacked type.  */
+
 /* For regular (maybe) overloaded functions, we have OVL_HIDDEN_P.
    But we also need to indicate hiddenness on implicit type decls
    (injected friend classes), and (coming soon) decls injected from
@@ -116,7 +130,246 @@ find_namespace_value (tree ns, tree name)
   return b ? MAYBE_STAT_DECL (*b) : NULL_TREE;
 }
 
-/* Add DECL to the list of things declared in B.  */
+/* Look in *SLOT for a the binding of NAME in imported module IX.
+   Returns pointer to binding's slot, or NULL if not found.  Does a
+   binary search, as this is mainly used for random access during
+   importing.  Do not use for the fixed slots.  */
+
+static mc_slot *
+search_imported_binding_slot (tree *slot, unsigned ix)
+{
+  gcc_assert (ix);
+
+  if (!*slot)
+    return NULL;
+
+  if (TREE_CODE (*slot) != MODULE_VECTOR)
+    return NULL;
+  
+  unsigned clusters = MODULE_VECTOR_NUM_CLUSTERS (*slot);
+  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (*slot);
+
+  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+    {
+      clusters--;
+      cluster++;
+    }
+
+  while (clusters > 1)
+    {
+      unsigned half = clusters / 2;
+      gcc_checking_assert (cluster[half].indices[0].span);
+      if (cluster[half].indices[0].base > ix)
+	clusters = half;
+      else
+	{
+	  clusters -= half;
+	  cluster += half;
+	}
+    }
+
+  if (clusters)
+    /* Is it in this cluster?  */
+    for (unsigned off = 0; off != MODULE_VECTOR_SLOTS_PER_CLUSTER; off++)
+      {
+	if (!cluster->indices[off].span)
+	  break;
+	if (cluster->indices[off].base > ix)
+	  break;
+
+	if (cluster->indices[off].base + cluster->indices[off].span > ix)
+	  return &cluster->slots[off];
+      }
+
+  return NULL;
+}
+
+static void
+init_global_partition (module_cluster *cluster, tree decl)
+{
+  bool purview = true;
+
+  if (header_module_p ())
+    purview = false;
+  else if (TREE_PUBLIC (decl)
+	   && TREE_CODE (decl) == NAMESPACE_DECL
+	   && !DECL_NAMESPACE_ALIAS (decl))
+    purview = false;
+  else if (!get_originating_module (decl))
+    purview = false;
+
+  mc_slot *mslot;
+  if (!purview)
+    mslot = &cluster[0].slots[MODULE_SLOT_GLOBAL];
+  else
+    mslot = &cluster[MODULE_SLOT_PARTITION
+		     / MODULE_VECTOR_SLOTS_PER_CLUSTER]
+      .slots[MODULE_SLOT_PARTITION
+	     % MODULE_VECTOR_SLOTS_PER_CLUSTER];
+
+  if (*mslot)
+    decl = ovl_make (decl, *mslot);
+  *mslot = decl;
+
+  if (TREE_CODE (decl) == CONST_DECL)
+    {
+      tree type = TREE_TYPE (decl);
+      if (TREE_CODE (type) == ENUMERAL_TYPE
+	  && IDENTIFIER_ANON_P (DECL_NAME (TYPE_NAME (type)))
+	  && decl == TREE_VALUE (TYPE_VALUES (type)))
+	/* Anonymous enums are keyed by their first enumerator, put
+	   the TYPE_DECL here too.  */
+	*mslot = ovl_make (TYPE_NAME (type), *mslot);
+    }
+}
+
+/* Get the fixed binding slot IX.  Creating the vector if CREATE is
+   non-zero.  If CREATE is < 0, make sure there is at least 1 spare
+   slot for an import.  (It is an error for CREATE < 0 and the slot to
+   already exist.)  */
+
+static tree *
+get_fixed_binding_slot (tree *slot, tree name, unsigned ix, int create)
+{
+  gcc_checking_assert (ix <= MODULE_SLOT_PARTITION);
+
+  /* An assumption is that the fixed slots all reside in one cluster.  */
+  gcc_checking_assert (MODULE_VECTOR_SLOTS_PER_CLUSTER >= MODULE_SLOTS_FIXED);
+
+  if (!*slot || TREE_CODE (*slot) != MODULE_VECTOR)
+    {
+      if (ix == MODULE_SLOT_CURRENT)
+	/* The current TU can just use slot directly.  */
+	return slot;
+
+      if (!create)
+	return NULL;
+
+      /* The partition slot is only needed when we know we're a named
+	 module.  */
+      bool partition_slot = named_module_p ();
+      unsigned want = ((MODULE_SLOTS_FIXED + partition_slot + (create < 0)
+			+ MODULE_VECTOR_SLOTS_PER_CLUSTER - 1)
+		       / MODULE_VECTOR_SLOTS_PER_CLUSTER);
+      tree new_vec = make_module_vec (name, want);
+      MODULE_VECTOR_NUM_CLUSTERS (new_vec) = want;
+      module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (new_vec);
+
+      /* Initialize the fixed slots.  */
+      for (unsigned jx = MODULE_SLOTS_FIXED; jx--;)
+	{
+	  cluster[0].indices[jx].base = 0;
+	  cluster[0].indices[jx].span = 1;
+	  cluster[0].slots[jx] = NULL_TREE;
+	}
+
+      if (partition_slot)
+	{
+	  unsigned off = MODULE_SLOT_PARTITION % MODULE_VECTOR_SLOTS_PER_CLUSTER;
+	  unsigned ind = MODULE_SLOT_PARTITION / MODULE_VECTOR_SLOTS_PER_CLUSTER;
+	  cluster[ind].indices[off].base = 0;
+	  cluster[ind].indices[off].span = 1;
+	  cluster[ind].slots[off] = NULL_TREE;
+	}
+
+      if (tree orig = *slot)
+	{
+	  /* Propagate existing value to current slot.  */
+
+	  /* Propagate global & module entities to the global and
+	     partition slots.  */
+	  if (tree type = MAYBE_STAT_TYPE (orig))
+	    init_global_partition (cluster, type);
+
+	  for (ovl_iterator iter (MAYBE_STAT_DECL (orig)); iter; ++iter)
+	    {
+	      tree decl = *iter;
+
+	      /* Internal linkage entities are in deduplicateable.  */
+	      init_global_partition (cluster, decl);
+	    }
+
+	  if (cluster[0].slots[MODULE_SLOT_GLOBAL]
+	      && !(TREE_CODE (orig) == NAMESPACE_DECL
+		   && !DECL_NAMESPACE_ALIAS (orig)))
+	    {
+	      /* Note that we had some GMF entries.  */
+	      if (!STAT_HACK_P (orig))
+		orig = stat_hack (orig);
+
+	      MODULE_BINDING_GLOBAL_P (orig) = true;
+	    }
+
+	  cluster[0].slots[MODULE_SLOT_CURRENT] = orig;
+	}
+
+      *slot = new_vec;
+    }
+  else
+    gcc_checking_assert (create >= 0);
+
+  unsigned off = ix % MODULE_VECTOR_SLOTS_PER_CLUSTER;
+  module_cluster &cluster
+    = MODULE_VECTOR_CLUSTER (*slot, ix / MODULE_VECTOR_SLOTS_PER_CLUSTER);
+
+  /* There must always be slots for these indices  */
+  gcc_checking_assert (cluster.indices[off].span == 1
+		       && !cluster.indices[off].base
+		       && !cluster.slots[off].is_lazy ());
+
+  return reinterpret_cast<tree *> (&cluster.slots[off]);
+}
+
+/* *SLOT is a namespace binding slot.  Append a slot for imported
+   module IX.  */
+
+static mc_slot *
+append_imported_binding_slot (tree *slot, tree name, unsigned ix)
+{
+  gcc_checking_assert (ix);
+
+  if (!*slot ||  TREE_CODE (*slot) != MODULE_VECTOR)
+    /* Make an initial module vector.  */
+    get_fixed_binding_slot (slot, name, MODULE_SLOT_GLOBAL, -1);
+  else if (!MODULE_VECTOR_CLUSTER_LAST (*slot)
+	   ->indices[MODULE_VECTOR_SLOTS_PER_CLUSTER - 1].span)
+    /* There is space in the last cluster.  */;
+  else if (MODULE_VECTOR_NUM_CLUSTERS (*slot)
+	   != MODULE_VECTOR_ALLOC_CLUSTERS (*slot))
+    /* There is space in the vector.  */
+    MODULE_VECTOR_NUM_CLUSTERS (*slot)++;
+  else
+    {
+      /* Extend the vector.  */
+      unsigned have = MODULE_VECTOR_NUM_CLUSTERS (*slot);
+      unsigned want = (have * 3 + 1) / 2;
+
+      if (want > (unsigned short)~0)
+	want = (unsigned short)~0;
+
+      tree new_vec = make_module_vec (name, want);
+      MODULE_VECTOR_NUM_CLUSTERS (new_vec) = have + 1;
+      memcpy (MODULE_VECTOR_CLUSTER_BASE (new_vec),
+	      MODULE_VECTOR_CLUSTER_BASE (*slot),
+	      have * sizeof (module_cluster));
+      *slot = new_vec;
+    }
+
+  module_cluster *last = MODULE_VECTOR_CLUSTER_LAST (*slot);
+  for (unsigned off = 0; off != MODULE_VECTOR_SLOTS_PER_CLUSTER; off++)
+    if (!last->indices[off].span)
+      {
+	/* Fill the free slot of the cluster.  */
+	last->indices[off].base = ix;
+	last->indices[off].span = 1;
+	last->slots[off] = NULL_TREE;
+	return &last->slots[off];
+      }
+
+  gcc_unreachable ();
+}
+
+/* Add DECL to the list of things declared in binding level B.  */
 
 static void
 add_decl_to_level (cp_binding_level *b, tree decl)
@@ -171,8 +424,13 @@ public:
 
 public:
   tree name;	/* The identifier being looked for.  */
+
+  /* Usually we just add things to the VALUE binding, but we record
+     (hidden) IMPLICIT_TYPEDEFs on the type binding, which is used for
+     using-decl resolution.  */
   tree value;	/* A (possibly ambiguous) set of things found.  */
   tree type;	/* A type that has been found.  */
+
   LOOK_want want;  /* What kind of entity we want.  */
 
   bool deduping; /* Full deduping is needed because using declarations
@@ -238,7 +496,7 @@ private:
   void add_value (tree new_val);
   void add_type (tree new_type);
   bool process_binding (tree val_bind, tree type_bind);
-
+  unsigned process_module_binding (tree val_bind, tree type_bind, unsigned);
   /* Look in only namespace.  */
   bool search_namespace_only (tree scope);
   /* Look in namespace and its (recursive) inlines. Ignore using
@@ -262,15 +520,16 @@ private:
 
 private:
   void add_fns (tree);
-
   void adl_expr (tree);
   void adl_type (tree);
   void adl_template_arg (tree);
   void adl_class (tree);
+  void adl_enum (tree);
   void adl_bases (tree);
   void adl_class_only (tree);
   void adl_namespace (tree);
-  void adl_namespace_only (tree);
+  void adl_class_fns (tree);
+  void adl_namespace_fns (tree, bitmap);
 
 public:
   /* Search namespace + inlines + maybe usings as qualified lookup.  */
@@ -433,8 +692,8 @@ name_lookup::add_overload (tree fns)
       if (probe && TREE_CODE (probe) == OVERLOAD
 	  && OVL_DEDUP_P (probe))
 	{
-	  /* We're about to add something found by a using
-	     declaration, so need to engage deduping mode.  */
+	  /* We're about to add something found by multiple paths, so
+	     need to engage deduping mode.  */
 	  lookup_mark (value, true);
 	  deduping = true;
 	}
@@ -540,36 +799,190 @@ name_lookup::process_binding (tree new_val, tree new_type)
   return new_val != NULL_TREE;
 }
 
+/* If we're importing a module containing this binding, add it to the
+   lookup set.  The trickiness is with namespaces, we only want to
+   find it once.  */
+
+unsigned
+name_lookup::process_module_binding (tree new_val, tree new_type,
+				     unsigned marker)
+{
+  /* Optimize for (re-)finding a public namespace.  We only need to
+     look once.  */
+  if (new_val && !new_type
+      && TREE_CODE (new_val) == NAMESPACE_DECL
+      && TREE_PUBLIC (new_val)
+      && !DECL_NAMESPACE_ALIAS (new_val))
+    {
+      if (marker & 2)
+	return marker;
+      marker |= 2;
+    }
+
+  if (new_type || new_val)
+    marker |= process_binding (new_val, new_type);
+
+  return marker;
+}
+
 /* Look in exactly namespace SCOPE.  */
 
 bool
 name_lookup::search_namespace_only (tree scope)
 {
   bool found = false;
-
   if (tree *binding = find_namespace_slot (scope, name))
     {
-      tree value = *binding, type = NULL_TREE;
-
-      if (STAT_HACK_P (value))
+      tree val = *binding;
+      if (TREE_CODE (val) == MODULE_VECTOR)
 	{
-	  type = STAT_TYPE (value);
-	  value = STAT_DECL (value);
-	  
-	  if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+	  /* I presume the binding list is going to be sparser than
+	     the import bitmap.  Hence iterate over the former
+	     checking for bits set in the bitmap.  */
+	  bitmap imports = get_import_bitmap ();
+	  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (val);
+	  int marker = 0;
+	  int dup_detect = 0;
+
+	  if (tree bind = cluster->slots[MODULE_SLOT_CURRENT])
 	    {
-	      if (STAT_TYPE_HIDDEN_P (*binding))
-		type = NULL_TREE;
-	      if (STAT_DECL_HIDDEN_P (*binding))
-		value = NULL_TREE;
-	      else
+	      if (!deduping)
+		{
+		  if (named_module_purview_p ())
+		    {
+		      dup_detect |= 2;
+
+		      if (STAT_HACK_P (bind) && MODULE_BINDING_GLOBAL_P (bind))
+			dup_detect |= 1;
+		    }
+		  else
+		    dup_detect |= 1;
+		}
+	      tree type = NULL_TREE;
+	      tree value = bind;
+
+	      if (STAT_HACK_P (bind))
+		{
+		  type = STAT_TYPE (bind);
+		  value = STAT_DECL (bind);
+
+		  if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+		    {
+		      if (STAT_TYPE_HIDDEN_P (bind))
+			type = NULL_TREE;
+		      if (STAT_DECL_HIDDEN_P (bind))
+			value = NULL_TREE;
+		      else
+			value = ovl_skip_hidden (value);
+		    }
+		}
+	      else if (!bool (want & LOOK_want::HIDDEN_FRIEND))
 		value = ovl_skip_hidden (value);
+
+	      marker = process_module_binding (value, type, marker);
 	    }
+
+	  /* Scan the imported bindings.  */
+	  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (val);
+	  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+	    {
+	      ix--;
+	      cluster++;
+	    }
+
+	  /* Do this in forward order, so we load modules in an order
+	     the user expects.  */
+	  for (; ix--; cluster++)
+	    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	      {
+		/* Are we importing this module?  */
+		if (unsigned base = cluster->indices[jx].base)
+		  if (unsigned span = cluster->indices[jx].span)
+		    do
+		      if (bitmap_bit_p (imports, base))
+			goto found;
+		    while (++base, --span);
+		continue;
+
+	      found:;
+		/* Is it loaded?  */
+		if (cluster->slots[jx].is_lazy ())
+		  {
+		    gcc_assert (cluster->indices[jx].span == 1);
+		    lazy_load_binding (cluster->indices[jx].base,
+				       scope, name, &cluster->slots[jx]);
+		  }
+		tree bind = cluster->slots[jx];
+		if (!bind)
+		  /* Load errors could mean there's nothing here.  */
+		  continue;
+
+		/* Extract what we can see from here.  If there's no
+		   stat_hack, then everything was exported.  */
+		tree type = NULL_TREE;
+
+
+		/* If STAT_HACK_P is false, everything is visible, and
+		   there's no duplication possibilities.  */
+		if (STAT_HACK_P (bind))
+		  {
+		    if (!deduping)
+		      {
+			/* Do we need to engage deduplication?  */
+			int dup = 0;
+			if (MODULE_BINDING_GLOBAL_P (bind))
+			  dup = 1;
+			else if (MODULE_BINDING_PARTITION_P (bind))
+			  dup = 2;
+			if (unsigned hit = dup_detect & dup)
+			  {
+			    if ((hit & 1 && MODULE_VECTOR_GLOBAL_DUPS_P (val))
+				|| (hit & 2
+				    && MODULE_VECTOR_PARTITION_DUPS_P (val)))
+			      {
+				lookup_mark (value, true);
+				deduping = true;
+			      }
+			  }
+			dup_detect |= dup;
+		      }
+
+		    if (STAT_TYPE_VISIBLE_P (bind))
+		      type = STAT_TYPE (bind);
+		    bind = STAT_VISIBLE (bind);
+		  }
+
+		/* And process it.  */
+		marker = process_module_binding (bind, type, marker);
+	      }
+	  found |= marker & 1;
 	}
-      else if (!bool (want & LOOK_want::HIDDEN_FRIEND))
-	value = ovl_skip_hidden (value);
+      else
+	{
+	  /* Only a current module binding, visible from the current module.  */
+	  tree bind = *binding;
+	  tree value = bind, type = NULL_TREE;
 
-      found |= process_binding (value, type);
+	  if (STAT_HACK_P (bind))
+	    {
+	      type = STAT_TYPE (bind);
+	      value = STAT_DECL (bind);
+
+	      if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+		{
+		  if (STAT_TYPE_HIDDEN_P (bind))
+		    type = NULL_TREE;
+		  if (STAT_DECL_HIDDEN_P (bind))
+		    value = NULL_TREE;
+		  else
+		    value = ovl_skip_hidden (value);
+		}
+	    }
+	  else if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+	    value = ovl_skip_hidden (value);
+
+	  found |= process_binding (value, type);
+	}
     }
 
   return found;
@@ -777,20 +1190,147 @@ name_lookup::add_fns (tree fns)
   add_overload (fns);
 }
 
-/* Add functions of a namespace to the lookup structure.  */
+/* Add the overloaded fns of SCOPE.  */
 
 void
-name_lookup::adl_namespace_only (tree scope)
+name_lookup::adl_namespace_fns (tree scope, bitmap imports)
 {
-  mark_seen (scope);
+  if (tree *binding = find_namespace_slot (scope, name))
+    {
+      tree val = *binding;
+      if (TREE_CODE (val) != MODULE_VECTOR)
+	add_fns (ovl_skip_hidden (MAYBE_STAT_DECL (val)));
+      else
+	{
+	  /* I presume the binding list is going to be sparser than
+	     the import bitmap.  Hence iterate over the former
+	     checking for bits set in the bitmap.  */
+	  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (val);
+	  int dup_detect = 0;
 
-  /* Look down into inline namespaces.  */
-  if (vec<tree, va_gc> *inlinees = DECL_NAMESPACE_INLINEES (scope))
-    for (unsigned ix = inlinees->length (); ix--;)
-      adl_namespace_only ((*inlinees)[ix]);
+	  if (tree bind = cluster->slots[MODULE_SLOT_CURRENT])
+	    {
+	      /* The current TU's bindings must be visible, we don't
+		 need to check the bitmaps.  */
+
+	      if (!deduping)
+		{
+		  if (named_module_purview_p ())
+		    {
+		      dup_detect |= 2;
+
+		      if (STAT_HACK_P (bind) && MODULE_BINDING_GLOBAL_P (bind))
+			dup_detect |= 1;
+		    }
+		  else
+		    dup_detect |= 1;
+		}
+
+	      add_fns (ovl_skip_hidden (MAYBE_STAT_DECL (bind)));
+	    }
+
+	  /* Scan the imported bindings.  */
+	  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (val);
+	  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+	    {
+	      ix--;
+	      cluster++;
+	    }
+
+	  /* Do this in forward order, so we load modules in an order
+	     the user expects.  */
+	  for (; ix--; cluster++)
+	    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	      {
+		/* Functions are never on merged slots.  */
+		if (!cluster->indices[jx].base
+		    || cluster->indices[jx].span != 1)
+		  continue;
+
+		/* Is this slot visible?  */
+		if (!bitmap_bit_p (imports, cluster->indices[jx].base))
+		  continue;
+
+		/* Is it loaded.  */
+		if (cluster->slots[jx].is_lazy ())
+		  lazy_load_binding (cluster->indices[jx].base,
+				     scope, name, &cluster->slots[jx]);
+
+		tree bind = cluster->slots[jx];
+		if (!bind)
+		  /* Load errors could mean there's nothing here.  */
+		  continue;
+
+		if (STAT_HACK_P (bind))
+		  {
+		    if (!deduping)
+		      {
+			/* Do we need to engage deduplication?  */
+			int dup = 0;
+			if (MODULE_BINDING_GLOBAL_P (bind))
+			  dup = 1;
+			else if (MODULE_BINDING_PARTITION_P (bind))
+			  dup = 2;
+			if (unsigned hit = dup_detect & dup)
+			  {
+			    if ((hit & 1 && MODULE_VECTOR_GLOBAL_DUPS_P (val))
+				|| (hit & 2
+				    && MODULE_VECTOR_PARTITION_DUPS_P (val)))
+			      {
+				lookup_mark (value, true);
+				deduping = true;
+			      }
+			  }
+			dup_detect |= dup;
+		      }
+
+		    bind = STAT_VISIBLE (bind);
+		  }
+
+		add_fns (bind);
+	      }
+	}
+    }
+}
+
+/* Add the hidden friends of SCOPE.  */
+
+void
+name_lookup::adl_class_fns (tree type)
+{
+  /* Add friends.  */
+  for (tree list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type));
+       list; list = TREE_CHAIN (list))
+    if (name == FRIEND_NAME (list))
+      {
+	tree context = NULL_TREE; /* Lazily computed.  */
+	for (tree friends = FRIEND_DECLS (list); friends;
+	     friends = TREE_CHAIN (friends))
+	  {
+	    tree fn = TREE_VALUE (friends);
+
+	    /* Only interested in global functions with potentially hidden
+	       (i.e. unqualified) declarations.  */
+	    if (!context)
+	      context = decl_namespace_context (type);
+	    if (CP_DECL_CONTEXT (fn) != context)
+	      continue;
+
+	    if (!deduping)
+	      {
+		lookup_mark (value, true);
+		deduping = true;
+	      }
+
+	    /* Template specializations are never found by name lookup.
+	       (Templates themselves can be found, but not template
+	       specializations.)  */
+	    if (TREE_CODE (fn) == FUNCTION_DECL && DECL_USE_TEMPLATE (fn))
+	      continue;
 
-  if (tree fns = find_namespace_value (scope, name))
-    add_fns (ovl_skip_hidden (fns));
+	    add_fns (fn);
+	  }
+      }
 }
 
 /* Find the containing non-inlined namespace, add it and all its
@@ -799,14 +1339,17 @@ name_lookup::adl_namespace_only (tree scope)
 void
 name_lookup::adl_namespace (tree scope)
 {
-  if (seen_p (scope))
+  if (see_and_mark (scope))
     return;
 
-  /* Find the containing non-inline namespace.  */
-  while (DECL_NAMESPACE_INLINE_P (scope))
-    scope = CP_DECL_CONTEXT (scope);
+  /* Look down into inline namespaces.  */
+  if (vec<tree, va_gc> *inlinees = DECL_NAMESPACE_INLINEES (scope))
+    for (unsigned ix = inlinees->length (); ix--;)
+      adl_namespace ((*inlinees)[ix]);
 
-  adl_namespace_only (scope);
+  if (DECL_NAMESPACE_INLINE_P (scope))
+    /* Mark parent.  */
+    adl_namespace (CP_DECL_CONTEXT (scope));
 }
 
 /* Adds the class and its friends to the lookup structure.  */
@@ -826,31 +1369,6 @@ name_lookup::adl_class_only (tree type)
 
   tree context = decl_namespace_context (type);
   adl_namespace (context);
-
-  complete_type (type);
-
-  /* Add friends.  */
-  for (tree list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type)); list;
-       list = TREE_CHAIN (list))
-    if (name == FRIEND_NAME (list))
-      for (tree friends = FRIEND_DECLS (list); friends;
-	   friends = TREE_CHAIN (friends))
-	{
-	  tree fn = TREE_VALUE (friends);
-
-	  /* Only interested in global functions with potentially hidden
-	     (i.e. unqualified) declarations.  */
-	  if (CP_DECL_CONTEXT (fn) != context)
-	    continue;
-
-	  /* Template specializations are never found by name lookup.
-	     (Templates themselves can be found, but not template
-	     specializations.)  */
-	  if (TREE_CODE (fn) == FUNCTION_DECL && DECL_USE_TEMPLATE (fn))
-	    continue;
-
-	  add_fns (fn);
-	}
 }
 
 /* Adds the class and its bases to the lookup structure.
@@ -873,7 +1391,7 @@ name_lookup::adl_bases (tree type)
 }
 
 /* Adds everything associated with a class argument type to the lookup
-   structure.  Returns true on error.
+   structure.
 
    If T is a class type (including unions), its associated classes are: the
    class itself; the class of which it is a member, if any; and its direct
@@ -897,11 +1415,13 @@ name_lookup::adl_class (tree type)
     return;
 
   type = TYPE_MAIN_VARIANT (type);
+
   /* We don't set found here because we have to have set seen first,
      which is done in the adl_bases walk.  */
   if (found_p (type))
     return;
 
+  complete_type (type);
   adl_bases (type);
   mark_found (type);
 
@@ -918,6 +1438,19 @@ name_lookup::adl_class (tree type)
     }
 }
 
+void
+name_lookup::adl_enum (tree type)
+{
+  type = TYPE_MAIN_VARIANT (type);
+  if (see_and_mark (type))
+    return;
+
+  if (TYPE_CLASS_SCOPE_P (type))
+    adl_class_only (TYPE_CONTEXT (type));
+  else
+    adl_namespace (decl_namespace_context (type));
+}
+
 void
 name_lookup::adl_expr (tree expr)
 {
@@ -1003,9 +1536,7 @@ name_lookup::adl_type (tree type)
       return;
 
     case ENUMERAL_TYPE:
-      if (TYPE_CLASS_SCOPE_P (type))
-	adl_class_only (TYPE_CONTEXT (type));
-      adl_namespace (decl_namespace_context (type));
+      adl_enum (type);
       return;
 
     case LANG_TYPE:
@@ -1074,10 +1605,9 @@ name_lookup::adl_template_arg (tree arg)
 tree
 name_lookup::search_adl (tree fns, vec<tree, va_gc> *args)
 {
-  deduping = true;
-  lookup_mark (fns, true);
-  value = fns;
-
+  gcc_checking_assert (!vec_safe_length (scopes));
+  
+  /* Gather each associated entity onto the lookup's scope list.  */
   unsigned ix;
   tree arg;
 
@@ -1089,7 +1619,91 @@ name_lookup::search_adl (tree fns, vec<tree, va_gc> *args)
     else
       adl_expr (arg);
 
-  fns = value;
+  if (vec_safe_length (scopes))
+    {
+      /* Now do the lookups.  */
+      if (fns)
+	{
+	  deduping = true;
+	  lookup_mark (fns, true);
+	}
+      value = fns;
+
+      /* INST_PATH will be NULL, if this is /not/ 2nd-phase ADL.  */
+      bitmap inst_path = NULL;
+      /* VISIBLE is the regular import bitmap.  */
+      bitmap visible = module_visible_instantiation_path (&inst_path);
+
+      for (unsigned ix = scopes->length (); ix--;)
+	{
+	  tree scope = (*scopes)[ix];
+	  if (TREE_CODE (scope) == NAMESPACE_DECL)
+	    adl_namespace_fns (scope, visible);
+	  else
+	    {
+	      if (RECORD_OR_UNION_TYPE_P (scope))
+		adl_class_fns (scope);
+
+	      /* During 2nd phase ADL: Any exported declaration D in N
+		 declared within the purview of a named module M
+		 (10.2) is visible if there is an associated entity
+		 attached to M with the same innermost enclosing
+		 non-inline namespace as D.
+		 [basic.lookup.argdep]/4.4 */ 
+
+	      if (!inst_path)
+		/* Not 2nd phase.  */
+		continue;
+
+	      tree ctx = CP_DECL_CONTEXT (TYPE_NAME (scope));
+	      if (TREE_CODE (ctx) != NAMESPACE_DECL)
+		/* Not namespace-scope class.  */
+		continue;
+
+	      tree origin = get_originating_module_decl (TYPE_NAME (scope));
+	      if (!DECL_LANG_SPECIFIC (origin)
+		  || !DECL_MODULE_IMPORT_P (origin))
+		/* Not imported.  */
+		continue;
+
+	      unsigned module = get_importing_module (origin);
+
+	      if (!bitmap_bit_p (inst_path, module))
+		/* Not on path of instantiation.  */
+		continue;
+
+	      if (bitmap_bit_p (visible, module))
+		/* If the module was in the visible set, we'll look at
+		   its namespace partition anyway.  */
+		continue;
+
+	      if (tree *slot = find_namespace_slot (ctx, name, false))
+		if (mc_slot *mslot = search_imported_binding_slot (slot, module))
+		  {
+		    if (mslot->is_lazy ())
+		      lazy_load_binding (module, ctx, name, mslot);
+
+		    if (tree bind = *mslot)
+		      {
+			if (!deduping)
+			  {
+			    /* We must turn on deduping, because some
+			       other class from this module might also
+			       be in this namespace.  */
+			    deduping = true;
+			    lookup_mark (value, true);
+			  }
+
+			/* Add the exported fns  */
+			if (STAT_HACK_P (bind))
+			  add_fns (STAT_VISIBLE (bind));
+		      }
+		  }
+	    }
+	}
+
+      fns = value;
+    }
 
   return fns;
 }
@@ -1284,6 +1898,41 @@ get_class_binding_direct (tree klass, tree name, bool want_type)
   return val;
 }
 
+/* We're about to lookup NAME in KLASS.  Make sure any lazily declared
+   members are now declared.  */
+
+static void
+maybe_lazily_declare (tree klass, tree name)
+{
+  tree main_decl = TYPE_NAME (TYPE_MAIN_VARIANT (klass));
+  if (DECL_LANG_SPECIFIC (main_decl)
+      && DECL_MODULE_PENDING_MEMBERS_P (main_decl))
+    lazy_load_members (main_decl);
+
+  /* Lazily declare functions, if we're going to search these.  */
+  if (IDENTIFIER_CTOR_P (name))
+    {
+      if (CLASSTYPE_LAZY_DEFAULT_CTOR (klass))
+	lazily_declare_fn (sfk_constructor, klass);
+      if (CLASSTYPE_LAZY_COPY_CTOR (klass))
+	lazily_declare_fn (sfk_copy_constructor, klass);
+      if (CLASSTYPE_LAZY_MOVE_CTOR (klass))
+	lazily_declare_fn (sfk_move_constructor, klass);
+    }
+  else if (IDENTIFIER_DTOR_P (name))
+    {
+      if (CLASSTYPE_LAZY_DESTRUCTOR (klass))
+	lazily_declare_fn (sfk_destructor, klass);
+    }
+  else if (name == assign_op_identifier)
+    {
+      if (CLASSTYPE_LAZY_COPY_ASSIGN (klass))
+	lazily_declare_fn (sfk_copy_assignment, klass);
+      if (CLASSTYPE_LAZY_MOVE_ASSIGN (klass))
+	lazily_declare_fn (sfk_move_assignment, klass);
+    }
+}
+
 /* Look for NAME's binding in exactly KLASS.  See
    get_class_binding_direct for argument description.  Does lazy
    special function creation as necessary.  */
@@ -1294,30 +1943,7 @@ get_class_binding (tree klass, tree name, bool want_type /*=false*/)
   klass = complete_type (klass);
 
   if (COMPLETE_TYPE_P (klass))
-    {
-      /* Lazily declare functions, if we're going to search these.  */
-      if (IDENTIFIER_CTOR_P (name))
-	{
-	  if (CLASSTYPE_LAZY_DEFAULT_CTOR (klass))
-	    lazily_declare_fn (sfk_constructor, klass);
-	  if (CLASSTYPE_LAZY_COPY_CTOR (klass))
-	    lazily_declare_fn (sfk_copy_constructor, klass);
-	  if (CLASSTYPE_LAZY_MOVE_CTOR (klass))
-	    lazily_declare_fn (sfk_move_constructor, klass);
-	}
-      else if (IDENTIFIER_DTOR_P (name))
-	{
-	  if (CLASSTYPE_LAZY_DESTRUCTOR (klass))
-	    lazily_declare_fn (sfk_destructor, klass);
-	}
-      else if (name == assign_op_identifier)
-	{
-	  if (CLASSTYPE_LAZY_COPY_ASSIGN (klass))
-	    lazily_declare_fn (sfk_copy_assignment, klass);
-	  if (CLASSTYPE_LAZY_MOVE_ASSIGN (klass))
-	    lazily_declare_fn (sfk_move_assignment, klass);
-	}
-    }
+    maybe_lazily_declare (klass, name);
 
   return get_class_binding_direct (klass, name, want_type);
 }
@@ -1338,14 +1964,11 @@ find_member_slot (tree klass, tree name)
       vec_alloc (member_vec, 8);
       CLASSTYPE_MEMBER_VEC (klass) = member_vec;
       if (complete_p)
-	{
-	  /* If the class is complete but had no member_vec, we need
-	     to add the TYPE_FIELDS into it.  We're also most likely
-	     to be adding ctors & dtors, so ask for 6 spare slots (the
-	     abstract cdtors and their clones).  */
-	  set_class_bindings (klass, 6);
-	  member_vec = CLASSTYPE_MEMBER_VEC (klass);
-	}
+	/* If the class is complete but had no member_vec, we need to
+	   add the TYPE_FIELDS into it.  We're also most likely to be
+	   adding ctors & dtors, so ask for 6 spare slots (the
+	   abstract cdtors and their clones).  */
+	member_vec = set_class_bindings (klass, 6);
     }
 
   if (IDENTIFIER_CONV_OP_P (name))
@@ -1665,7 +2288,11 @@ member_vec_dedup (vec<tree, va_gc> *member_vec)
 	  if (!current)
 	    current = to_type;
 	  else
-	    current = stat_hack (current, to_type);
+	    {
+	      current = stat_hack (current, to_type);
+	      /* Also point the chain at the decls.  */
+	      OVL_CHAIN (current) = STAT_DECL (current);
+	    }
 	}
 
       if (current)
@@ -1687,18 +2314,18 @@ member_vec_dedup (vec<tree, va_gc> *member_vec)
    no existing MEMBER_VEC and fewer than 8 fields, do nothing.  We
    know there must be at least 1 field -- the self-reference
    TYPE_DECL, except for anon aggregates, which will have at least
-   one field anyway.  */
+   one field anyway.  If EXTRA < 0, always create the vector.  */
 
-void 
-set_class_bindings (tree klass, unsigned extra)
+vec<tree, va_gc> *
+set_class_bindings (tree klass, int extra)
 {
   unsigned n_fields = count_class_fields (klass);
   vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass);
 
-  if (member_vec || n_fields >= 8)
+  if (member_vec || n_fields >= 8 || extra < 0)
     {
       /* Append the new fields.  */
-      vec_safe_reserve_exact (member_vec, extra + n_fields);
+      vec_safe_reserve_exact (member_vec, n_fields + (extra >= 0 ? extra : 0));
       member_vec_append_class_fields (member_vec, klass);
     }
 
@@ -1708,6 +2335,8 @@ set_class_bindings (tree klass, unsigned extra)
       member_vec->qsort (member_name_cmp);
       member_vec_dedup (member_vec);
     }
+
+  return member_vec;
 }
 
 /* Insert lately defined enum ENUMTYPE into KLASS for the sorted case.  */
@@ -1856,7 +2485,7 @@ push_binding (tree id, tree decl, cp_binding_level* level)
 void
 pop_local_binding (tree id, tree decl)
 {
-  if (id == NULL_TREE)
+  if (!id || IDENTIFIER_ANON_P (id))
     /* It's easiest to write the loops that call this function without
        checking whether or not the entities involved have names.  We
        get here for such an entity.  */
@@ -2202,8 +2831,9 @@ update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
   tree to_type = old_type;
   bool local_overload = false;
 
-  gcc_assert (level->kind == sk_namespace ? !binding
+  gcc_assert (!level || level->kind == sk_namespace ? !binding
 	      : level->kind != sk_class && !slot);
+
   if (old == error_mark_node)
     old = NULL_TREE;
 
@@ -2279,7 +2909,7 @@ update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	warning (OPT_Wshadow, "%q#D hides constructor for %q#D",
 		 decl, to_type);
 
-      local_overload = old && level->kind != sk_namespace;
+      local_overload = old && level && level->kind != sk_namespace;
       to_val = ovl_insert (decl, old, -int (hiding));
     }
   else if (old)
@@ -2290,11 +2920,8 @@ update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
       else if (TREE_CODE (old) == TYPE_DECL)
 	{
 	  if (same_type_p (TREE_TYPE (old), TREE_TYPE (decl)))
-	    {
-	      /* Two type decls to the same type.  Do nothing.  */
-	      gcc_checking_assert (!hiding);
-	      return old;
-	    }
+	    /* Two type decls to the same type.  Do nothing.  */
+	    return old;
 	  else
 	    goto conflict;
 	}
@@ -2306,7 +2933,7 @@ update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	    goto conflict;
 
 	  /* The new one must be an alias at this point.  */
-	  gcc_assert (DECL_NAMESPACE_ALIAS (decl) && !hiding);
+	  gcc_assert (DECL_NAMESPACE_ALIAS (decl));
 	  return old;
 	}
       else if (TREE_CODE (old) == VAR_DECL)
@@ -2342,7 +2969,13 @@ update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	  update_local_overload (binding, to_val);
 	}
       else
-	add_decl_to_level (level, decl);
+	{
+	  /* Don't add namespaces here.  They're done in
+	     push_namespace.  */
+	  if (level && (TREE_CODE (decl) != NAMESPACE_DECL
+			|| DECL_NAMESPACE_ALIAS (decl)))
+	    add_decl_to_level (level, decl);
+	}
 
       if (slot)
 	{
@@ -2495,6 +3128,11 @@ check_local_shadow (tree decl)
   if (DECL_EXTERNAL (decl))
     return;
 
+  /* No need to do it when cloning, and with modules this can cause
+     out-of-order reading when we try and instantiate stuff.  */
+  if (current_function_decl && DECL_CLONED_FUNCTION_P (current_function_decl))
+    return;
+
   tree old = NULL_TREE;
   cp_binding_level *old_scope = NULL;
   if (cxx_binding *binding = outer_binding (DECL_NAME (decl), NULL, true))
@@ -2847,6 +3485,182 @@ push_local_extern_decl_alias (tree decl)
   DECL_LOCAL_DECL_ALIAS (decl) = alias;
 }
 
+/* NS needs to be exported, mark it and all its parents as exported.  */
+
+static void
+implicitly_export_namespace (tree ns)
+{
+  while (!DECL_MODULE_EXPORT_P (ns))
+    {
+      DECL_MODULE_EXPORT_P (ns) = true;
+      ns = CP_DECL_CONTEXT (ns);
+    }
+}
+
+/* DECL has just been bound at LEVEL.  finish up the bookkeeping.  */
+
+static void
+newbinding_bookkeeping (tree name, tree decl, cp_binding_level *level)
+{
+  if (TREE_CODE (decl) == TYPE_DECL)
+    {
+      tree type = TREE_TYPE (decl);
+
+      if (type != error_mark_node)
+	{
+	  if (TYPE_NAME (type) != decl)
+	    set_underlying_type (decl);
+
+	  set_identifier_type_value_with_scope (name, decl, level);
+
+	  if (level->kind != sk_namespace
+	      && !instantiating_current_function_p ())
+	    /* If this is a locally defined typedef in a function that
+	       is not a template instantation, record it to implement
+	       -Wunused-local-typedefs.  */
+	    record_locally_defined_typedef (decl);
+	}
+    }
+  else
+    {
+      if (VAR_P (decl) && !DECL_LOCAL_DECL_P (decl))
+	maybe_register_incomplete_var (decl);
+
+      if (VAR_OR_FUNCTION_DECL_P (decl)
+	  && DECL_EXTERN_C_P (decl))
+	check_extern_c_conflict (decl);
+    }
+}
+
+/* DECL is a global or module-purview entity.  If it has non-internal
+   linkage, and we have a module vector, record it in the appropriate
+   slot.  We have already checked for duplicates.  */
+
+static void
+maybe_record_mergeable_decl (tree *slot, tree name, tree decl)
+{
+  if (TREE_CODE (*slot) != MODULE_VECTOR)
+    return;
+
+  if (!TREE_PUBLIC (CP_DECL_CONTEXT (decl)))
+    /* Member of internal namespace.  */
+    return;
+
+  tree not_tmpl = STRIP_TEMPLATE (decl);
+  if ((TREE_CODE (not_tmpl) == FUNCTION_DECL
+       || TREE_CODE (not_tmpl) == VAR_DECL)
+      && DECL_THIS_STATIC (not_tmpl))
+    /* Internal linkage.  */
+    return;
+
+  bool partition = named_module_p ();
+  tree *gslot = get_fixed_binding_slot
+    (slot, name, partition ? MODULE_SLOT_PARTITION : MODULE_SLOT_GLOBAL, true);
+
+  if (!partition)
+    {
+      mc_slot &orig
+	= MODULE_VECTOR_CLUSTER (*gslot, 0).slots[MODULE_SLOT_CURRENT];
+
+      if (!STAT_HACK_P (tree (orig)))
+	orig = stat_hack (tree (orig));
+
+      MODULE_BINDING_GLOBAL_P (tree (orig)) = true;
+    }
+
+  add_mergeable_namespace_entity (gslot, decl);
+}
+
+/* DECL is being pushed.  Check whether it hides or ambiguates
+   something seen as an import.  This include decls seen in our own
+   interface, which is OK.  Also, check for merging a
+   global/partition decl.  */
+
+static tree
+check_module_override (tree decl, tree mvec, bool hiding,
+		       tree scope, tree name)
+{
+  bitmap imports = get_import_bitmap ();
+  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (mvec);
+  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (mvec);
+
+  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+    {
+      cluster++;
+      ix--;
+    }
+
+  for (; ix--; cluster++)
+    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+      {
+	/* Are we importing this module?  */
+	if (cluster->indices[jx].span != 1)
+	  continue;
+	if (!cluster->indices[jx].base)
+	  continue;
+	if (!bitmap_bit_p (imports, cluster->indices[jx].base))
+	  continue;
+	/* Is it loaded? */
+	if (cluster->slots[jx].is_lazy ())
+	  {
+	    gcc_assert (cluster->indices[jx].span == 1);
+	    lazy_load_binding (cluster->indices[jx].base,
+			       scope, name, &cluster->slots[jx]);
+	  }
+	tree bind = cluster->slots[jx];
+	if (!bind)
+	  /* Errors could cause there to be nothing.  */
+	  continue;
+
+	if (STAT_HACK_P (bind))
+	  /* We do not have to check STAT_TYPE here, the xref_tag
+	     machinery deals with that problem. */
+	  bind = STAT_VISIBLE (bind);
+
+	for (ovl_iterator iter (bind); iter; ++iter)
+	  if (iter.using_p ())
+	    ;
+	  else if (tree match = duplicate_decls (decl, *iter, hiding))
+	    {
+	      if (TREE_CODE (match) == TYPE_DECL)
+		/* The IDENTIFIER will have the type referring to the
+		   now-smashed TYPE_DECL, because ...?  Reset it.  */
+		SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (match));
+
+	      return match;
+	    }
+      }
+
+  if (TREE_PUBLIC (scope) && TREE_PUBLIC (decl) && !not_module_p ()
+      /* Namespaces are dealt with specially in
+	 make_namespace_finish.  */
+      && !(TREE_CODE (decl) == NAMESPACE_DECL && !DECL_NAMESPACE_ALIAS (decl)))
+    {
+      /* Look in the appropriate mergeable decl slot.  */
+      tree mergeable = NULL_TREE;
+      if (named_module_p ())
+	mergeable = MODULE_VECTOR_CLUSTER (mvec, MODULE_SLOT_PARTITION
+					   / MODULE_VECTOR_SLOTS_PER_CLUSTER)
+	  .slots[MODULE_SLOT_PARTITION % MODULE_VECTOR_SLOTS_PER_CLUSTER];
+      else
+	mergeable = MODULE_VECTOR_CLUSTER (mvec, 0).slots[MODULE_SLOT_GLOBAL];
+
+      for (ovl_iterator iter (mergeable); iter; ++iter)
+	{
+	  tree match = *iter;
+	  
+	  if (duplicate_decls (decl, match, hiding))
+	    {
+	      if (TREE_CODE (match) == TYPE_DECL)
+		SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (match));
+	      return match;
+	    }
+	}
+    }
+
+  return NULL_TREE;
+}
+
 /* Record DECL as belonging to the current lexical scope.  Check for
    errors (such as an incompatible declaration for the same name
    already seen in the same scope).  IS_FRIEND is true if DECL is
@@ -2875,11 +3689,12 @@ do_pushdecl (tree decl, bool hiding)
   /* An anonymous namespace has a NULL DECL_NAME, but we still want to
      insert it.  Other NULL-named decls, not so much.  */
   tree name = DECL_NAME (decl);
-  if (name || TREE_CODE (decl) == NAMESPACE_DECL)
+  if (name ? !IDENTIFIER_ANON_P (name) : TREE_CODE (decl) == NAMESPACE_DECL)
     {
       cxx_binding *binding = NULL; /* Local scope binding.  */
       tree ns = NULL_TREE; /* Searched namespace.  */
       tree *slot = NULL; /* Binding slot in namespace.  */
+      tree *mslot = NULL; /* Current module slot in namespace.  */
       tree old = NULL_TREE;
 
       if (level->kind == sk_namespace)
@@ -2893,7 +3708,11 @@ do_pushdecl (tree decl, bool hiding)
 	     that's where we'll be pushing anyway.  */
 	  slot = find_namespace_slot (ns, name, ns == current_namespace);
 	  if (slot)
-	    old = MAYBE_STAT_DECL (*slot);
+	    {
+	      mslot = get_fixed_binding_slot (slot, name, MODULE_SLOT_CURRENT,
+					      ns == current_namespace);
+	      old = MAYBE_STAT_DECL (*mslot);
+	    }
 	}
       else
 	{
@@ -2908,6 +3727,10 @@ do_pushdecl (tree decl, bool hiding)
       for (ovl_iterator iter (old); iter; ++iter)
 	if (iter.using_p ())
 	  ; /* Ignore using decls here.  */
+	else if (iter.hidden_p ()
+		 && DECL_LANG_SPECIFIC (*iter)
+		 && DECL_MODULE_IMPORT_P (*iter))
+	  ; /* An undeclared builtin imported from elsewhere.  */
 	else if (tree match
 		 = duplicate_decls (decl, *iter, hiding, iter.hidden_p ()))
 	  {
@@ -2946,6 +3769,26 @@ do_pushdecl (tree decl, bool hiding)
 	    return match;
 	  }
 
+      /* Check for redeclaring an import.  */
+      if (slot && *slot && TREE_CODE (*slot) == MODULE_VECTOR)
+	if (tree match
+	    = check_module_override (decl, *slot, hiding, ns, name))
+	  {
+	    if (match == error_mark_node)
+	      return match;
+
+	    /* We found a decl in an interface, push it into this
+	       binding.  */
+	    decl = update_binding (NULL, binding, mslot, old,
+				   match, hiding);
+
+	    if (match == decl && DECL_MODULE_EXPORT_P (decl)
+		&& !DECL_MODULE_EXPORT_P (level->this_entity))
+	      implicitly_export_namespace (level->this_entity);
+
+	    return decl;
+	  }
+
       /* We are pushing a new decl.  */
 
       /* Skip a hidden builtin we failed to match already.  There can
@@ -2953,104 +3796,400 @@ do_pushdecl (tree decl, bool hiding)
       if (old && anticipated_builtin_p (old))
 	old = OVL_CHAIN (old);
 
-      check_template_shadow (decl);
+      check_template_shadow (decl);
+
+      if (DECL_DECLARES_FUNCTION_P (decl))
+	{
+	  check_default_args (decl);
+
+	  if (hiding)
+	    {
+	      if (level->kind != sk_namespace)
+		{
+		  /* In a local class, a friend function declaration must
+		     find a matching decl in the innermost non-class scope.
+		     [class.friend/11] */
+		  error_at (DECL_SOURCE_LOCATION (decl),
+			    "friend declaration %qD in local class without "
+			    "prior local declaration", decl);
+		  /* Don't attempt to push it.  */
+		  return error_mark_node;
+		}
+	    }
+	}
+
+      if (level->kind != sk_namespace)
+	{
+	  check_local_shadow (decl);
+
+	  if (TREE_CODE (decl) == NAMESPACE_DECL)
+	    /* A local namespace alias.  */
+	    set_identifier_type_value_with_scope (name, NULL_TREE, level);
+
+	  if (!binding)
+	    binding = create_local_binding (level, name);
+	}
+      else if (!slot)
+	{
+	  ns = current_namespace;
+	  slot = find_namespace_slot (ns, name, true);
+	  mslot = get_fixed_binding_slot (slot, name, MODULE_SLOT_CURRENT, true);
+	  /* Update OLD to reflect the namespace we're going to be
+	     pushing into.  */
+	  old = MAYBE_STAT_DECL (*mslot);
+	}
+
+      old = update_binding (level, binding, mslot, old, decl, hiding);
+
+      if (old != decl)
+	/* An existing decl matched, use it.  */
+	decl = old;
+      else
+	{
+	  newbinding_bookkeeping (name, decl, level);
+
+	  if (VAR_OR_FUNCTION_DECL_P (decl)
+	      && DECL_LOCAL_DECL_P (decl)
+	      && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
+	    push_local_extern_decl_alias (decl);
+
+	  if (level->kind == sk_namespace
+	      && TREE_PUBLIC (level->this_entity))
+	    {
+	      if (TREE_CODE (decl) != CONST_DECL
+		  && DECL_MODULE_EXPORT_P (decl)
+		  && !DECL_MODULE_EXPORT_P (level->this_entity))
+		implicitly_export_namespace (level->this_entity);
+
+	      if (!not_module_p ())
+		maybe_record_mergeable_decl (slot, name, decl);
+	    }
+	}
+    }
+  else
+    add_decl_to_level (level, decl);
+
+  return decl;
+}
+
+/* Record a decl-node X as belonging to the current lexical scope.
+   It's a friend if IS_FRIEND is true -- which affects exactly where
+   we push it.  */
+
+tree
+pushdecl (tree x, bool hiding)
+{
+  bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
+  tree ret = do_pushdecl (x, hiding);
+  timevar_cond_stop (TV_NAME_LOOKUP, subtime);
+  return ret;
+}
+
+/* A mergeable entity is being loaded into namespace NS slot NAME.
+   Create and return the appropriate vector slot for that.  Either a
+   GMF slot or a module-specific one.  */
+
+tree *
+mergeable_namespace_slots (tree ns, tree name, bool is_global, tree *vec)
+{
+  tree *mslot = find_namespace_slot (ns, name, true);
+  tree *vslot = get_fixed_binding_slot
+    (mslot, name, is_global ? MODULE_SLOT_GLOBAL : MODULE_SLOT_PARTITION, true);
+
+  gcc_checking_assert (TREE_CODE (*mslot) == MODULE_VECTOR);
+  *vec = *mslot;
+
+  return vslot;
+}
+
+/* DECL is a new mergeable namespace-scope decl.  Add it to the
+   mergeable entities on GSLOT.  */
+
+void
+add_mergeable_namespace_entity (tree *gslot, tree decl)
+{
+  *gslot = ovl_make (decl, *gslot);
+}
+
+/* A mergeable entity of KLASS called NAME is being loaded.  Return
+   the set of things it could be.  All such non-as_base classes have
+   been given a member vec.  */
+
+tree
+lookup_class_binding (tree klass, tree name)
+{
+  tree found = NULL_TREE;
+
+  if (!COMPLETE_TYPE_P (klass))
+    ;
+  else if (TYPE_LANG_SPECIFIC (klass))
+    {
+      vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass);
+
+      found = member_vec_binary_search (member_vec, name);
+      if (IDENTIFIER_CONV_OP_P (name))
+	{
+	  gcc_checking_assert (name == conv_op_identifier);
+	  if (found)
+	    found = OVL_CHAIN (found);
+	}
+    }
+  else
+    {
+      gcc_checking_assert (IS_FAKE_BASE_TYPE (klass)
+			   || TYPE_PTRMEMFUNC_P (klass));
+      found = fields_linear_search (klass, name, false);
+    }
+
+  return found;
+}
+
+/* Given a namespace-level binding BINDING, walk it, calling CALLBACK
+   for all decls of the current module.  When partitions are involved,
+   decls might be mentioned more than once.   */
+
+unsigned
+walk_module_binding (tree binding, bitmap partitions,
+		     bool (*callback) (tree decl, WMB_Flags, void *data),
+		     void *data)
+{
+  // FIXME: We don't quite deal with using decls naming stat hack
+  // type.
+  // Also using decls exporting something from the same scope
+  tree current = binding;
+  unsigned count = 0;
+
+  if (TREE_CODE (binding) == MODULE_VECTOR)
+    current = MODULE_VECTOR_CLUSTER (binding, 0).slots[MODULE_SLOT_CURRENT];
+
+  bool decl_hidden = false;
+  if (tree type = MAYBE_STAT_TYPE (current))
+    {
+      WMB_Flags flags = WMB_None;
+      if (STAT_TYPE_HIDDEN_P (current))
+	flags = WMB_Flags (flags | WMB_Hidden);
+      count += callback (type, flags, data);
+      decl_hidden = STAT_DECL_HIDDEN_P (current);
+    }
+
+  for (ovl_iterator iter (MAYBE_STAT_DECL (current)); iter; ++iter)
+    {
+      if (iter.hidden_p ())
+	decl_hidden = true;
+      if (!(decl_hidden && DECL_UNDECLARED_BUILTIN_P (*iter)))
+	{
+	  WMB_Flags flags = WMB_None;
+	  if (decl_hidden)
+	    flags = WMB_Flags (flags | WMB_Hidden);
+	  if (iter.using_p ())
+	    {
+	      flags = WMB_Flags (flags | WMB_Using);
+	      if (iter.exporting_p ())
+		flags = WMB_Flags (flags | WMB_Export);
+	    }
+	  count += callback (*iter, flags, data);
+	}
+      decl_hidden = false;
+    }
+
+  if (partitions && TREE_CODE (binding) == MODULE_VECTOR)
+    {
+      /* Process partition slots.  */
+      module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (binding);
+      unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (binding);
+      if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+	{
+	  ix--;
+	  cluster++;
+	}
+
+      bool maybe_dups = MODULE_VECTOR_PARTITION_DUPS_P (binding);
+
+      for (; ix--; cluster++)
+	for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	  if (!cluster->slots[jx].is_lazy ())
+	    if (tree bind = cluster->slots[jx])
+	      {
+		if (TREE_CODE (bind) == NAMESPACE_DECL
+		    && !DECL_NAMESPACE_ALIAS (bind))
+		  {
+		    if (unsigned base = cluster->indices[jx].base)
+		      if (unsigned span = cluster->indices[jx].span)
+			do
+			  if (bitmap_bit_p (partitions, base))
+			    goto found;
+			while (++base, --span);
+		    /* Not a partition's namespace.  */
+		    continue;
+		  found:
+
+		    WMB_Flags flags = WMB_None;
+		    if (maybe_dups)
+		      flags = WMB_Flags (flags | WMB_Dups);
+		    count += callback (bind, flags, data);
+		  }
+		else if (STAT_HACK_P (bind) && MODULE_BINDING_PARTITION_P (bind))
+		  {
+		    if (tree btype = STAT_TYPE (bind))
+		      {
+			WMB_Flags flags = WMB_None;
+			if (maybe_dups)
+			  flags = WMB_Flags (flags | WMB_Dups);
+			if (STAT_TYPE_HIDDEN_P (bind))
+			  flags = WMB_Flags (flags | WMB_Hidden);
+
+			count += callback (btype, flags, data);
+		      }
+		    bool hidden = STAT_DECL_HIDDEN_P (bind);
+		    for (ovl_iterator iter (MAYBE_STAT_DECL (STAT_DECL (bind)));
+			 iter; ++iter)
+		      {
+			if (iter.hidden_p ())
+			  hidden = true;
+			gcc_checking_assert
+			  (!(hidden && DECL_UNDECLARED_BUILTIN_P (*iter)));
+
+			WMB_Flags flags = WMB_None;
+			if (maybe_dups)
+			  flags = WMB_Flags (flags | WMB_Dups);
+			if (decl_hidden)
+			  flags = WMB_Flags (flags | WMB_Hidden);
+			if (iter.using_p ())
+			  {
+			    flags = WMB_Flags (flags | WMB_Using);
+			    if (iter.exporting_p ())
+			      flags = WMB_Flags (flags | WMB_Export);
+			  }
+			count += callback (*iter, flags, data);
+			hidden = false;
+		      }
+		  }
+	      }
+    }
+
+  return count;
+}
+
+/* Imported module MOD has a binding to NS::NAME, stored in section
+   SNUM.  */
+
+bool
+import_module_binding  (tree ns, tree name, unsigned mod, unsigned snum)
+{
+  tree *slot = find_namespace_slot (ns, name, true);
+  mc_slot *mslot = append_imported_binding_slot (slot, name, mod);
+
+  if (mslot->is_lazy () || *mslot)
+    /* Oops, something was already there.  */
+    return false;
+
+  mslot->set_lazy (snum);
+  return true;
+}
+
+/* An import of MODULE is binding NS::NAME.  There should be no
+   existing binding for >= MODULE.  MOD_GLOB indicates whether MODULE
+   is a header_unit (-1) or part of the current module (+1).  VALUE
+   and TYPE are the value and type bindings. VISIBLE are the value
+   bindings being exported.  */
+
+bool
+set_module_binding (tree ns, tree name, unsigned mod, int mod_glob,
+		    tree value, tree type, tree visible)
+{
+  if (!value)
+    /* Bogus BMIs could give rise to nothing to bind.  */
+    return false;
 
-      if (DECL_DECLARES_FUNCTION_P (decl))
-	{
-	  check_default_args (decl);
+  gcc_assert (TREE_CODE (value) != NAMESPACE_DECL
+	      || DECL_NAMESPACE_ALIAS (value));
+  gcc_checking_assert (mod);
 
-	  if (hiding)
-	    {
-	      if (level->kind != sk_namespace)
-		{
-		  /* In a local class, a friend function declaration must
-		     find a matching decl in the innermost non-class scope.
-		     [class.friend/11] */
-		  error_at (DECL_SOURCE_LOCATION (decl),
-			    "friend declaration %qD in local class without "
-			    "prior local declaration", decl);
-		  /* Don't attempt to push it.  */
-		  return error_mark_node;
-		}
-	    }
-	}
+  tree *slot = find_namespace_slot (ns, name, true);
+  mc_slot *mslot = search_imported_binding_slot (slot, mod);
 
-      if (level->kind != sk_namespace)
-	{
-	  check_local_shadow (decl);
+  if (!mslot || !mslot->is_lazy ())
+    /* Again, bogus BMI could give find to missing or already loaded slot.  */
+    return false;
 
-	  if (TREE_CODE (decl) == NAMESPACE_DECL)
-	    /* A local namespace alias.  */
-	    set_identifier_type_value_with_scope (name, NULL_TREE, level);
+  tree bind = value;
+  if (type || visible != bind || mod_glob)
+    {
+      bind = stat_hack (bind, type);
+      STAT_VISIBLE (bind) = visible;
+      if ((mod_glob > 0 && TREE_PUBLIC (ns))
+	  || (type && DECL_MODULE_EXPORT_P (type)))
+	STAT_TYPE_VISIBLE_P (bind) = true;
+    }
 
-	  if (!binding)
-	    binding = create_local_binding (level, name);
-	}
-      else if (!slot)
-	{
-	  ns = current_namespace;
-	  slot = find_namespace_slot (ns, name, true);
-	  /* Update OLD to reflect the namespace we're going to be
-	     pushing into.  */
-	  old = MAYBE_STAT_DECL (*slot);
-	}
+  /* Note if this is this-module or global binding.  */
+  if (mod_glob > 0)
+    MODULE_BINDING_PARTITION_P (bind) = true;
+  else if (mod_glob < 0)
+    MODULE_BINDING_GLOBAL_P (bind) = true;
 
-      old = update_binding (level, binding, slot, old, decl, hiding);
+  *mslot = bind;
 
-      if (old != decl)
-	/* An existing decl matched, use it.  */
-	decl = old;
-      else if (TREE_CODE (decl) == TYPE_DECL)
-	{
-	  tree type = TREE_TYPE (decl);
+  return true;
+}
 
-	  if (type != error_mark_node)
-	    {
-	      if (TYPE_NAME (type) != decl)
-		set_underlying_type (decl);
+void
+note_pending_specializations (tree ns, tree name, bool is_header)
+{
+  if (tree *slot = find_namespace_slot (ns, name, false))
+    if (TREE_CODE (*slot) == MODULE_VECTOR)
+      {
+	tree vec = *slot;
+	MODULE_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = true;
+	if (is_header)
+	  MODULE_VECTOR_PENDING_IS_HEADER_P (vec) = true;
+	else
+	  MODULE_VECTOR_PENDING_IS_PARTITION_P (vec) = true;
+      }
+}
 
-	      set_identifier_type_value_with_scope (name, decl, level);
-	    }
+void
+load_pending_specializations (tree ns, tree name)
+{
+  tree *slot = find_namespace_slot (ns, name, false);
 
-	  /* If this is a locally defined typedef in a function that
-	     is not a template instantation, record it to implement
-	     -Wunused-local-typedefs.  */
-	  if (!instantiating_current_function_p ())
-	    record_locally_defined_typedef (decl);
-	}
-      else
-	{
-	  if (VAR_P (decl) && !DECL_LOCAL_DECL_P (decl))
-	    maybe_register_incomplete_var (decl);
+  if (!slot || TREE_CODE (*slot) != MODULE_VECTOR
+      || !MODULE_VECTOR_PENDING_SPECIALIZATIONS_P (*slot))
+    return;
 
-	  if (VAR_OR_FUNCTION_DECL_P (decl))
-	    {
-	      if (DECL_LOCAL_DECL_P (decl)
-		  && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
-		push_local_extern_decl_alias (decl);
+  tree vec = *slot;
+  MODULE_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = false;
 
-	      if (DECL_EXTERN_C_P (decl))
-		check_extern_c_conflict (decl);
-	    }
-	}
+  bool do_header = MODULE_VECTOR_PENDING_IS_HEADER_P (vec);
+  bool do_partition = MODULE_VECTOR_PENDING_IS_PARTITION_P (vec);
+  MODULE_VECTOR_PENDING_IS_HEADER_P (vec) = false;
+  MODULE_VECTOR_PENDING_IS_PARTITION_P (vec) = false;
+
+  gcc_checking_assert (do_header | do_partition);
+  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (vec);
+  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (vec);
+  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+    {
+      ix--;
+      cluster++;
     }
-  else
-    add_decl_to_level (level, decl);
 
-  return decl;
+  for (; ix--; cluster++)
+    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+      if (cluster->indices[jx].span
+	  && cluster->slots[jx].is_lazy ()
+	  && lazy_specializations_p (cluster->indices[jx].base,
+				     do_header, do_partition))
+	lazy_load_binding (cluster->indices[jx].base, ns, name,
+			   &cluster->slots[jx]);
 }
 
-/* Record a decl-node X as belonging to the current lexical scope.
-   It's a friend if IS_FRIEND is true -- which affects exactly where
-   we push it.  */
-
-tree
-pushdecl (tree x, bool hiding)
+void
+add_module_decl (tree ns, tree name, tree decl)
 {
-  bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
-  tree ret = do_pushdecl (x, hiding);
-  timevar_cond_stop (TV_NAME_LOOKUP, subtime);
-  return ret;
+  gcc_assert (!DECL_CHAIN (decl));
+  add_decl_to_level (NAMESPACE_LEVEL (ns), decl);
+  newbinding_bookkeeping (name, decl, NAMESPACE_LEVEL (ns));
 }
 
 /* Enter DECL into the symbol table, if that's appropriate.  Returns
@@ -3654,7 +4793,7 @@ set_identifier_type_value_with_scope (tree id, tree decl, cp_binding_level *b)
   else
     {
       gcc_assert (decl);
-      if (CHECKING_P)
+      if (false && CHECKING_P)
 	{
 	  tree *slot = find_namespace_slot (current_namespace, id);
 	  gcc_checking_assert (slot
@@ -3778,7 +4917,7 @@ pushdecl_outermost_localscope (tree x)
 
 static bool
 do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
-			 tree *value_p, tree *type_p)
+			 bool insert_p, tree *value_p, tree *type_p)
 {
   tree value = *value_p;
   tree type = *type_p;
@@ -3798,13 +4937,33 @@ do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
       lookup.value = NULL_TREE;
     }
 
+  /* Only process exporting if we're going to be inserting.  */
+  bool revealing_p = insert_p && !fn_scope_p && module_has_cmi_p ();
+
+  /* First do the value binding.  */
   if (!lookup.value)
-    /* Nothing.  */;
+    /* Nothing (only implicit typedef found).  */
+    gcc_checking_assert (lookup.type);
   else if (OVL_P (lookup.value) && (!value || OVL_P (value)))
     {
       for (lkp_iterator usings (lookup.value); usings; ++usings)
 	{
 	  tree new_fn = *usings;
+	  bool exporting = revealing_p && module_exporting_p ();
+	  if (exporting)
+	    {
+	      /* If the using decl is exported, the things it refers
+		 to must also be exported (or not in module purview).  */
+	      if (!DECL_MODULE_EXPORT_P (new_fn)
+		  && (DECL_LANG_SPECIFIC (new_fn)
+		      && DECL_MODULE_PURVIEW_P (new_fn)))
+		{
+		  error ("%q#D does not have external linkage", new_fn);
+		  inform (DECL_SOURCE_LOCATION (new_fn),
+			  "%q#D declared here", new_fn);
+		  exporting = false;
+		}
+	    }
 
 	  /* [namespace.udecl]
 
@@ -3812,6 +4971,10 @@ do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
 	     scope has the same name and the same parameter types as a
 	     function introduced by a using declaration the program is
 	     ill-formed.  */
+	  /* This seems overreaching, asking core -- why do we care
+	     about decls in the namespace that we cannot name (because
+	     they are not transitively imported.  We just check the
+	     decls that are in this TU.  */
 	  bool found = false;
 	  for (ovl_iterator old (value); !found && old; ++old)
 	    {
@@ -3820,8 +4983,25 @@ do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
 	      if (new_fn == old_fn)
 		{
 		  /* The function already exists in the current
-		     namespace.  */
+		     namespace.  We will still want to insert it if
+		     it is revealing a not-revealed thing.  */
 		  found = true;
+		  if (!revealing_p)
+		    ;
+		  else if (old.using_p ())
+		    {
+		      if (exporting)
+			/* Update in place.  'tis ok.  */
+			OVL_EXPORT_P (old.get_using ()) = true;
+		      ;
+		    }
+		  else if (DECL_MODULE_EXPORT_P (new_fn))
+		    ;
+		  else
+		    {
+		      value = old.remove_node (value);
+		      found = false;
+		    }
 		  break;
 		}
 	      else if (old.using_p ())
@@ -3845,11 +5025,11 @@ do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
 		}
 	    }
 
-	  if (!found)
+	  if (!found && insert_p)
 	    /* Unlike the decl-pushing case we don't drop anticipated
 	       builtins here.  They don't cause a problem, and we'd
 	       like to match them with a future declaration.  */
-	    value = ovl_insert (new_fn, value, true);
+	    value = ovl_insert (new_fn, value, 1 + exporting);
 	}
     }
   else if (value
@@ -3860,28 +5040,34 @@ do_nonmember_using_decl (name_lookup &lookup, bool fn_scope_p,
       diagnose_name_conflict (lookup.value, value);
       failed = true;
     }
-  else
+  else if (insert_p)
+    // FIXME:what if we're newly exporting lookup.value
     value = lookup.value;
-
+  
+  /* Now the type binding.  */
   if (lookup.type && lookup.type != type)
     {
+      // FIXME: What if we're exporting lookup.type?
       if (type && !decls_match (lookup.type, type))
 	{
 	  diagnose_name_conflict (lookup.type, type);
 	  failed = true;
 	}
-      else
+      else if (insert_p)
 	type = lookup.type;
     }
 
-  /* If value is empty, shift any class or enumeration name back.  */
-  if (!value)
+  if (insert_p)
     {
-      value = type;
-      type = NULL_TREE;
+      /* If value is empty, shift any class or enumeration name back.  */
+      if (!value)
+	{
+	  value = type;
+	  type = NULL_TREE;
+	}
+      *value_p = value;
+      *type_p = type;
     }
-  *value_p = value;
-  *type_p = type;
 
   return failed;
 }
@@ -4645,8 +5831,10 @@ do_class_using_decl (tree scope, tree name)
 }
 
 \f
-/* Return the binding for NAME in NS.  If NS is NULL, look in
-   global_namespace.  */
+/* Return the binding for NAME in NS in the current TU.  If NS is
+   NULL, look in global_namespace.  We will not find declarations
+   from imports.  Users of this who, having found nothing, push a new
+   decl must be prepared for that pushing to match an existing decl.  */
 
 tree
 get_namespace_binding (tree ns, tree name)
@@ -4655,7 +5843,18 @@ get_namespace_binding (tree ns, tree name)
   if (!ns)
     ns = global_namespace;
   gcc_checking_assert (!DECL_NAMESPACE_ALIAS (ns));
-  tree ret = find_namespace_value (ns, name);
+  tree ret = NULL_TREE;
+
+  if (tree *b = find_namespace_slot (ns, name))
+    {
+      ret = *b;
+
+      if (TREE_CODE (ret) == MODULE_VECTOR)
+	ret = MODULE_VECTOR_CLUSTER (ret, 0).slots[0];
+      if (ret)
+	ret = MAYBE_STAT_DECL (ret);
+    }
+
   timevar_cond_stop (TV_NAME_LOOKUP, subtime);
   return ret;
 }
@@ -4956,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space)
   DECL_NAMESPACE_ALIAS (alias) = name_space;
   DECL_EXTERNAL (alias) = 1;
   DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
+
+  set_originating_module (alias);
+
   pushdecl (alias);
 
   /* Emit debug info for namespace alias.  */
@@ -5033,26 +6235,100 @@ finish_nonmember_using_decl (tree scope, tree name)
   if (current_binding_level->kind == sk_namespace)
     {
       tree *slot = find_namespace_slot (current_namespace, name, true);
+      tree *mslot = get_fixed_binding_slot (slot, name,
+					    MODULE_SLOT_CURRENT, true);
+      bool failed = false;
+
+      if (mslot != slot)
+	{
+	  /* A module vector.  I presume the binding list is going to
+	     be sparser than the import bitmap.  Hence iterate over
+	     the former checking for bits set in the bitmap.  */
+	  bitmap imports = get_import_bitmap ();
+	  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (*slot);
+
+	  /* Scan the imported bindings.  */
+	  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (*slot);
+	  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+	    {
+	      ix--;
+	      cluster++;
+	    }
+
+	  /* Do this in forward order, so we load modules in an order
+	     the user expects.  */
+	  for (; ix--; cluster++)
+	    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	      {
+		/* Are we importing this module?  */
+		if (unsigned base = cluster->indices[jx].base)
+		  if (unsigned span = cluster->indices[jx].span)
+		    do
+		      if (bitmap_bit_p (imports, base))
+			goto found;
+		    while (++base, --span);
+		continue;
+
+	      found:;
+		/* Is it loaded?  */
+		if (cluster->slots[jx].is_lazy ())
+		  {
+		    gcc_assert (cluster->indices[jx].span == 1);
+		    lazy_load_binding (cluster->indices[jx].base,
+				       scope, name, &cluster->slots[jx]);
+		  }
+
+		tree value = cluster->slots[jx];
+		if (!value)
+		  /* Load errors could mean there's nothing here.  */
+		  continue;
+
+		/* Extract what we can see from here.  If there's no
+		   stat_hack, then everything was exported.  */
+		tree type = NULL_TREE;
 
-      tree value = MAYBE_STAT_DECL (*slot);
-      tree type = MAYBE_STAT_TYPE (*slot);
+		/* If no stat hack, everything is visible.  */
+		if (STAT_HACK_P (value))
+		  {
+		    if (STAT_TYPE_VISIBLE_P (value))
+		      type = STAT_TYPE (value);
+		    value = STAT_VISIBLE (value);
+		  }
 
-      do_nonmember_using_decl (lookup, false, &value, &type);
+		if (do_nonmember_using_decl (lookup, false, false,
+					     &value, &type))
+		  {
+		    failed = true;
+		    break;
+		  }
+	      }
+	}
 
-      if (STAT_HACK_P (*slot))
+      if (!failed)
 	{
-	  STAT_DECL (*slot) = value;
-	  STAT_TYPE (*slot) = type;
+	  /* Now do the current slot.  */
+	  tree value = MAYBE_STAT_DECL (*mslot);
+	  tree type = MAYBE_STAT_TYPE (*mslot);
+
+	  do_nonmember_using_decl (lookup, false, true, &value, &type);
+
+	  // FIXME: Partition mergeableness?
+	  if (STAT_HACK_P (*mslot))
+	    {
+	      STAT_DECL (*mslot) = value;
+	      STAT_TYPE (*mslot) = type;
+	    }
+	  else if (type)
+	    *mslot = stat_hack (value, type);
+	  else
+	    *mslot = value;
 	}
-      else if (type)
-	*slot = stat_hack (value, type);
-      else
-	*slot = value;
     }
   else
     {
       tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
       USING_DECL_SCOPE (using_decl) = scope;
+      DECL_CONTEXT (using_decl) = current_function_decl;
       add_decl_expr (using_decl);
 
       cxx_binding *binding = find_local_binding (current_binding_level, name);
@@ -5067,7 +6343,7 @@ finish_nonmember_using_decl (tree scope, tree name)
       /* DR 36 questions why using-decls at function scope may not be
 	 duplicates.  Disallow it, as C++11 claimed and PR 20420
 	 implemented.  */
-      do_nonmember_using_decl (lookup, true, &value, &type);
+      do_nonmember_using_decl (lookup, true, true, &value, &type);
 
       if (!value)
 	;
@@ -5719,7 +6995,7 @@ get_std_name_hint (const char *name)
 
 /* Describe DIALECT.  */
 
-static const char *
+const char *
 get_cxx_dialect_name (enum cxx_dialect dialect)
 {
   switch (dialect)
@@ -5885,7 +7161,14 @@ lookup_qualified_name (tree scope, tree name, LOOK_want want, bool complain)
       name_lookup lookup (name, want);
 
       if (qualified_namespace_lookup (scope, &lookup))
-	t = lookup.value;
+	{
+	  t = lookup.value;
+
+	  /* If we have a known type overload, pull it out.  This can happen
+	     for using decls.  */
+	  if (TREE_CODE (t) == OVERLOAD && TREE_TYPE (t) != unknown_type_node)
+	    t = OVL_FUNCTION (t);
+	}
     }
   else if (cxx_dialect != cxx98 && TREE_CODE (scope) == ENUMERAL_TYPE)
     t = lookup_enumerator (scope, name);
@@ -5983,9 +7266,10 @@ maybe_add_fuzzy_decl (auto_vec<tree> &vec, tree decl)
 }
 
 /* Examing the namespace binding BINDING, and add at most one instance
-   of the name, if it contains a visible entity of interest.  */
+   of the name, if it contains a visible entity of interest.  Return
+   true if we added something.  */
 
-void
+bool
 maybe_add_fuzzy_binding (auto_vec<tree> &vec, tree binding,
 			      lookup_name_fuzzy_kind kind)
 {
@@ -5997,7 +7281,7 @@ maybe_add_fuzzy_binding (auto_vec<tree> &vec, tree binding,
 	  && STAT_TYPE (binding))
 	{
 	  if (maybe_add_fuzzy_decl (vec, STAT_TYPE (binding)))
-	    return;
+	    return true;
 	}
       else if (!STAT_DECL_HIDDEN_P (binding))
 	value = STAT_DECL (binding);
@@ -6012,8 +7296,11 @@ maybe_add_fuzzy_binding (auto_vec<tree> &vec, tree binding,
       if (kind != FUZZY_LOOKUP_TYPENAME
 	  || TREE_CODE (STRIP_TEMPLATE (value)) == TYPE_DECL)
 	if (maybe_add_fuzzy_decl (vec, value))
-	  return;
+	  return true;
     }
+
+  /* Nothing found.  */
+  return false;
 }
 
 /* Helper function for lookup_name_fuzzy.
@@ -6079,8 +7366,54 @@ consider_binding_level (tree name, best_match <tree, const char *> &bm,
 	(DECL_NAMESPACE_BINDINGS (ns)->end ());
       for (hash_table<named_decl_hash>::iterator iter
 	     (DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter)
-	maybe_add_fuzzy_binding (vec, *iter, kind);
+	{
+	  tree binding = *iter;
+
+	  if (TREE_CODE (binding) == MODULE_VECTOR)
+	    {
+	      bitmap imports = get_import_bitmap ();
+	      module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (binding);
+
+	      if (tree bind = cluster->slots[MODULE_SLOT_CURRENT])
+		if (maybe_add_fuzzy_binding (vec, bind, kind))
+		  continue;
 
+	      /* Scan the imported bindings.  */
+	      unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (binding);
+	      if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+		{
+		  ix--;
+		  cluster++;
+		}
+
+	      for (; ix--; cluster++)
+		for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER;
+		     jx++)
+		  {
+		    /* Are we importing this module?  */
+		    if (unsigned base = cluster->indices[jx].base)
+		      if (unsigned span = cluster->indices[jx].span)
+			do
+			  if (bitmap_bit_p (imports, base))
+			    goto found;
+			while (++base, --span);
+		    continue;
+
+		  found:;
+		    /* Is it loaded?  */
+		    if (cluster->slots[jx].is_lazy ())
+		      /* Let's not read in everything on the first
+			 spello! **/
+		      continue;
+		    if (tree bind = cluster->slots[jx])
+		      if (maybe_add_fuzzy_binding (vec, bind, kind))
+			break;
+		  }
+	    }
+	  else
+	    maybe_add_fuzzy_binding (vec, binding, kind);
+	}
+	
       vec.qsort ([] (const void *a_, const void *b_)
 		 {
 		   return strcmp (IDENTIFIER_POINTER (*(const tree *)a_),
@@ -6515,9 +7848,10 @@ lookup_name_1 (tree name, LOOK_where where, LOOK_want want)
 
  found:;
 
-  /* If we have a single function from a using decl, pull it out.  */
-  if (val && TREE_CODE (val) == OVERLOAD && !really_overloaded_fn (val))
-    val = OVL_FUNCTION (val);
+  /* If we have a known type overload, pull it out.  This can happen
+     for both using decls and unhidden functions.  */
+  if (val && TREE_CODE (val) == OVERLOAD && TREE_TYPE (val) != unknown_type_node)
+    val = OVL_FIRST (val);
 
   return val;
 }
@@ -6622,31 +7956,115 @@ lookup_elaborated_type_1 (tree name, TAG_how how)
   tree ns = b->this_entity;
   if (tree *slot = find_namespace_slot (ns, name))
     {
-      /* If this is the kind of thing we're looking for, we're done.  */
-      if (tree type = MAYBE_STAT_TYPE (*slot))
-	{
-	  if (how != TAG_how::HIDDEN_FRIEND)
-	    /* No longer hidden.  */
-	    STAT_TYPE_HIDDEN_P (*slot) = false;
+      tree bind = *slot;
+      if (TREE_CODE (bind) == MODULE_VECTOR)
+	bind = MODULE_VECTOR_CLUSTER (bind, 0).slots[MODULE_SLOT_CURRENT];
 
-	  return type;
-	}
-      else if (tree decl = MAYBE_STAT_DECL (*slot))
+      if (bind)
 	{
-	  if (qualify_lookup (decl, LOOK_want::TYPE))
+	  /* If this is the kind of thing we're looking for, we're done.  */
+	  if (tree type = MAYBE_STAT_TYPE (bind))
+	    {
+	      if (how != TAG_how::HIDDEN_FRIEND)
+		/* No longer hidden.  */
+		STAT_TYPE_HIDDEN_P (*slot) = false;
+	      
+	      return type;
+	    }
+	  else if (tree decl = MAYBE_STAT_DECL (bind))
 	    {
-	      if (how != TAG_how::HIDDEN_FRIEND && STAT_HACK_P (*slot)
-		  && STAT_DECL_HIDDEN_P (*slot))
+	      if (qualify_lookup (decl, LOOK_want::TYPE))
 		{
-		  if (STAT_TYPE (*slot))
-		    STAT_DECL_HIDDEN_P (*slot) = false;
-		  else
-		    /* There is no type, just remove the stat
-		       hack.  */
-		    *slot = decl;
+		  if (how != TAG_how::HIDDEN_FRIEND && STAT_HACK_P (bind)
+		      && STAT_DECL_HIDDEN_P (bind))
+		    {
+		      if (STAT_TYPE (bind))
+			STAT_DECL_HIDDEN_P (bind) = false;
+		      else
+			{
+			  /* There is no type, just remove the stat
+			     hack.  */
+			  if (*slot == bind)
+			    *slot = decl;
+			  else
+			    MODULE_VECTOR_CLUSTER (bind, 0)
+			      .slots[MODULE_SLOT_CURRENT] = decl;
+			}
+		    }
+		  return decl;
 		}
+	    }
+	}
+
+      if (TREE_CODE (*slot) == MODULE_VECTOR)
+	{
+	  /* We could be redeclaring a global module entity, (from GMF
+   	     or header unit), or from another partition, or
+   	     specializing an imported template.  */
+	  bitmap imports = get_import_bitmap ();
+	  module_cluster *cluster = MODULE_VECTOR_CLUSTER_BASE (*slot);
+
+	  /* Scan the imported bindings.  */
+	  unsigned ix = MODULE_VECTOR_NUM_CLUSTERS (*slot);
+	  if (MODULE_VECTOR_SLOTS_PER_CLUSTER == MODULE_SLOTS_FIXED)
+	    {
+	      ix--;
+	      cluster++;
+	    }
+
+	  /* Do this in forward order, so we load modules in an order
+	     the user expects.  */
+	  for (; ix--; cluster++)
+	    for (unsigned jx = 0; jx != MODULE_VECTOR_SLOTS_PER_CLUSTER; jx++)
+	      {
+		/* Are we importing this module?  */
+		if (unsigned base = cluster->indices[jx].base)
+		  if (unsigned span = cluster->indices[jx].span)
+		    do
+		      if (bitmap_bit_p (imports, base))
+			goto found;
+		    while (++base, --span);
+		continue;
+
+	      found:;
+		/* Is it loaded?  */
+		if (cluster->slots[jx].is_lazy ())
+		  {
+		    gcc_assert (cluster->indices[jx].span == 1);
+		    lazy_load_binding (cluster->indices[jx].base,
+				       ns, name, &cluster->slots[jx]);
+		  }
+		tree bind = cluster->slots[jx];
+		if (!bind)
+		  /* Load errors could mean there's nothing here.  */
+		  continue;
+
+		/* Extract what we can see from here.  If there's no
+		   stat_hack, then everything was exported.  */
+		tree type = NULL_TREE;
+
+		/* If no stat hack, everything is visible.  */
+		if (STAT_HACK_P (bind))
+		  {
+		    if (STAT_TYPE_VISIBLE_P (bind))
+		      type = STAT_TYPE (bind);
+		    bind = STAT_VISIBLE (bind);
+		  }
+
+		if (type && qualify_lookup (type, LOOK_want::TYPE))
+		  return type;
+
+		if (bind && qualify_lookup (bind, LOOK_want::TYPE))
+		  return bind;
+	      }
 
-	      return decl;
+	  if (!module_purview_p ())
+	    {
+	      /* We're in the global module, perhaps there's a tag
+		 there?  */
+	      // FIXME: This isn't quite right, if we find something
+	      // here, from the language PoV we're not supposed to
+	      // know it?
 	    }
 	}
     }
@@ -6778,7 +8196,6 @@ do_pushtag (tree name, tree type, TAG_how how)
   if (identifier_type_value_1 (name) != type)
     {
       tree tdef;
-      int in_class = 0;
       tree context = TYPE_CONTEXT (type);
 
       if (! context)
@@ -6809,13 +8226,10 @@ do_pushtag (tree name, tree type, TAG_how how)
       if (!context)
 	context = current_namespace;
 
-      if (b->kind == sk_class
-	  || (b->kind == sk_template_parms
-	      && b->level_chain->kind == sk_class))
-	in_class = 1;
-
       tdef = create_implicit_typedef (name, type);
       DECL_CONTEXT (tdef) = FROB_CONTEXT (context);
+      set_originating_module (tdef);
+
       decl = maybe_process_template_type_declaration
 	(type, how == TAG_how::HIDDEN_FRIEND, b);
       if (decl == error_mark_node)
@@ -6853,9 +8267,6 @@ do_pushtag (tree name, tree type, TAG_how how)
 	    }
 	}
 
-      if (! in_class)
-	set_identifier_type_value_with_scope (name, tdef, b);
-
       TYPE_CONTEXT (type) = DECL_CONTEXT (decl);
 
       /* If this is a local class, keep track of it.  We need this
@@ -7140,8 +8551,6 @@ do_push_nested_namespace (tree ns)
   else
     {
       do_push_nested_namespace (CP_DECL_CONTEXT (ns));
-      gcc_checking_assert
-	(find_namespace_value (current_namespace, DECL_NAME (ns)) == ns);
       resume_scope (NAMESPACE_LEVEL (ns));
       current_namespace = ns;
     }
@@ -7163,10 +8572,10 @@ do_pop_nested_namespace (tree ns)
   do_pop_from_top_level ();
 }
 
-/* Add TARGET to USINGS, if it does not already exist there.
-   We used to build the complete graph of usings at this point, from
-   the POV of the source namespaces.  Now we build that as we perform
-   the unqualified search.  */
+/* Add TARGET to USINGS, if it does not already exist there.  We used
+   to build the complete graph of usings at this point, from the POV
+   of the source namespaces.  Now we build that as we perform the
+   unqualified search.  */
 
 static void
 add_using_namespace (vec<tree, va_gc> *&usings, tree target)
@@ -7273,6 +8682,85 @@ push_inline_namespaces (tree ns)
   return count;
 }
 
+/* SLOT is the (possibly empty) binding slot for NAME in CTX.
+   Reuse or create a namespace NAME.  NAME is null for the anonymous
+   namespace.  */
+
+static tree
+reuse_namespace (tree *slot, tree ctx, tree name)
+{
+  if (modules_p () && *slot && TREE_PUBLIC (ctx) && name)
+    {
+      /* Public namespace.  Shared.  */
+      tree *global_slot = slot;
+      if (TREE_CODE (*slot) == MODULE_VECTOR)
+	global_slot = get_fixed_binding_slot (slot, name,
+					      MODULE_SLOT_GLOBAL, false);
+
+      for (ovl_iterator iter (*global_slot); iter; ++iter)
+	{
+	  tree decl = *iter;
+
+	  if (TREE_CODE (decl) == NAMESPACE_DECL && !DECL_NAMESPACE_ALIAS (decl))
+	    return decl;
+	}
+    }
+  return NULL_TREE;
+}
+
+static tree
+make_namespace (tree ctx, tree name, location_t loc, bool inline_p)
+{
+  /* Create the namespace.  */
+  tree ns = build_lang_decl (NAMESPACE_DECL, name, void_type_node);
+  DECL_SOURCE_LOCATION (ns) = loc;
+  SCOPE_DEPTH (ns) = SCOPE_DEPTH (ctx) + 1;
+  if (!SCOPE_DEPTH (ns))
+    /* We only allow depth 255. */
+    sorry ("cannot nest more than %d namespaces", SCOPE_DEPTH (ctx));
+  DECL_CONTEXT (ns) = FROB_CONTEXT (ctx);
+
+  if (!name)
+    /* It's possible we'll need to give anon-namespaces in different
+       header-unit imports distinct names.  If so, I think those
+       names can be unique to this TU -- use the module index?  */
+    SET_DECL_ASSEMBLER_NAME (ns, anon_identifier);
+  else if (TREE_PUBLIC (ctx))
+    TREE_PUBLIC (ns) = true;
+
+  if (inline_p)
+    DECL_NAMESPACE_INLINE_P (ns) = true;
+
+  return ns;
+}
+
+static void
+make_namespace_finish (tree ns, tree *slot, bool from_import = false)
+{
+  if (modules_p () && TREE_PUBLIC (ns) && (from_import || *slot != ns))
+    {
+      /* Merge into global slot.  */
+      tree *gslot = get_fixed_binding_slot (slot, DECL_NAME (ns),
+					    MODULE_SLOT_GLOBAL, true);
+      *gslot = ns;
+    }
+
+  /* NS was newly created, finish off making it.  */
+  tree ctx = CP_DECL_CONTEXT (ns);
+  cp_binding_level *scope = ggc_cleared_alloc<cp_binding_level> ();
+  scope->this_entity = ns;
+  scope->more_cleanups_ok = true;
+  scope->kind = sk_namespace;
+  scope->level_chain = NAMESPACE_LEVEL (ctx);
+  NAMESPACE_LEVEL (ns) = scope;
+
+  if (DECL_NAMESPACE_INLINE_P (ns))
+    vec_safe_push (DECL_NAMESPACE_INLINEES (ctx), ns);
+
+  if (DECL_NAMESPACE_INLINE_P (ns) || !DECL_NAME (ns))
+    emit_debug_info_using_namespace (ctx, ns, true);
+}
+
 /* Push into the scope of the NAME namespace.  If NAME is NULL_TREE,
    then we enter an anonymous namespace.  If MAKE_INLINE is true, then
    we create an inline namespace (it is up to the caller to check upon
@@ -7349,59 +8837,80 @@ push_namespace (tree name, bool make_inline)
 	}
   }
 
-  bool new_ns = false;
   if (ns)
-    /* DR2061.  NS might be a member of an inline namespace.  We
-       need to push into those namespaces.  */
-    count += push_inline_namespaces (CP_DECL_CONTEXT (ns));
+    {
+      /* DR2061.  NS might be a member of an inline namespace.  We
+	 need to push into those namespaces.  */
+      if (modules_p ())
+	{
+	  for (tree parent, ctx = ns; ctx != current_namespace;
+	       ctx = parent)
+	    {
+	      parent = CP_DECL_CONTEXT (ctx);
+
+	      tree bind = *find_namespace_slot (parent, DECL_NAME (ctx), false);
+	      if (bind != ctx)
+		{
+		  mc_slot &slot
+		    = MODULE_VECTOR_CLUSTER (bind, 0).slots[MODULE_SLOT_CURRENT];
+		  gcc_checking_assert (!(tree)slot || (tree)slot == ctx);
+		  slot = ctx;
+		}
+	    }
+	}
+
+      count += push_inline_namespaces (CP_DECL_CONTEXT (ns));
+      if (DECL_SOURCE_LOCATION (ns) == BUILTINS_LOCATION)
+	/* It's not builtin now.  */
+	DECL_SOURCE_LOCATION (ns) = input_location;
+    }
   else
     {
-      ns = build_lang_decl (NAMESPACE_DECL, name, void_type_node);
-      SCOPE_DEPTH (ns) = SCOPE_DEPTH (current_namespace) + 1;
-      if (!SCOPE_DEPTH (ns))
-	/* We only allow depth 255. */
-	sorry ("cannot nest more than %d namespaces",
-	       SCOPE_DEPTH (current_namespace));
-      DECL_CONTEXT (ns) = FROB_CONTEXT (current_namespace);
-      new_ns = true;
+      /* Before making a new namespace, see if we already have one in
+	 the existing partitions of the current namespace.  */
+      tree *slot = find_namespace_slot (current_namespace, name, false);
+      if (slot)
+	ns = reuse_namespace (slot, current_namespace, name);
+      if (!ns)
+	ns = make_namespace (current_namespace, name,
+			     input_location, make_inline);
 
       if (pushdecl (ns) == error_mark_node)
 	ns = NULL_TREE;
       else
 	{
-	  if (!name)
-	    {
-	      SET_DECL_ASSEMBLER_NAME (ns, anon_identifier);
-
-	      if (!make_inline)
-		add_using_namespace (current_binding_level->using_directives,
-				     ns);
-	    }
-	  else if (TREE_PUBLIC (current_namespace))
-	    TREE_PUBLIC (ns) = 1;
-
-	  if (make_inline)
+	  /* finish up making the namespace.  */
+	  add_decl_to_level (NAMESPACE_LEVEL (current_namespace), ns);
+	  if (!slot)
 	    {
-	      DECL_NAMESPACE_INLINE_P (ns) = true;
-	      vec_safe_push (DECL_NAMESPACE_INLINEES (current_namespace), ns);
+	      slot = find_namespace_slot (current_namespace, name);
+	      /* This should find the slot created by pushdecl.  */
+	      gcc_checking_assert (slot && *slot == ns);
 	    }
+	  make_namespace_finish (ns, slot);
 
-	  if (!name || make_inline)
-	    emit_debug_info_using_namespace (current_namespace, ns, true);
+	  /* Add the anon using-directive here, we don't do it in
+	     make_namespace_finish.  */
+	  if (!DECL_NAMESPACE_INLINE_P (ns) && !name)
+	    add_using_namespace (current_binding_level->using_directives, ns);
 	}
     }
 
   if (ns)
     {
+      /* A public namespace is exported only if explicitly marked, or
+	 it contains exported entities.  */
+      if (!DECL_MODULE_EXPORT_P (ns) && TREE_PUBLIC (ns)
+	  && module_exporting_p ())
+	implicitly_export_namespace (ns);
+
       if (make_inline && !DECL_NAMESPACE_INLINE_P (ns))
 	{
-	  error ("inline namespace must be specified at initial definition");
+	  error_at (input_location,
+		    "inline namespace must be specified at initial definition");
 	  inform (DECL_SOURCE_LOCATION (ns), "%qD defined here", ns);
 	}
-      if (new_ns)
-	begin_scope (sk_namespace, ns);
-      else
-	resume_scope (NAMESPACE_LEVEL (ns));
+      resume_scope (NAMESPACE_LEVEL (ns));
       current_namespace = ns;
       count++;
     }
@@ -7425,6 +8934,66 @@ pop_namespace (void)
   timevar_cond_stop (TV_NAME_LOOKUP, subtime);
 }
 
+// FIXME: Something is not correct about the VISIBLE_P handling.  We
+// need to insert this namespace into
+// (a) the GLOBAL or PARTITION slot, if it is TREE_PUBLIC
+// (b) The importing module's slot (always)
+// (c) Do we need to put it in the CURRENT slot?  This is the
+// confused piece.
+
+tree
+add_imported_namespace (tree ctx, tree name, unsigned origin, location_t loc,
+			bool visible_p, bool inline_p)
+{
+  gcc_checking_assert (origin);
+  tree *slot = find_namespace_slot (ctx, name, true);
+  tree decl = reuse_namespace (slot, ctx, name);
+  if (!decl)
+    {
+      decl = make_namespace (ctx, name, loc, inline_p);
+      DECL_MODULE_IMPORT_P (decl) = true;
+      make_namespace_finish (decl, slot, true);
+    }
+  else if (DECL_NAMESPACE_INLINE_P (decl) != inline_p)
+    {
+      error_at (loc, "%s namespace %qD conflicts with reachable definition",
+		inline_p ? "inline" : "non-inline", decl);
+      inform (DECL_SOURCE_LOCATION (decl), "reachable %s definition here",
+	      inline_p ? "non-inline" : "inline");
+    }
+
+  if (TREE_PUBLIC (decl) && TREE_CODE (*slot) == MODULE_VECTOR)
+    {
+      /* See if we can extend the final slot.  */
+      module_cluster *last = MODULE_VECTOR_CLUSTER_LAST (*slot);
+      gcc_checking_assert (last->indices[0].span);
+      unsigned jx = MODULE_VECTOR_SLOTS_PER_CLUSTER;
+
+      while (--jx)
+	if (last->indices[jx].span)
+	  break;
+      tree final = last->slots[jx];
+      if (visible_p == !STAT_HACK_P (final)
+	  && MAYBE_STAT_DECL (final) == decl
+	  && last->indices[jx].base + last->indices[jx].span == origin
+	  && (MODULE_VECTOR_NUM_CLUSTERS (*slot) > 1
+	      || (MODULE_VECTOR_SLOTS_PER_CLUSTER > MODULE_SLOTS_FIXED
+		  && jx >= MODULE_SLOTS_FIXED)))
+	{
+	  last->indices[jx].span++;
+	  return decl;
+	}
+    }
+
+  /* Append a new slot.  */
+  tree *mslot = &(tree &)*append_imported_binding_slot (slot, name, origin);
+
+  gcc_assert (!*mslot);
+  *mslot = visible_p ? decl : stat_hack (decl, NULL_TREE);
+
+  return decl;
+}
+
 /* External entry points for do_{push_to/pop_from}_top_level.  */
 
 void
@@ -7580,8 +9149,8 @@ maybe_save_operator_binding (tree e)
 
   /* Do this for lambdas and code that will emit a CMI.  In a module's
      GMF we don't yet know whether there will be a CMI.  */
-  if (!current_lambda_expr ())
-    return;
+  if (!module_has_cmi_p () && !global_purview_p () && !current_lambda_expr())
+     return;
 
   tree fnname = ovl_op_identifier (false, TREE_CODE (e));
   if (!fnname)
diff --git c/gcc/cp/name-lookup.h w/gcc/cp/name-lookup.h
index 6d18539e730..db228bdcfdc 100644
--- c/gcc/cp/name-lookup.h
+++ w/gcc/cp/name-lookup.h
@@ -68,6 +68,46 @@ struct GTY(()) cxx_saved_binding {
   tree real_type_value;
 };
 
+/* To support lazy module loading, we squirrel away a section number
+   for unloaded bindings.  We rely on pointers being aligned and
+   setting the bottom bit to mark a lazy value.
+   GTY doesn't like an array of union, so hve a containing struct.  */
+
+struct GTY(()) mc_slot {
+  union GTY((desc ("%1.is_lazy ()"))) mc_slot_lazy {
+    tree GTY((tag ("false"))) binding;
+  } u;
+
+  operator tree & ()
+  {
+    gcc_checking_assert (!is_lazy ());
+    return u.binding;
+  }
+  mc_slot &operator= (tree t)
+  {
+    u.binding = t;
+    return *this;
+  }
+  bool is_lazy () const
+  {
+    return bool (uintptr_t (u.binding) & 1);
+  }
+  void set_lazy (unsigned snum)
+  {
+    gcc_checking_assert (!u.binding);
+    u.binding = tree (uintptr_t ((snum << 1) | 1));
+  }
+  void or_lazy (unsigned snum)
+  {
+    gcc_checking_assert (is_lazy ());
+    u.binding = tree (uintptr_t (u.binding) | (snum << 1));
+  }
+  unsigned get_lazy () const
+  {
+    gcc_checking_assert (is_lazy ());
+    return unsigned (uintptr_t (u.binding) >> 1);
+  }
+};
 
 extern tree identifier_type_value (tree);
 extern void set_identifier_type_value (tree, tree);
@@ -338,7 +378,7 @@ extern tree *find_member_slot (tree klass, tree name);
 extern tree *add_member_slot (tree klass, tree name);
 extern void resort_type_member_vec (void *, void *,
 				    gt_pointer_operator, void *);
-extern void set_class_bindings (tree, unsigned extra = 0);
+extern vec<tree, va_gc> *set_class_bindings (tree, int extra = 0);
 extern void insert_late_enum_def_bindings (tree, tree);
 extern tree innermost_non_namespace_value (tree);
 extern cxx_binding *outer_binding (tree, cxx_binding *, bool);
@@ -361,4 +401,33 @@ extern void maybe_save_operator_binding (tree);
 extern void push_operator_bindings (void);
 extern void discard_operator_bindings (tree);
 
+/* Lower level interface for modules. */
+extern tree *mergeable_namespace_slots (tree ns, tree name, bool is_global,
+					tree *mvec);
+extern void add_mergeable_namespace_entity (tree *slot, tree decl);
+extern tree lookup_class_binding (tree ctx, tree name);
+extern bool import_module_binding (tree ctx, tree name, unsigned mod,
+				   unsigned snum);
+extern bool set_module_binding (tree ctx, tree name, unsigned mod,
+				int mod_glob_flag,
+				tree value, tree type, tree visible);
+extern void add_module_decl (tree ctx, tree name, tree decl);
+
+enum WMB_Flags
+{
+  WMB_None = 0,
+  WMB_Dups = 1 << 0,
+  WMB_Export = 1 << 1,
+  WMB_Using = 1 << 2,
+  WMB_Hidden = 1 << 3,
+};
+
+extern unsigned walk_module_binding (tree binding, bitmap partitions,
+				     bool (*)(tree decl, WMB_Flags, void *data),
+				     void *data);
+extern tree add_imported_namespace (tree ctx, tree name, unsigned module,
+				    location_t, bool visible_p, bool inline_p);
+extern void note_pending_specializations (tree ns, tree name, bool is_header);
+extern void load_pending_specializations (tree ns, tree name);
+extern const char *get_cxx_dialect_name (enum cxx_dialect dialect);
 #endif /* GCC_CP_NAME_LOOKUP_H */


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

* [27/32] parser
       [not found]                                                 ` <df8d76cf-b00f-6b4c-3b8c-132843c15d62@acm.org>
@ 2020-11-03 21:17                                                   ` Nathan Sidwell
       [not found]                                                   ` <e67492d5-3be5-0993-538d-b798875c5e89@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

the parser changes are two-fold

a) call out to the token inspector added to lex, so that module control 
lines are processed during tokenization

b) adding parser support for module and import declarations.

As mentioned in the introduction, I do not handle the case of textually 
parsing a definition when an existing definition is already know via 
(header-unit) import.  You'll get a multiple-definition error.

nathan

-- 
Nathan Sidwell



[-- Attachment #2: 27-c++-parser.diff --]
[-- Type: text/x-patch, Size: 18680 bytes --]

diff --git c/gcc/cp/parser.c w/gcc/cp/parser.c
index dd8c4b56bd0..98ca9aae082 100644
--- c/gcc/cp/parser.c
+++ w/gcc/cp/parser.c
@@ -646,9 +646,17 @@ cp_lexer_new_main (void)
   /* Put the first token in the buffer.  */
   cp_token *tok = lexer->buffer->quick_push (token);
 
+  uintptr_t coro = 0;
+  if (modules_p ())
+    coro = module_token_cdtor (parse_in, coro);
+
   /* Get the remaining tokens from the preprocessor.  */
   while (tok->type != CPP_EOF)
     {
+      if (coro)
+	/* Process the previous token.  */
+	module_token_lang (tok->type, tok->keyword, tok->u.value,
+			   tok->location, coro);
       tok = vec_safe_push (lexer->buffer, cp_token ());
       cp_lexer_get_preprocessor_token (C_LEX_STRING_NO_JOIN, tok);
     }
@@ -658,10 +666,15 @@ cp_lexer_new_main (void)
                       + lexer->buffer->length ()
 		      - 1;
 
+  if (coro)
+    module_token_cdtor (parse_in, coro);
+
   /* Subsequent preprocessor diagnostics should use compiler
      diagnostic functions to get the compiler source location.  */
   done_lexing = true;
 
+  maybe_check_all_macros (parse_in);
+
   gcc_assert (!lexer->next_token->purged_p);
   return lexer;
 }
@@ -834,6 +847,8 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
   token->purged_p = false;
   token->error_reported = false;
   token->tree_check_p = false;
+  /* Usually never see a zero, but just in case ... */
+  token->main_source_p = line_table->depth <= 1;
 
   /* On some systems, some header files are surrounded by an
      implicit extern "C" block.  Set a flag in the token if it
@@ -2179,6 +2194,28 @@ static tree cp_parser_implicitly_scoped_statement
 static void cp_parser_already_scoped_statement
   (cp_parser *, bool *, const token_indent_info &);
 
+/* State of module-declaration parsing.  */
+enum module_parse
+{
+  MP_NOT_MODULE,	/* Not a module.  */
+
+  _MP_UNUSED,
+
+  MP_FIRST,	/* First declaration of TU.  */
+  MP_GLOBAL,	/* Global Module Fragment.  */
+
+  MP_PURVIEW_IMPORTS,   /* Imports of a module.  */
+  MP_PURVIEW,	/* Purview of a named module.  */
+
+  MP_PRIVATE_IMPORTS, /* Imports of a Private Module Fragment.  */
+  MP_PRIVATE,   /* Private Module Fragment.  */
+};
+
+static module_parse cp_parser_module_declaration
+  (cp_parser *parser, module_parse, bool exporting);
+static void cp_parser_import_declaration
+  (cp_parser *parser, module_parse, bool exporting);
+
 /* Declarations [gram.dcl.dcl] */
 
 static void cp_parser_declaration_seq_opt
@@ -3393,6 +3430,7 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
 	}
       else
 	error_at (location, "%qE does not name a type", id);
+
       /* If we're in a template class, it's possible that the user was
 	 referring to a type from a base class.  For example:
 
@@ -3400,14 +3438,23 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
 	   template <typename T> struct B : public A<T> { X x; };
 
 	 The user should have said "typename A<T>::X".  */
-      if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_CONSTEXPR])
+      if (cxx_dialect < cxx11 && C_RID_CODE (id) == RID_CONSTEXPR)
 	inform (location, "C++11 %<constexpr%> only available with "
 		"%<-std=c++11%> or %<-std=gnu++11%>");
-      else if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_NOEXCEPT])
+      else if (cxx_dialect < cxx11 && C_RID_CODE (id) == RID_NOEXCEPT)
 	inform (location, "C++11 %<noexcept%> only available with "
 		"%<-std=c++11%> or %<-std=gnu++11%>");
-      else if (cxx_dialect < cxx11
-	       && TREE_CODE (id) == IDENTIFIER_NODE
+      else if (TREE_CODE (id) == IDENTIFIER_NODE
+	       && (id_equal (id, "module") || id_equal (id, "import")))
+	{
+	  if (!modules_p ())
+	    inform (location, "%qE only available with %<-fmodules-ts%>", id);
+	  else
+	    inform (location, "%qE was not recognized as a module control-line",
+		    id);
+	}
+      else if (TREE_CODE (id) == IDENTIFIER_NODE
+	       && cxx_dialect < cxx11
 	       && id_equal (id, "thread_local"))
 	inform (location, "C++11 %<thread_local%> only available with "
 		"%<-std=c++11%> or %<-std=gnu++11%>");
@@ -3699,6 +3746,13 @@ cp_parser_skip_to_closing_parenthesis_1 (cp_parser *parser,
 	    condop_depth--;
 	  break;
 
+	case CPP_KEYWORD:
+	  if (token->keyword != RID__EXPORT
+	      && token->keyword != RID__MODULE
+	      && token->keyword != RID__IMPORT)
+	    break;
+	  /* FALLTHROUGH  */
+
 	case CPP_PRAGMA:
 	  /* We fell into a pragma.  Skip it, and continue. */
 	  cp_parser_skip_to_pragma_eol (parser, recovering ? token : nullptr);
@@ -3795,6 +3849,13 @@ cp_parser_skip_to_end_of_statement (cp_parser* parser)
 	  ++nesting_depth;
 	  break;
 
+	case CPP_KEYWORD:
+	  if (token->keyword != RID__EXPORT
+	      && token->keyword != RID__MODULE
+	      && token->keyword != RID__IMPORT)
+	    break;
+	  /* FALLTHROUGH  */
+
 	case CPP_PRAGMA:
 	  /* We fell into a pragma.  Skip it, and continue or return. */
 	  cp_parser_skip_to_pragma_eol (parser, token);
@@ -3877,6 +3938,13 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
 	  nesting_depth++;
 	  break;
 
+	case CPP_KEYWORD:
+	  if (token->keyword != RID__EXPORT
+	      && token->keyword != RID__MODULE
+	      && token->keyword != RID__IMPORT)
+	    break;
+	  /* FALLTHROUGH  */
+
 	case CPP_PRAGMA:
 	  /* Skip it, and continue or return. */
 	  cp_parser_skip_to_pragma_eol (parser, token);
@@ -4758,6 +4826,10 @@ cp_parser_translation_unit (cp_parser* parser)
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
 
+  module_parse mp_state = MP_NOT_MODULE;
+  if (modules_p () && !header_module_p ())
+    mp_state = MP_FIRST;
+
   bool implicit_extern_c = false;
 
   /* Parse until EOF.  */
@@ -4781,6 +4853,55 @@ cp_parser_translation_unit (cp_parser* parser)
       if (token->type == CPP_EOF)
 	break;
 
+      if (modules_p ())
+	{
+	  /* Top-level module declarations are ok, and change the
+	     portion of file we're in.  Top-level import declarations
+	     are significant for the import portions.  */
+
+	  cp_token *next = token;
+	  bool exporting = token->keyword == RID__EXPORT;
+	  if (exporting)
+	    {
+	      cp_lexer_consume_token (parser->lexer);
+	      next = cp_lexer_peek_token (parser->lexer);
+	    }
+	  if (next->keyword == RID__MODULE)
+	    {
+	      mp_state
+		= cp_parser_module_declaration (parser, mp_state, exporting);
+	      continue;
+	    }
+	  else if (next->keyword == RID__IMPORT)
+	    {
+	      if (mp_state == MP_FIRST)
+		mp_state = MP_NOT_MODULE;
+	      cp_parser_import_declaration (parser, mp_state, exporting);
+	      continue;
+	    }
+	  else
+	    gcc_checking_assert (!exporting);
+
+	  if (mp_state == MP_GLOBAL && token->main_source_p)
+	    {
+	      static bool warned = false;
+	      if (!warned)
+		{
+		  warned = true;
+		  error_at (token->location,
+			    "global module fragment contents must be"
+			    " from preprocessor inclusion");
+		}
+	    }
+	}
+
+      /* This relies on the ordering of module_parse values.  */
+      if (mp_state == MP_PURVIEW_IMPORTS || mp_state == MP_PRIVATE_IMPORTS)
+	/* We're no longer in the import portion of a named module.  */
+	mp_state = module_parse (mp_state + 1);
+      else if (mp_state == MP_FIRST)
+	mp_state = MP_NOT_MODULE;
+
       if (token->type == CPP_CLOSE_BRACE)
 	{
 	  cp_parser_error (parser, "expected declaration");
@@ -13449,9 +13570,234 @@ cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p,
     }
 }
 
+/* Modules */
+
+/* Parse a module-name,
+   identifier
+   module-name . identifier
+   header-name
+
+   Returns a pointer to module object, NULL.   */
+
+static module_state *
+cp_parser_module_name (cp_parser *parser)
+{
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  if (token->type == CPP_HEADER_NAME)
+    {
+      cp_lexer_consume_token (parser->lexer);
+
+      return get_module (token->u.value);
+    }
+
+  module_state *parent = NULL;
+  bool partitioned = false;
+  if (token->type == CPP_COLON && named_module_p ())
+    {
+      partitioned = true;
+      cp_lexer_consume_token (parser->lexer);
+    }
+
+  for (;;)
+    {
+      if (cp_lexer_peek_token (parser->lexer)->type != CPP_NAME)
+	{
+	  cp_parser_error (parser, "expected module-name");
+	  break;
+	}
+
+      tree name = cp_lexer_consume_token (parser->lexer)->u.value;
+      parent = get_module (name, parent, partitioned);
+      token = cp_lexer_peek_token (parser->lexer);
+      if (!partitioned && token->type == CPP_COLON)
+	partitioned = true;
+      else if (token->type != CPP_DOT)
+	break;
+
+      cp_lexer_consume_token (parser->lexer);
+   }
+
+  return parent;
+}
+
+/* Named module-declaration
+     __module ; PRAGMA_EOL
+     __module private ; PRAGMA_EOL (unimplemented)
+     [__export] __module module-name attr-spec-seq-opt ; PRAGMA_EOL
+*/
+
+static module_parse
+cp_parser_module_declaration (cp_parser *parser, module_parse mp_state,
+			      bool exporting)
+{
+  /* We're a pseudo pragma.  */
+  parser->lexer->in_pragma = true;
+  cp_token *token = cp_lexer_consume_token (parser->lexer);
+
+  if (mp_state == MP_FIRST && !exporting
+      && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      /* Start global module fragment.  */
+      cp_lexer_consume_token (parser->lexer);
+      module_kind |= MK_GLOBAL;
+      mp_state = MP_GLOBAL;
+      cp_parser_require_pragma_eol (parser, token);
+    }
+  else if (!exporting
+	   && cp_lexer_next_token_is (parser->lexer, CPP_COLON)
+	   && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_PRIVATE)
+	   && cp_lexer_nth_token_is (parser->lexer, 3, CPP_SEMICOLON))
+    {
+      cp_lexer_consume_token (parser->lexer);
+      cp_lexer_consume_token (parser->lexer);
+      cp_lexer_consume_token (parser->lexer);
+      cp_parser_require_pragma_eol (parser, token);
+
+      if ((mp_state != MP_PURVIEW && mp_state != MP_PURVIEW_IMPORTS)
+	  || !module_interface_p () || module_partition_p ())
+	error_at (token->location,
+		  "private module fragment not permitted here");
+      else
+	{
+	  mp_state = MP_PRIVATE_IMPORTS;
+	  sorry_at (token->location, "private module fragment");
+	}
+    }
+  else if (mp_state != MP_FIRST && mp_state != MP_GLOBAL)
+    {
+      error_at (token->location, "module-declaration not permitted here");
+    skip_eol:
+      cp_parser_skip_to_pragma_eol (parser, token);
+    }
+  else
+    {
+      module_state *mod = cp_parser_module_name (parser);
+      tree attrs = cp_parser_attributes_opt (parser);
+
+      mp_state = MP_PURVIEW_IMPORTS;
+      if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+	goto skip_eol;
+
+      declare_module (mod, token->location, exporting, attrs, parse_in);
+      cp_parser_require_pragma_eol (parser, token);
+    }
+
+  return mp_state;
+}
+
+/* Import-declaration
+   [__export] __import module-name attr-spec-seq-opt ; PRAGMA_EOL */
+
+static void
+cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,
+			      bool exporting)
+{
+  /* We're a pseudo pragma.  */
+  parser->lexer->in_pragma = true;
+  cp_token *token = cp_lexer_consume_token (parser->lexer);
+
+  if (mp_state != MP_PURVIEW_IMPORTS
+      && mp_state != MP_PRIVATE_IMPORTS
+      && module_purview_p ()
+      && !global_purview_p ())
+    {
+      error_at (token->location, "post-module-declaration"
+		" imports must be contiguous");
+    note_lexer:
+      inform (token->location, "perhaps insert a line break, or other"
+	      " disambiguation, to prevent this being considered a"
+	      " module control-line");
+    skip_eol:
+      cp_parser_skip_to_pragma_eol (parser, token);
+    }
+  else if (current_scope () != global_namespace)
+    {
+      error_at (token->location, "import-declaration must be at global scope");
+      goto note_lexer;
+    }
+  else
+    {
+      module_state *mod = cp_parser_module_name (parser);
+      tree attrs = cp_parser_attributes_opt (parser);
+
+      if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+	goto skip_eol;
+      cp_parser_require_pragma_eol (parser, token);
+
+      if (parser->in_unbraced_linkage_specification_p)
+	error_at (token->location, "import cannot appear directly in"
+		  " a linkage-specification");
+
+      if (attrs && module_purview_p () && !global_purview_p ()
+	  && private_lookup_attribute ("__translated",
+				       strlen ("__translated"), attrs))
+	error_at (token->location, "post-module-declaration imports"
+		  " must not be include-translated");
+      else if ((mp_state == MP_PURVIEW_IMPORTS
+		|| mp_state == MP_PRIVATE_IMPORTS)
+	       && !token->main_source_p)
+	error_at (token->location, "post-module-declaration imports"
+		  " must not be from header inclusion");
+
+      import_module (mod, token->location, exporting, attrs, parse_in);
+    }
+}
+
+/*  export-declaration.
+
+    export declaration
+    export { declaration-seq-opt }  */
+
+static void
+cp_parser_module_export (cp_parser *parser)
+{
+  gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT));
+  cp_token *token = cp_lexer_consume_token (parser->lexer);
+
+  if (!module_interface_p ())
+    error_at (token->location,
+	      "%qE may only occur after a module interface declaration",
+	      token->u.value);
+
+  bool braced = cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE);
+
+  unsigned mk = module_kind;
+  if (module_exporting_p ())
+    error_at (token->location,
+	      "%qE may only occur once in an export declaration",
+	      token->u.value);
+  module_kind |= MK_EXPORTING;
+
+  if (braced)
+    {
+      cp_ensure_no_omp_declare_simd (parser);
+      cp_ensure_no_oacc_routine (parser);
+
+      cp_lexer_consume_token (parser->lexer);
+      cp_parser_declaration_seq_opt (parser);
+      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+    }
+  else
+    {
+      /* Explicitly check if the next tokens might be a
+         module-directive line, so we can give a clearer error message
+         about why the directive will be rejected.  */
+      if (cp_lexer_next_token_is_keyword (parser->lexer, RID__MODULE)
+	  || cp_lexer_next_token_is_keyword (parser->lexer, RID__IMPORT)
+	  || cp_lexer_next_token_is_keyword (parser->lexer, RID__EXPORT))
+	error_at (token->location, "%<export%> not part of following"
+		  " module-directive");
+      cp_parser_declaration (parser);
+    }
+
+  module_kind = mk;
+}
+
 /* Declarations [gram.dcl.dcl] */
 
-/* Parse an optional declaration-sequence.
+/* Parse an optional declaration-sequence.  TOP_LEVEL is true, if this
+   is the top-level declaration sequence.  That affects whether we
+   deal with module-preamble.
 
    declaration-seq:
      declaration
@@ -13486,6 +13832,14 @@ cp_parser_declaration_seq_opt (cp_parser* parser)
    C++17:
      deduction-guide
 
+   modules:
+     (all these are only allowed at the outermost level, check
+   	that semantically, for better diagnostics)
+     module-declaration
+     module-export-declaration
+     module-import-declaration
+     export-declaration
+
    GNU extension:
 
    declaration:
@@ -13538,10 +13892,28 @@ cp_parser_declaration (cp_parser* parser)
       else
 	cp_parser_explicit_instantiation (parser);
     }
-  /* If the next token is `export', then we have a template
-     declaration.  */
+  /* If the next token is `export', it's new-style modules or
+     old-style template.  */
   else if (token1->keyword == RID_EXPORT)
-    cp_parser_template_declaration (parser, /*member_p=*/false);
+    {
+      if (!modules_p ())
+	cp_parser_template_declaration (parser, /*member_p=*/false);
+      else
+	cp_parser_module_export (parser);
+    }
+  else if (token1->keyword == RID__EXPORT
+	   || token1->keyword == RID__IMPORT
+	   || token1->keyword == RID__MODULE)
+    {
+      bool exporting = token1->keyword == RID__EXPORT;
+      cp_token *next = exporting ? token2 : token1;
+      if (exporting)
+	cp_lexer_consume_token (parser->lexer);
+      if (next->keyword == RID__MODULE)
+	cp_parser_module_declaration (parser, MP_NOT_MODULE, exporting);
+      else
+	cp_parser_import_declaration (parser, MP_NOT_MODULE, exporting);
+    }
   /* If the next token is `extern', 'static' or 'inline' and the one
      after that is `template', we have a GNU extended explicit
      instantiation directive.  */
@@ -16027,8 +16399,13 @@ cp_parser_template_declaration (cp_parser* parser, bool member_p)
     {
       /* Consume the `export' token.  */
       cp_lexer_consume_token (parser->lexer);
-      /* Warn that we do not support `export'.  */
-      warning (0, "keyword %<export%> not implemented, and will be ignored");
+      /* Warn that this use of export is deprecated.  */
+      if (cxx_dialect < cxx11)
+	warning (0, "keyword %<export%> not implemented, and will be ignored");
+      else if (cxx_dialect < cxx20)
+	warning (0, "keyword %<export%> is deprecated, and is ignored");
+      else
+	warning (0, "keyword %<export%> is enabled with %<-fmodules-ts%>");
     }
 
   cp_parser_template_declaration_after_export (parser, member_p);
@@ -17749,7 +18126,6 @@ cp_parser_explicit_specialization (cp_parser* parser)
       /* Give it C++ linkage to avoid confusing other parts of the
 	 front end.  */
       push_lang_context (lang_name_cplusplus);
-      need_lang_pop = true;
     }
 
   /* Let the front end know that we are beginning a specialization.  */
@@ -28483,11 +28861,6 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 					  prefer_type_arg (tag_type),
 					  /*complain=*/true);
 
-	  /* If we have a single function from a using decl, pull it out.  */
-	  if (TREE_CODE (decl) == OVERLOAD
-	      && !really_overloaded_fn (decl))
-	    decl = OVL_FUNCTION (decl);
-
 	  if (pushed_scope)
 	    pop_scope (pushed_scope);
 	}
@@ -29206,6 +29579,14 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
 
   /* Finish the function.  */
   fn = finish_function (inline_p);
+
+  if (modules_p ()
+      && !inline_p
+      && TYPE_P (DECL_CONTEXT (fn))
+      && (DECL_DECLARED_INLINE_P (fn)
+	  || processing_template_decl))
+    set_defining_module (fn);
+
   /* Generate code for it, if necessary.  */
   expand_or_defer_fn (fn);
   /* Restore the saved values.  */
diff --git c/gcc/cp/parser.h w/gcc/cp/parser.h
index ec487ea4252..13c37b21326 100644
--- c/gcc/cp/parser.h
+++ w/gcc/cp/parser.h
@@ -58,7 +58,8 @@ struct GTY (()) cp_token {
      deleted.  */
   bool purged_p : 1;
   bool tree_check_p : 1;
-  /* 4 unused bits.  */
+  bool main_source_p : 1;
+  /* 3 unused bits.  */
 
   /* The location at which this token was found.  */
   location_t location;



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

* [28/32] lang hook implementation
       [not found]                                                   ` <e67492d5-3be5-0993-538d-b798875c5e89@acm.org>
@ 2020-11-03 21:17                                                     ` Nathan Sidwell
       [not found]                                                     ` <b378cdb3-fe6f-956a-84a1-23af87ce6695@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:17 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

This implements the C++ overides of the langhooks I introduced.


-- 
Nathan Sidwell


[-- Attachment #2: 28-c++-langhooks.diff --]
[-- Type: text/x-patch, Size: 3102 bytes --]

diff --git c/gcc/cp/cp-lang.c w/gcc/cp/cp-lang.c
index 9e980bc6de9..d75fa8ab708 100644
--- c/gcc/cp/cp-lang.c
+++ w/gcc/cp/cp-lang.c
@@ -77,6 +77,16 @@ static tree cxx_enum_underlying_base_type (const_tree);
 #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type
 #undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE
 #define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type
+#undef LANG_HOOKS_PREPROCESS_MAIN_FILE
+#define LANG_HOOKS_PREPROCESS_MAIN_FILE module_begin_main_file
+#undef LANG_HOOKS_PREPROCESS_OPTIONS
+#define LANG_HOOKS_PREPROCESS_OPTIONS module_preprocess_options
+#undef LANG_HOOKS_PREPROCESS_UNDEF
+#define LANG_HOOKS_PREPROCESS_UNDEF module_cpp_undef
+#undef LANG_HOOKS_PREPROCESS_DEFERRED_MACRO
+#define LANG_HOOKS_PREPROCESS_DEFERRED_MACRO module_cpp_deferred_macro
+#undef LANG_HOOKS_PREPROCESS_TOKEN
+#define LANG_HOOKS_PREPROCESS_TOKEN module_token_pre
 
 #if CHECKING_P
 #undef LANG_HOOKS_RUN_LANG_SELFTESTS
diff --git c/gcc/cp/cp-objcp-common.c w/gcc/cp/cp-objcp-common.c
index e1397b7b710..d215fb07285 100644
--- c/gcc/cp/cp-objcp-common.c
+++ w/gcc/cp/cp-objcp-common.c
@@ -436,6 +436,9 @@ cp_register_dumps (gcc::dump_manager *dumps)
   class_dump_id = dumps->dump_register
     (".class", "lang-class", "lang-class", DK_lang, OPTGROUP_NONE, false);
 
+  module_dump_id = dumps->dump_register
+    (".module", "lang-module", "lang-module", DK_lang, OPTGROUP_NONE, false);
+
   raw_dump_id = dumps->dump_register
     (".raw", "lang-raw", "lang-raw", DK_lang, OPTGROUP_NONE, false);
 }
@@ -548,4 +551,16 @@ cp_common_init_ts (void)
   c_common_init_ts ();
 }
 
+/* Handle C++-specficic options here.  Punt to c_common otherwise.  */
+
+bool
+cp_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
+		  int kind, location_t loc,
+		  const struct cl_option_handlers *handlers)
+{
+  if (handle_module_option (unsigned (scode), arg, value))
+    return true;
+  return c_common_handle_option (scode, arg, value, kind, loc, handlers);
+}
+
 #include "gt-cp-cp-objcp-common.h"
diff --git c/gcc/cp/cp-objcp-common.h w/gcc/cp/cp-objcp-common.h
index 0936f166d5b..4b5b96fedbc 100644
--- c/gcc/cp/cp-objcp-common.h
+++ w/gcc/cp/cp-objcp-common.h
@@ -34,6 +34,8 @@ extern tree cp_unit_size_without_reusable_padding (tree);
 extern tree cp_get_global_decls ();
 extern tree cp_pushdecl (tree);
 extern void cp_register_dumps (gcc::dump_manager *);
+extern bool cp_handle_option (size_t, const char *, HOST_WIDE_INT, int,
+			      location_t, const struct cl_option_handlers *);
 extern tree cxx_make_type_hook			(tree_code);
 extern tree cxx_simulate_enum_decl (location_t, const char *,
 				    vec<string_int_pair>);
@@ -63,7 +65,7 @@ extern tree cxx_simulate_enum_decl (location_t, const char *,
 #undef LANG_HOOKS_REGISTER_DUMPS
 #define LANG_HOOKS_REGISTER_DUMPS cp_register_dumps
 #undef LANG_HOOKS_HANDLE_OPTION
-#define LANG_HOOKS_HANDLE_OPTION c_common_handle_option
+#define LANG_HOOKS_HANDLE_OPTION cp_handle_option
 #undef LANG_HOOKS_HANDLE_FILENAME
 #define LANG_HOOKS_HANDLE_FILENAME c_common_handle_filename
 #undef LANG_HOOKS_POST_OPTIONS


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

* [29/32] make-lang.in
       [not found]                                                     ` <b378cdb3-fe6f-956a-84a1-23af87ce6695@acm.org>
@ 2020-11-03 21:18                                                       ` Nathan Sidwell
       [not found]                                                       ` <bedd1f52-8a2c-b257-74bc-caf0cc1d0589@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:18 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here is the change to cp/Make-lang.in

In addition to adding new object files, it sets MODULE_VERSION to the 
current date, when DEVPHASE is experimental.  I also wedge that version 
into the REVISION variable.

for releases the MODULE_VERSION is the MAJOR.MINOR pair.  It'll refuse 
to load modules when the MODULE_VERSION is not on the same day (there's 
an override flag).

I'm not sure if we should continue putting the version into REVISION, 
particularly for non-experimental builds.

nathan

-- 
Nathan Sidwell


[-- Attachment #2: 29-c++-make.diff --]
[-- Type: text/x-patch, Size: 3003 bytes --]

diff --git c/gcc/cp/Make-lang.in w/gcc/cp/Make-lang.in
index 6ee4e41266f..b9aa4025bd4 100644
--- c/gcc/cp/Make-lang.in
+++ w/gcc/cp/Make-lang.in
@@ -46,13 +46,36 @@ CP_PLUGIN_HEADERS := cp-tree.h cxx-pretty-print.h name-lookup.h type-utils.h ope
 # Note that it would be nice to move the dependency on g++
 # into the C++ rule, but that needs a little bit of work
 # to do the right thing within all.cross.
-c++: cc1plus$(exeext)
+c++: cc1plus$(exeext) mapper-server$(exeext)
 
 # Tell GNU make to ignore these if they exist.
 .PHONY: c++
 
 CFLAGS-cp/g++spec.o += $(DRIVER_DEFINES)
 
+CFLAGS-cp/module.o += -DHOST_MACHINE=\"$(host)\" \
+	-DTARGET_MACHINE=\"$(target)\"
+
+ifeq ($(DEVPHASE_c),experimental)
+# We want to make the module version volatile, when in development mode,
+# so link it to the cp dir modification time
+cp/module.o : $(srcdir)/cp
+# Some date's don't grok 'r', if so, simply use today's
+# date (don't bootstrap at midnight).
+MODULE_VERSION := $(shell date -r $(srcdir)/cp '+%y%m%d-%H%M' 2>/dev/null\
+  || date '+%y%m%d-0000' 2>/dev/null || echo 0)
+version.o : $(srcdir)/cp
+# Set REVISION string
+ifeq ($(REVISION_c),)
+REVISION_c := []
+endif
+# Wedge inside [...], if that's what it already looks like
+REVISION_s := '" $(REVISION_c:]=)$(if \
+	$(REVISION_c:[]=),:):20$(MODULE_VERSION)$(if \
+	$(filter %],$(REVISION_c)),])"'
+CFLAGS-cp/module.o += -DMODULE_VERSION='($(subst -,,$(MODULE_VERSION))U)'
+endif
+
 # Create the compiler driver for g++.
 GXX_OBJS = $(GCC_OBJS) cp/g++spec.o
 xg++$(exeext): $(GXX_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
@@ -80,7 +124,8 @@ CXX_AND_OBJCXX_OBJS = \
 	cp/error.o cp/except.o cp/expr.o \
 	cp/friend.o cp/init.o \
 	cp/lambda.o cp/lex.o cp/logic.o \
-	cp/mangle.o cp/method.o \
+	cp/mangle.o cp/mapper-client.o cp/mapper-resolver.o \
+	cp/method.o cp/module.o \
 	cp/name-lookup.o cp/optimize.o \
 	cp/parser.o cp/pt.o cp/ptree.o \
 	cp/rtti.o \
@@ -94,6 +139,11 @@ $(CXX_AND_OBJCXX_OBJS): CFLAGS += -fauto-profile=cc1plus.fda
 $(CXX_AND_OBJCXX_OBJS): cc1plus.fda
 endif
 
+MAPPER_SERVER_OBJS := cp/mapper-server.o cp/mapper-resolver.o
+mapper-server$(exeext): $(MAPPER_SERVER_OBJS) $(LIBDEPS)
+	+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+	$(MAPPER_SERVER_OBJS) version.o $(CODYLIB) $(LIBIBERTY) $(LIBINTL)
+
 # Language-specific object files for C++.
 CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
 
@@ -239,6 +289,9 @@ c++.install-common: installdirs
 	    fi ; \
 	  fi; \
 	fi
+	rm -f $(DESTDIR)$(libexecsubdir)/mapper-server$(exeext)
+	$(INSTALL_PROGRAM) mapper-server$(exeext) \
+	  $(DESTDIR)$(libexecsubdir)/mapper-server$(exeext)
 
 # We can't use links because not everyone supports them.  So just copy the
 # manpage.
@@ -284,6 +337,7 @@ c++.mostlyclean:
 	-rm -f cp/*$(objext)
 	-rm -f cp/*$(coverageexts)
 	-rm -f xg++$(exeext) g++-cross$(exeext) cc1plus$(exeext) cc1plus.fda
+	-rm -f mapper-server$(exeext)
 c++.clean:
 c++.distclean:
 	-rm -f cp/config.status cp/Makefile


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

* [30/32] test harness
       [not found]                                                       ` <bedd1f52-8a2c-b257-74bc-caf0cc1d0589@acm.org>
@ 2020-11-03 21:18                                                         ` Nathan Sidwell
  2020-11-06 20:30                                                           ` Jeff Law
       [not found]                                                         ` <0a62f316-7c23-3492-f7c3-9c1653a61a75@acm.org>
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:18 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here's the test harness change, adding a few more prune cases.

-- 
Nathan Sidwell


[-- Attachment #2: 30-test-harness.diff --]
[-- Type: text/x-patch, Size: 1362 bytes --]

diff --git c/gcc/testsuite/lib/prune.exp w/gcc/testsuite/lib/prune.exp
index 190367c44e0..5d051b99def 100644
--- c/gcc/testsuite/lib/prune.exp
+++ w/gcc/testsuite/lib/prune.exp
@@ -48,7 +48,11 @@ proc prune_gcc_output { text } {
     regsub -all "(^|\n)\[^\n\]*: re(compiling|linking)\[^\n\]*" $text "" text
     regsub -all "(^|\n)Please submit.*instructions\[^\n\]*" $text "" text
     regsub -all "(^|\n)\[0-9\]\[0-9\]* errors\." $text "" text
-    regsub -all "(^|\n)(In file included|\[ \]+from)\[^\n\]*" $text "" text
+
+    # Diagnostic inclusion stack
+    regsub -all "(^|\n)(In file)?\[ \]+included from \[^\n\]*" $text "" text
+    regsub -all "(^|\n)\[ \]+from \[^\n\]*" $text "" text
+    regsub -all "(^|\n)(In|of) module( \[^\n \]*,)? imported at \[^\n\]*" $text "" text
 
     # Ignore informational notes.
     regsub -all "(^|\n)\[^\n\]*: note: \[^\n\]*" $text "" text
@@ -128,8 +133,8 @@ proc prune_file_path { text } {
 # footnote.
 
 proc prune_ices { text } {
-  regsub -all "(^|\n)\[^\n\]*: internal compiler error:.*for instructions\[^\n\]*" $text "" text
-  regsub -all "(^|\n|')*Internal compiler error:.*for instructions\[^\n\]*" $text "" text
+  regsub -all "(^|\n)\[^\n\]*: internal compiler error:.*\nSee \[^\n\]*" $text "" text
+  regsub -all "(^|\n|')*Internal compiler error:.*\nSee \[^\n\]*" $text "" text
   return $text
 }
 


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

* [31/32] test suite
       [not found]                                                         ` <0a62f316-7c23-3492-f7c3-9c1653a61a75@acm.org>
@ 2020-11-03 21:18                                                           ` Nathan Sidwell
       [not found]                                                           ` <f4bda23c-7fa1-5a1b-e970-9fae9b2575b6@acm.org>
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:18 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

Here's the testsuite, a new g++.dg/modules directory

I don't think this'll work with cross-testing

-- 
Nathan Sidwell


[-- Attachment #2: 31-testsuite.diff.gz --]
[-- Type: application/gzip, Size: 70911 bytes --]

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

* [32/32] fixinclude
       [not found]                                                           ` <f4bda23c-7fa1-5a1b-e970-9fae9b2575b6@acm.org>
@ 2020-11-03 21:18                                                             ` Nathan Sidwell
  2020-11-04  0:04                                                               ` David Edelsohn
  0 siblings, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 21:18 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener, David Edelsohn

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

AIX needed a fixinclude.  It contained

typedef struct {...} *ptr;

that's an ODR landmine.  Fixed by giving the struct a name

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 32-aix-fixincl.diff --]
[-- Type: text/x-patch, Size: 3955 bytes --]

diff --git c/fixincludes/fixincl.x w/fixincludes/fixincl.x
index 758d5620641..21439652bce 100644
--- c/fixincludes/fixincl.x
+++ w/fixincludes/fixincl.x
@@ -2,11 +2,11 @@
  *
  * DO NOT EDIT THIS FILE   (fixincl.x)
  *
- * It has been AutoGen-ed  October  3, 2020 at 11:40:52 PM by AutoGen 5.18
+ * It has been AutoGen-ed  October 21, 2020 at 10:43:22 AM by AutoGen 5.18.16
  * From the definitions    inclhack.def
  * and the template file   fixincl
  */
-/* DO NOT SVN-MERGE THIS FILE, EITHER Sat Oct  3 23:40:52 UTC 2020
+/* DO NOT SVN-MERGE THIS FILE, EITHER Wed Oct 21 10:43:22 EDT 2020
  *
  * You must regenerate it.  Use the ./genfixes script.
  *
@@ -15,7 +15,7 @@
  * certain ANSI-incompatible system header files which are fixed to work
  * correctly with ANSI C and placed in a directory that GNU C will search.
  *
- * This file contains 259 fixup descriptions.
+ * This file contains 260 fixup descriptions.
  *
  * See README for more information.
  *
@@ -1247,6 +1247,43 @@ static const char* apzAix_Rwlock_Initializer_1Patch[] = {
 {{ \\\n",
     (char*)NULL };
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Description of Aix_Physadr_T fix
+ */
+tSCC zAix_Physadr_TName[] =
+     "aix_physadr_t";
+
+/*
+ *  File name selection pattern
+ */
+tSCC zAix_Physadr_TList[] =
+  "sys/types.h\0";
+/*
+ *  Machine/OS name selection pattern
+ */
+tSCC* apzAix_Physadr_TMachs[] = {
+        "*-*-aix*",
+        (const char*)NULL };
+
+/*
+ *  content selection pattern - do fix if pattern found
+ */
+tSCC zAix_Physadr_TSelect0[] =
+       "typedef[ \t]*struct[ \t]*([{][^}]*[}][ \t]*\\*[ \t]*physadr_t;)";
+
+#define    AIX_PHYSADR_T_TEST_CT  1
+static tTestDesc aAix_Physadr_TTests[] = {
+  { TT_EGREP,    zAix_Physadr_TSelect0, (regex_t*)NULL }, };
+
+/*
+ *  Fix Command Arguments for Aix_Physadr_T
+ */
+static const char* apzAix_Physadr_TPatch[] = {
+    "format",
+    "typedef struct __physadr_s %1",
+    (char*)NULL };
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * *
  *
  *  Description of Aix_Pthread fix
@@ -10521,9 +10558,9 @@ static const char* apzX11_SprintfPatch[] = {
  *
  *  List of all fixes
  */
-#define REGEX_COUNT          297
+#define REGEX_COUNT          298
 #define MACH_LIST_SIZE_LIMIT 187
-#define FIX_COUNT            259
+#define FIX_COUNT            260
 
 /*
  *  Enumerate the fixes
@@ -10555,6 +10592,7 @@ typedef enum {
     AIX_MUTEX_INITIALIZER_1_FIXIDX,
     AIX_COND_INITIALIZER_1_FIXIDX,
     AIX_RWLOCK_INITIALIZER_1_FIXIDX,
+    AIX_PHYSADR_T_FIXIDX,
     AIX_PTHREAD_FIXIDX,
     AIX_STDINT_1_FIXIDX,
     AIX_STDINT_2_FIXIDX,
@@ -10921,6 +10959,11 @@ tFixDesc fixDescList[ FIX_COUNT ] = {
      AIX_RWLOCK_INITIALIZER_1_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
      aAix_Rwlock_Initializer_1Tests,   apzAix_Rwlock_Initializer_1Patch, 0 },
 
+  {  zAix_Physadr_TName,    zAix_Physadr_TList,
+     apzAix_Physadr_TMachs,
+     AIX_PHYSADR_T_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
+     aAix_Physadr_TTests,   apzAix_Physadr_TPatch, 0 },
+
   {  zAix_PthreadName,    zAix_PthreadList,
      apzAix_PthreadMachs,
      AIX_PTHREAD_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
diff --git c/fixincludes/inclhack.def w/fixincludes/inclhack.def
index 47eb236586c..80c9adfb07c 100644
--- c/fixincludes/inclhack.def
+++ w/fixincludes/inclhack.def
@@ -720,6 +720,20 @@ fix = {
 		"{ \\\\\n";
 };
 
+
+/* On AIX 'typedef struct {<stuff>} * physadr_t;' needs to give the struct a
+   name for linkage purposes.  Fortunately it is on exactly one
+   line.  */
+fix = {
+    hackname  = aix_physadr_t;
+    mach      = "*-*-aix*";
+    files     = sys/types.h;
+    select    = "typedef[ \t]*struct[ \t]*([{][^}]*[}][ \t]*\\*[ \t]*physadr_t;)";
+    c_fix     = format;
+    c_fix_arg = "typedef struct __physadr_s %1";
+    test_text = "typedef struct __physadr_s {";
+};
+
 /*
  *  pthread.h on AIX 4.3.3 tries to define a macro without whitspace
  *  which violates a requirement of ISO C.

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

* Re: [04/32] cpp lexer
  2020-11-03 21:13       ` [04/32] cpp lexer Nathan Sidwell
@ 2020-11-03 23:08         ` Joseph Myers
  2020-11-03 23:21           ` Nathan Sidwell
  2020-11-06 20:23         ` Jeff Law
  1 sibling, 1 reply; 68+ messages in thread
From: Joseph Myers @ 2020-11-03 23:08 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: GCC Patches, Jason Merrill, Richard Biener

On Tue, 3 Nov 2020, Nathan Sidwell wrote:

> @@ -888,9 +915,9 @@ struct GTY(()) cpp_hashnode {
>    unsigned int directive_index : 7;	/* If is_directive,
>  					   then index into directive table.
>  					   Otherwise, a NODE_OPERATOR.  */
> -  unsigned char rid_code;		/* Rid code - for front ends.  */
> +  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
> +  unsigned int flags : 9;		/* CPP flags.  */
>    ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
> -  unsigned int flags : 8;		/* CPP flags.  */
>  
>    /* 6 bits spare (plus another 32 on 64-bit hosts).  */

I'd expect this "6 bits spare" comment to be updated when expanding the 
flags field.

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: [04/32] cpp lexer
  2020-11-03 23:08         ` Joseph Myers
@ 2020-11-03 23:21           ` Nathan Sidwell
  0 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-03 23:21 UTC (permalink / raw)
  To: Joseph Myers; +Cc: GCC Patches, Jason Merrill, Richard Biener

On 11/3/20 6:08 PM, Joseph Myers wrote:
> On Tue, 3 Nov 2020, Nathan Sidwell wrote:
> 
>> @@ -888,9 +915,9 @@ struct GTY(()) cpp_hashnode {
>>     unsigned int directive_index : 7;	/* If is_directive,
>>   					   then index into directive table.
>>   					   Otherwise, a NODE_OPERATOR.  */
>> -  unsigned char rid_code;		/* Rid code - for front ends.  */
>> +  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
>> +  unsigned int flags : 9;		/* CPP flags.  */
>>     ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
>> -  unsigned int flags : 8;		/* CPP flags.  */
>>   
>>     /* 6 bits spare (plus another 32 on 64-bit hosts).  */
> 
> I'd expect this "6 bits spare" comment to be updated when expanding the
> flags field.

ah, that;s an error on my part separating two close pieces of this diff. 
  It does in fact say that

nathan

-- 
Nathan Sidwell

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

* Re: [32/32] fixinclude
  2020-11-03 21:18                                                             ` [32/32] fixinclude Nathan Sidwell
@ 2020-11-04  0:04                                                               ` David Edelsohn
  2020-11-18 18:36                                                                 ` Nathan Sidwell
  0 siblings, 1 reply; 68+ messages in thread
From: David Edelsohn @ 2020-11-04  0:04 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: GCC Patches, Jason Merrill, Richard Biener

On Tue, Nov 3, 2020 at 4:18 PM Nathan Sidwell <nathan@acm.org> wrote:
>
> AIX needed a fixinclude.  It contained
>
> typedef struct {...} *ptr;
>
> that's an ODR landmine.  Fixed by giving the struct a name

Okay.

Thanks, David

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

* Re: [25/32] modules!
  2020-11-03 21:17                                               ` [25/32] modules! Nathan Sidwell
@ 2020-11-04 12:48                                                 ` Nathan Sidwell
  0 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-04 12:48 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

this one failed to post due to size.  The intro is attached uncompressed 
-- or you could just checkout the branch and read it there.

this is the new modules file. (also resolvesa long standing fixme in pt.c)

I'll leave the introductory comment to speak for itself, but ask if
something's unclear.  As mentioned in the introduction, this is where
the bulk of the fixmes remain.

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 25-module-intro.txt --]
[-- Type: text/plain, Size: 8390 bytes --]


/* Comments in this file have a non-negligible chance of being wrong
   or at least inaccurate.  Due to (a) my misunderstanding, (b)
   ambiguities that I have interpretted differently to original intent
   (c) changes in the specification, (d) my poor wording, (e) source
   changes.  */

/* (Incomplete) Design Notes

   A hash table contains all module names.  Imported modules are
   present in a modules array, which by construction places an
   import's dependencies before the import itself.  The single
   exception is the current TU, which always occupies slot zero (even
   when it is not a module).

   Imported decls occupy an entity_ary, an array of mc_slots, indexed
   by importing module and index within that module.  A flat index is
   used, as each module reserves a contiguous range of indices.
   Initially each slot indicates the CMI section containing the
   streamed decl.  When the decl is imported it will point to the decl
   itself.

   Additionally each imported decl is mapped in the entity_map via its
   DECL_UID to the flat index in the entity_ary.  Thus we can locate
   the index for any imported decl by using this map and then
   de-flattening the index via a binary seach of the module vector.
   Cross-module references are by (remapped) module number and
   module-local index.

   Each importable DECL contains several flags.  The simple set are
   DECL_EXPORT_P, DECL_MODULE_PURVIEW_P and DECL_MODULE_IMPORT_P.  The
   first indicates whether it is exported, the second whether it is in
   the module purview (as opposed to the global module fragment), and
   the third indicates whether it was an import into this TU or not.

   The more detailed flags are DECL_MODULE_PARTITION_P,
   DECL_MODULE_ENTITY_P & DECL_MODULE_PENDING_SPECIALIZATIONS_P.  The
   first is set in a primary interface unit on decls that were read
   from module partitions (these will have DECL_MODULE_IMPORT_P set
   too).  Such decls will be streamed out to the primary's CMI.
   DECL_MODULE_ENTITY_P is set when an entity is imported, even if it
   matched a non-imported entity.  Such a decl will not have
   DECL_MODULE_IMPORT_P set, even though it has an entry in the entity
   map and array.  DECL_MODULE_PENDING_SPECIALIZATIONS_P is set on a
   primary template, and indicates there are specializations that
   should be streamed in before trying to specialize this template.

   Header units are module-like.

   For namespace-scope lookup, the decls for a particular module are
   held located in a sparse array hanging off the binding of the name.
   This is partitioned into two: a few fixed slots at the start
   followed by the sparse slots afterwards.  By construction we only
   need to append new slots to the end -- there is never a need to
   insert in the middle.  The fixed slots are MODULE_SLOT_CURRENT for
   the current TU (regardless of whether it is a module or not),
   MODULE_SLOT_GLOBAL and MODULE_SLOT_PARTITION.  These latter two
   slots are used for merging entities across the global module and
   module partitions respectively.  MODULE_SLOT_PARTITION is only
   present in a module.  Neither of those two slots is searched during
   name lookup -- they are internal use only.  This vector is created
   lazily once we require it, if there is only a declaration from the
   current TU, a regular binding is present.  It is converted on
   demand.

   OPTIMIZATION: Outside of the current TU, we only need ADL to work.
   We could optimize regular lookup for the current TU by glomming all
   the visible decls on its slot.  Perhaps wait until design is a
   little more settled though.

   There is only one instance of each extern-linkage namespace.  It
   appears in every module slot that makes it visible.  It also
   appears in MODULE_SLOT_GLOBAL.  (It is an ODR violation if they
   collide with some other global module entity.)  We also have an
   optimization that shares the slot for adjacent modules that declare
   the same such namespace.

   A module interface compilation produces a Compiled Module Interface
   (CMI).  The format used is Encapsulated Lazy Records Of Numbered
   Declarations, which is essentially ELF's section encapsulation. (As
   all good nerds are aware, Elrond is half Elf.)  Some sections are
   named, and contain information about the module as a whole (indices
   etc), and other sections are referenced by number.  Although I
   don't defend against actively hostile CMIs, there is some
   checksumming involved to verify data integrity.  When dumping out
   an interface, we generate a graph of all the
   independently-redeclarable DECLS that are needed, and the decls
   they reference.  From that we determine the strongly connected
   components (SCC) within this TU.  Each SCC is dumped to a separate
   numbered section of the CMI.  We generate a binding table section,
   mapping each namespace&name to a defining section.  This allows
   lazy loading.

   Lazy loading employs mmap to map a read-only image of the CMI.
   It thus only occupies address space and is paged in on demand,
   backed by the CMI file itself.  If mmap is unavailable, regular
   FILEIO is used.  Also, there's a bespoke ELF reader/writer here,
   which implements just the section table and sections (including
   string sections) of a 32-bit ELF in host byte-order.  You can of
   course inspect it with readelf.  I figured 32-bit is sufficient,
   for a single module.  I detect running out of section numbers, but
   do not implement the ELF overflow mechanism.  At least you'll get
   an error if that happens.

   We do not separate declarations and definitions.  My guess is that
   if you refer to the declaration, you'll also need the definition
   (template body, inline function, class definition etc).  But this
   does mean we can get larger SCCs than if we separated them.  It is
   unclear whether this is a win or not.

   Notice that we embed section indices into the contents of other
   sections.  Thus random manipulation of the CMI file by ELF tools
   may well break it.  The kosher way would probably be to introduce
   indirection via section symbols, but that would require defining a
   relocation type.

   Notice that lazy loading of one module's decls can cause lazy
   loading of other decls in the same or another module.  Clearly we
   want to avoid loops.  In a correct program there can be no loops in
   the module dependency graph, and the above-mentioned SCC algorithm
   places all intra-module circular dependencies in the same SCC.  It
   also orders the SCCs wrt each other, so dependent SCCs come first.
   As we load dependent modules first, we know there can be no
   reference to a higher-numbered module, and because we write out
   dependent SCCs first, likewise for SCCs within the module.  This
   allows us to immediately detect broken references.  When loading,
   we must ensure the rest of the compiler doesn't cause some
   unconnected load to occur (for instance, instantiate a template).

Classes used:

   dumper - logger

   data - buffer

   bytes - data streamer
   bytes_in : bytes - scalar reader
   bytes_out : bytes - scalar writer

   elf - ELROND format
   elf_in : elf - ELROND reader
   elf_out : elf - ELROND writer

   trees_in : bytes_in - tree reader
   trees_out : bytes_out - tree writer

   depset - dependency set
   depset::hash - hash table of depsets
   depset::tarjan - SCC determinator

   uidset<T> - set T's related to a UID
   uidset<T>::hash hash table of uidset<T>

   loc_spans - location map data

   module_state - module object

   slurping - data needed during loading

   macro_import - imported macro data
   macro_export - exported macro data

   The ELROND objects use mmap, for both reading and writing.  If mmap
   is unavailable, fileno IO is used to read and write blocks of data.

   The mapper object uses fileno IO to communicate with the server or
   program.   */

/* In expermental (trunk) sources, MODULE_VERSION is a #define passed
   in from the Makefile.  It records the modification date of the
   source directory -- that's the only way to stay sane.  In release
   sources, we (plan to) use the compiler's major.minor versioning.
   While the format might not change between at minor versions, it
   seems simplest to tie the two together.  There's no concept of
   inter-version compatibility.  */

[-- Attachment #3: 25-c++-modules.diff.gz --]
[-- Type: application/gzip, Size: 136673 bytes --]

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

* Re: [21/32] miscelaneous
  2020-11-03 21:16                                       ` [21/32] miscelaneous Nathan Sidwell
@ 2020-11-05 13:30                                         ` Richard Biener
  2020-11-06 17:08                                           ` Nathan Sidwell
  0 siblings, 1 reply; 68+ messages in thread
From: Richard Biener @ 2020-11-05 13:30 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: GCC Patches, Jason Merrill

On Tue, Nov 3, 2020 at 10:16 PM Nathan Sidwell <nathan@acm.org> wrote:
>
> These are changes to gcc/tree.h adding some raw accessors to nodes,
> which seemed preferable to direct field access.  I also needed access to
> the integral constant cache

can you please document the adjusted interface to cache_integer_cst in
its (non-existing) function level comment?  It looks like 'replace'== true
turns it into get_or_insert from now put with an assertion it wasn't in the
cache.

Otherwise OK.

Thanks,
Richard.

>
> --
> Nathan Sidwell
>

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

* Re: [21/32] miscelaneous
  2020-11-05 13:30                                         ` Richard Biener
@ 2020-11-06 17:08                                           ` Nathan Sidwell
  0 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-06 17:08 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, Jason Merrill

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

On 11/5/20 8:30 AM, Richard Biener wrote:
> On Tue, Nov 3, 2020 at 10:16 PM Nathan Sidwell <nathan@acm.org> wrote:
>>
>> These are changes to gcc/tree.h adding some raw accessors to nodes,
>> which seemed preferable to direct field access.  I also needed access to
>> the integral constant cache
> 
> can you please document the adjusted interface to cache_integer_cst in
> its (non-existing) function level comment?  It looks like 'replace'== true
> turns it into get_or_insert from now put with an assertion it wasn't in the
> cache.

Sure.  It's a little weird in that the current behaviour is to allow 
duplicates in the hash table, but not in the type's small-value vector.

I renamed the new parameter and documented what happens.  I'll apply 
this as a distinct patch during the merge (with changelog).  For now it 
lives on the modules branch

nathan

-- 
Nathan Sidwell

[-- Attachment #2: int-cst.diff --]
[-- Type: text/x-patch, Size: 2138 bytes --]

diff --git c/gcc/tree.c w/gcc/tree.c
index 9260772b846..9e10df0d7d0 100644
--- c/gcc/tree.c
+++ w/gcc/tree.c
@@ -1727,8 +1727,15 @@ wide_int_to_tree (tree type, const poly_wide_int_ref &value)
   return build_poly_int_cst (type, value);
 }
 
-void
-cache_integer_cst (tree t)
+/* Insert INTEGER_CST T into a cache of integer constants.  And return
+   the cached constant (which may or may not be T).  If MAY_DUPLICATE
+   is false, and T falls into the type's 'smaller values' range, there
+   cannot be an existing entry.  Otherwise, if MAY_DUPLICATE is true,
+   or the value is large, should an existing entry exist, it is
+   returned (rather than inserting T).  */
+
+tree
+cache_integer_cst (tree t, bool may_duplicate ATTRIBUTE_UNUSED)
 {
   tree type = TREE_TYPE (t);
   int ix = -1;
@@ -1742,7 +1749,7 @@ cache_integer_cst (tree t)
   switch (TREE_CODE (type))
     {
     case NULLPTR_TYPE:
-      gcc_assert (integer_zerop (t));
+      gcc_checking_assert (integer_zerop (t));
       /* Fallthru.  */
 
     case POINTER_TYPE:
@@ -1822,21 +1829,32 @@ cache_integer_cst (tree t)
 	  TYPE_CACHED_VALUES (type) = make_tree_vec (limit);
 	}
 
-      gcc_assert (TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) == NULL_TREE);
-      TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) = t;
+      if (tree r = TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix))
+	{
+	  gcc_checking_assert (may_duplicate);
+	  t = r;
+	}
+      else
+	TREE_VEC_ELT (TYPE_CACHED_VALUES (type), ix) = t;
     }
   else
     {
       /* Use the cache of larger shared ints.  */
       tree *slot = int_cst_hash_table->find_slot (t, INSERT);
-      /* If there is already an entry for the number verify it's the
-         same.  */
-      if (*slot)
-	gcc_assert (wi::to_wide (tree (*slot)) == wi::to_wide (t));
+      if (tree r = *slot)
+	{
+	  /* If there is already an entry for the number verify it's the
+	     same value.  */
+	  gcc_checking_assert (wi::to_wide (tree (r)) == wi::to_wide (t));
+	  /* And return the cached value.  */
+	  t = r;
+	}
       else
 	/* Otherwise insert this one into the hash table.  */
 	*slot = t;
     }
+
+  return t;
 }
 
 

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

* Re: [01/32] langhooks
  2020-11-03 21:13 ` [01/32] langhooks Nathan Sidwell
@ 2020-11-06 19:58   ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 19:58 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:13 PM, Nathan Sidwell wrote:
> I needed a set of hook interfacing the preprocessor to the language.
> they get called from pieces in c-family.
>
> preprocess_main_file:  we need to know when any forced headers have
> been parsed in order to deal with linemaps and macro visibility
>
> preprocess_options: A way for the language to adjust any preprocessor
> options and alter direct callbacks
>
> preprocess_undef: We need visibility of #undefs
>
> preprocess_deferred_macro: macros from header-units are instantiated
> lazily.  This is the hook for the preprocessor to get that done.
>
> preprocess_token: Even in -E processing, we need to observe the token
> stream in order to load up the macro tables of header units.
>
> c-family's c-lex.c, c-opts.c & c-ppoutput.c get to call these hooks in
> various cases

LGTM.

jeff



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

* Re: [02/32] linemaps
  2020-11-03 21:13   ` [02/32] linemaps Nathan Sidwell
@ 2020-11-06 20:10     ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:10 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:13 PM, Nathan Sidwell wrote:
> Location handling needs to add 2 things
>
> 1) a new kind of inclusion -- namely a module.  We add LC_MODULE as a
> map kind,
>
> 2) the ability to allocate blocks of line-maps for both ordinary
> locations and macro locations, that are then filled in by the module
> loader.

LGTM, but give David M a little more time to chime in (he's been on PTO
for at least part of this week).


Say perhaps until Tuesday?


jeff



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

* Re: [03/32] cpp-callbacks
  2020-11-03 21:13     ` [03/32] cpp-callbacks Nathan Sidwell
@ 2020-11-06 20:10       ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:10 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:13 PM, Nathan Sidwell wrote:
> Here are the callbacks in the preprocessor itself.
>
> a) one to handle deferred macros
>
> b) one to handle include translation.  For every '#include <header>',
> there is the possibility of replacing that with 'import <header>'. 
> This hook determines if that happens.

OK

jeff



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

* Re: [04/32] cpp lexer
  2020-11-03 21:13       ` [04/32] cpp lexer Nathan Sidwell
  2020-11-03 23:08         ` Joseph Myers
@ 2020-11-06 20:23         ` Jeff Law
  2020-11-06 21:06           ` Nathan Sidwell
  1 sibling, 1 reply; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:23 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:13 PM, Nathan Sidwell wrote:
> c++ modules creates 2 new kinds of preprocessor lines
> [export] module ...
> [export] import ...
>
> To all intents and purposes these are cppdirectives spelt without a
> leading '#'.  module and import are context-sensitive keywords.  Thus
> preprocessor tokenizing needs a bit of token peeking.  This is that
> peeking.
>
> We have a new node flag 'NODE_MODULE', which marks whether an
> identifier is significant to this peeking.  When we see such an
> identifier at the beginning of a logical line, we need to peek further
> and figure out whether these are those keywords.
>
> When successfully peeked, we replace the identifiers with
> internal-only tokens that the c++ parser recognizes.
>
>
> 04-cpp-lexer.diff
>
> diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
> index 8e398863cf6..81be6457951 100644
> --- c/libcpp/include/cpplib.h
> +++ w/libcpp/include/cpplib.h
>
> @@ -888,9 +915,9 @@ struct GTY(()) cpp_hashnode {
>    unsigned int directive_index : 7;	/* If is_directive,
>  					   then index into directive table.
>  					   Otherwise, a NODE_OPERATOR.  */
> -  unsigned char rid_code;		/* Rid code - for front ends.  */
> +  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
> +  unsigned int flags : 9;		/* CPP flags.  */
>    ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
> -  unsigned int flags : 8;		/* CPP flags.  */
>  
>    /* 6 bits spare (plus another 32 on 64-bit hosts).  */

Someone already mentioned it, but the # of spare bits needs updating.


>  
> diff --git c/libcpp/lex.c w/libcpp/lex.c
> index fb222924c8c..b3498f195bf 100644
> --- c/libcpp/lex.c
> +++ w/libcpp/lex.c
> @@ -2606,6 +2622,131 @@ _cpp_temp_token (cpp_reader *pfile)
>    return result;
>  }
>  
> +/* RESULT is a CPP_NAME with NODE_MODULE set.  See if we should enter
> +   deferred_pragma mode to tokenize the rest of the line.  */
> +
> +static void
> +cpp_maybe_module_directive (cpp_reader *pfile, cpp_token *result)
> +{
> +  unsigned backup = 0; /* Tokens we peeked.  */
> +  cpp_hashnode *node = result->val.node.node;
> +  cpp_token *peek = result;
> +  cpp_token *keyword = peek;
> +  cpp_hashnode *(&n_modules)[spec_nodes::M_HWM][2] = pfile->spec_nodes.n_modules;
> +  int header_count = 0;
> +
> +  /* Enter directives mode for the peeking.  */
> +  pfile->state.in_deferred_pragma = true;
> +  pfile->state.pragma_allow_expansion = true;
> +  pfile->state.save_comments = 0;
> +  pfile->directive_line = result->src_loc;
It looks like you slam in known values when you drop out of directive
mode rather than restoring the original values.  That may be OK, I'm not
all that familiar with this code to know if that's corerct or not.
> +
> +  if (__builtin_expect (node == n_modules[spec_nodes::M__IMPORT][0], false))
> +    /* __import  */
> +    header_count = backup + 2 + 16;
> +  else if (__builtin_expect (node == n_modules[spec_nodes::M_IMPORT][0], false))
> +    /* import  */
> +    header_count = backup + 2 + (CPP_OPTION (pfile, preprocessed) ? 16 : 0);
> +  else if (__builtin_expect (node == n_modules[spec_nodes::M_MODULE][0], false))
> +    ; /* module  */
> +  else
> +    goto not_module;

Are the __builtin_expects really useful here?  If not I'd prefer to
avoid them and just let the predictors do their job.  Similarly 
elsewhere in this patch.


+ {
> +    not_module:
> +      /* Drop out of directive mode.  */
> +      pfile->state.save_comments
> +	= !CPP_OPTION (pfile, discard_comments);
> +      pfile->state.in_deferred_pragma = false;
> +      pfile->state.angled_headers = false;

This doesn't seem to match the code to go into directives mode all that
well.  You don't reset pragma_allow_expansion or directive_line.


Jeff

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

* Re: [11/32] instrumentation
  2020-11-03 21:14                   ` [11/32] instrumentation Nathan Sidwell
@ 2020-11-06 20:28                     ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:28 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:14 PM, Nathan Sidwell wrote:
> I add one new parameter -- the number of concurrently open module
> files, and 3 instrumentation timers.
>
>
>
> 11-core-parmtime.diff
>

OK

jeff



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

* Re: [14/32] new keywords
  2020-11-03 21:15                         ` [14/32] new keywords Nathan Sidwell
@ 2020-11-06 20:29                           ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:29 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:15 PM, Nathan Sidwell wrote:
> We have 3 new keywords.  As I mentioned in the preprocessor lexing,
> the keywords are context-sensitive, and we create internal ones. 
> These internal ones have an 'invisible' space at the end of them. 
> This has the advantage of making them return to normal identifiers in
> preprocessor output.
>
>
> 14-family-keywords.diff
>
OK

jeff



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

* Re: [19/32] global trees
  2020-11-03 21:16                                   ` [19/32] global trees Nathan Sidwell
@ 2020-11-06 20:29                                     ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:29 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:16 PM, Nathan Sidwell wrote:
> We directly reference global trees, and expect them to be immutable.
> This reorders the global tree arrays, moving the mutable ones after a
> High Water Mark.  Those after the HWM are not directly accessed in the
> module machinery (we'll reference by name or equivalent).
>
>
>
> 19-global-trees.diff
>
OK

jeff



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

* Re: [30/32] test harness
  2020-11-03 21:18                                                         ` [30/32] test harness Nathan Sidwell
@ 2020-11-06 20:30                                                           ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-06 20:30 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:18 PM, Nathan Sidwell wrote:
> Here's the test harness change, adding a few more prune cases.
>
>
> 30-test-harness.diff
>
OK

jeff



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

* Re: [04/32] cpp lexer
  2020-11-06 20:23         ` Jeff Law
@ 2020-11-06 21:06           ` Nathan Sidwell
  2020-11-09  6:15             ` Boris Kolpackov
  2020-11-09 13:54             ` Nathan Sidwell
  0 siblings, 2 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-06 21:06 UTC (permalink / raw)
  To: Jeff Law, GCC Patches, Jason Merrill, Richard Biener

On 11/6/20 3:23 PM, Jeff Law wrote:

>>
>> 04-cpp-lexer.diff
>>
>> diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
>> index 8e398863cf6..81be6457951 100644
>> --- c/libcpp/include/cpplib.h
>> +++ w/libcpp/include/cpplib.h
>>
>> @@ -888,9 +915,9 @@ struct GTY(()) cpp_hashnode {
>>     unsigned int directive_index : 7;	/* If is_directive,
>>   					   then index into directive table.
>>   					   Otherwise, a NODE_OPERATOR.  */
>> -  unsigned char rid_code;		/* Rid code - for front ends.  */
>> +  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
>> +  unsigned int flags : 9;		/* CPP flags.  */
>>     ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
>> -  unsigned int flags : 8;		/* CPP flags.  */
>>   
>>     /* 6 bits spare (plus another 32 on 64-bit hosts).  */
> 
> Someone already mentioned it, but the # of spare bits needs updating.

yeah, andit was me manually editing a patch wot flubbed it.

>> +  /* Enter directives mode for the peeking.  */
>> +  pfile->state.in_deferred_pragma = true;
>> +  pfile->state.pragma_allow_expansion = true;
>> +  pfile->state.save_comments = 0;
>> +  pfile->directive_line = result->src_loc;
> It looks like you slam in known values when you drop out of directive 
> mode rather than restoring the original values.  That may be OK, I'm not 
> all that familiar with this code to know if that's corerct or not.

Looks like comments would be useful, and maybe asserts.  You only get to 
call the peeking when you were in a known (non-directives) state.  I 
wanted the peeking to be as cheap as possible, so saving and restoring 
did not seem needed.


>> +
>> +  if (__builtin_expect (node == n_modules[spec_nodes::M__IMPORT][0], false))
>> +    /* __import  */
>> +    header_count = backup + 2 + 16;
>> +  else if (__builtin_expect (node == n_modules[spec_nodes::M_IMPORT][0], false))
>> +    /* import  */
>> +    header_count = backup + 2 + (CPP_OPTION (pfile, preprocessed) ? 16 : 0);
>> +  else if (__builtin_expect (node == n_modules[spec_nodes::M_MODULE][0], false))
>> +    ; /* module  */
>> +  else
>> +    goto not_module;
> 
> Are the __builtin_expects really useful here?  If not I'd prefer to 
> avoid them and just let the predictors do their job. Similarly  
> elsewhere in this patch.

I'll investigate.

>> +    not_module:
>> +      /* Drop out of directive mode.  */
>> +      pfile->state.save_comments
>> +	= !CPP_OPTION (pfile, discard_comments);
>> +      pfile->state.in_deferred_pragma = false;
>> +      pfile->state.angled_headers = false;
> 
> This doesn't seem to match the code to go into directives mode all that 
> well.  You don't reset pragma_allow_expansion or directive_line.

Hm, yeah.  I think neither of those fields have any bearing in 
non-directives mode, and always get set when entering it?  Again, 
comment at the very least.

nathan

-- 
Nathan Sidwell

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

* Re: [04/32] cpp lexer
  2020-11-06 21:06           ` Nathan Sidwell
@ 2020-11-09  6:15             ` Boris Kolpackov
  2020-11-09 13:54             ` Nathan Sidwell
  1 sibling, 0 replies; 68+ messages in thread
From: Boris Kolpackov @ 2020-11-09  6:15 UTC (permalink / raw)
  To: gcc-patches

Nathan Sidwell <nathan@acm.org> writes:

> > This doesn't seem to match the code to go into directives mode all that 
> > well.? You don't reset pragma_allow_expansion or directive_line.
> 
> Hm, yeah.  I think neither of those fields have any bearing in 
> non-directives mode, and always get set when entering it?  Again, 
> comment at the very least.

FWIW, I remember wrestling with that login in my branch, here are the
changes to directives-only.c (in particular, see the "Handle import 
as pseudo-directive (P1703R0)" commit):

https://github.com/boris-kolpackov/gcc-cxx-modules-ex/commits/c%2B%2B-modules-ex/libcpp/directives-only.c

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

* Re: [24/32] module mapper
  2020-11-03 21:17                                             ` [24/32] module mapper Nathan Sidwell
@ 2020-11-09  6:42                                               ` Boris Kolpackov
  2020-11-09 15:39                                                 ` Nathan Sidwell
  2020-11-12 16:24                                               ` Nathan Sidwell
  1 sibling, 1 reply; 68+ messages in thread
From: Boris Kolpackov @ 2020-11-09  6:42 UTC (permalink / raw)
  To: gcc-patches

I've noticed the following issues with the module mapper in the
-fdirectives-only mode:


1. When partially preprocessing the module interface unit, the mapper
   receives the MODULE-EXPORT request that's unnecessary (BMI is not
   written):

   g++ ... -x c++ -E -fdirectives-only -o hello.gcm.ii hello.mxx

   Similarly, in this mode, the mapper receives MODULE-IMPORT for
   (non-header) module imports. Again, this is not necessary and
   replying with a non-existent BMI works.


2. When doing full preprocessing of a partially preprocessed unit,
   the mapper again receives MODULE-EXPORT and MODULE-IMPORT for
   non-header modules:

   g++-m ... -E -x c++ -fpreprocessed -fdirectives-only hello.gcm.ii

   These are also unnecessary.

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

* Re: [04/32] cpp lexer
  2020-11-06 21:06           ` Nathan Sidwell
  2020-11-09  6:15             ` Boris Kolpackov
@ 2020-11-09 13:54             ` Nathan Sidwell
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-09 13:54 UTC (permalink / raw)
  To: Jeff Law, GCC Patches, Richard Biener

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

Jeff,
here is an updated patch with changelog.  I've added checking_asserts 
and comments for the state changes you were concerned about.

While the __builtin_expects do make a change to generated code, you are 
probably right that they are not significant and I have removed them -- 
cpplib tends to sprinkle them liberally and I guess I got infected.

I trust this addresses your concerns.

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 04a-cpp-lexer.diff --]
[-- Type: text/x-patch, Size: 20898 bytes --]

	libcpp/
	* include/cpplib.h (struct cpp_options): Add module_directives
	option.
	(NODE_MODULE): New node flag.
	(cpp_hashnode): Make rid_code bitfield, increase flag bits.
	* internal.h (struct lexer_state): Add directive_file_token field.
	(struct spec_nodes): Add module enum, n_modules field.
	* init.c (post_options): Initialize modules fields of spec_nodes.
	* lex.c (cpp_maybe_module_directive): New.
	(_cpp_lex_token): Call it.
	(cpp_output_token): Add CPP_HEADER_NAME functionality.
	(do_peek_ident, do_peek_module): New.
	(cpp_directive_only_process): Add module control line detection.
	* macro.c (cpp_get_token_1): Add directive_file_token for module
	control lines.
	gcc/c-family/
	* c-lex.c (c_lex_with_flags): Accept CPP_HEADER_NAME.

diff --git c/libcpp/include/cpplib.h w/libcpp/include/cpplib.h
index c4d7cc520d1..eb82599dd22 100644
--- c/libcpp/include/cpplib.h
+++ w/libcpp/include/cpplib.h
@@ -487,6 +494,9 @@ struct cpp_options
   /* Nonzero for the '::' token.  */
   unsigned char scope;
 
+  /* Nonzero means tokenize C++20 module directives.  */
+  unsigned char module_directives;
+
   /* Holds the name of the target (execution) character set.  */
   const char *narrow_charset;
 
@@ -831,6 +857,7 @@ struct GTY(()) cpp_macro {
 #define NODE_USED	(1 << 5)	/* Dumped with -dU.  */
 #define NODE_CONDITIONAL (1 << 6)	/* Conditional macro */
 #define NODE_WARN_OPERATOR (1 << 7)	/* Warn about C++ named operator.  */
+#define NODE_MODULE (1 << 8)		/* C++-20 module-related name.  */
 
 /* Different flavors of hash node.  */
 enum node_type
@@ -888,11 +915,11 @@ struct GTY(()) cpp_hashnode {
   unsigned int directive_index : 7;	/* If is_directive,
 					   then index into directive table.
 					   Otherwise, a NODE_OPERATOR.  */
-  unsigned char rid_code;		/* Rid code - for front ends.  */
+  unsigned int rid_code : 8;		/* Rid code - for front ends.  */
+  unsigned int flags : 9;		/* CPP flags.  */
   ENUM_BITFIELD(node_type) type : 2;	/* CPP node type.  */
-  unsigned int flags : 8;		/* CPP flags.  */
 
-  /* 6 bits spare (plus another 32 on 64-bit hosts).  */
+  /* 5 bits spare (plus another 32 on 64-bit hosts).  */
 
   union _cpp_hashnode_value GTY ((desc ("%1.type"))) value;
 };
diff --git c/libcpp/internal.h w/libcpp/internal.h
index d7780e49d27..60a0c194a7d 100644
--- c/libcpp/internal.h
+++ w/libcpp/internal.h
@@ -280,6 +280,9 @@ struct lexer_state
   /* Nonzero when tokenizing a deferred pragma.  */
   unsigned char in_deferred_pragma;
 
+  /* Count to token that is a header-name.  */
+  unsigned char directive_file_token;
+
   /* Nonzero if the deferred pragma being handled allows macro expansion.  */
   unsigned char pragma_allow_expansion;
 };
@@ -292,6 +295,12 @@ struct spec_nodes
   cpp_hashnode *n_false;		/* C++ keyword false */
   cpp_hashnode *n__VA_ARGS__;		/* C99 vararg macros */
   cpp_hashnode *n__VA_OPT__;		/* C++ vararg macros */
+
+  enum {M_EXPORT, M_MODULE, M_IMPORT, M__IMPORT, M_HWM};
+  
+  /* C++20 modules, only set when module_directives is in effect.
+     incoming variants [0], outgoing ones [1] */
+  cpp_hashnode *n_modules[M_HWM][2];
 };
 
 typedef struct _cpp_line_note _cpp_line_note;
diff --git c/libcpp/init.c w/libcpp/init.c
index dcf1d4be587..2266fff17ff 100644
--- c/libcpp/init.c
+++ w/libcpp/init.c
@@ -841,4 +856,27 @@ post_options (cpp_reader *pfile)
       CPP_OPTION (pfile, trigraphs) = 0;
       CPP_OPTION (pfile, warn_trigraphs) = 0;
     }
+
+  if (CPP_OPTION (pfile, module_directives))
+    {
+      /* These unspellable tokens have a leading space.  */
+      const char *const inits[spec_nodes::M_HWM]
+	= {"export ", "module ", "import ", "__import"};
+
+      for (int ix = 0; ix != spec_nodes::M_HWM; ix++)
+	{
+	  cpp_hashnode *node = cpp_lookup (pfile, UC (inits[ix]),
+					   strlen (inits[ix]));
+
+	  /* Token we pass to the compiler.  */
+	  pfile->spec_nodes.n_modules[ix][1] = node;
+
+	  if (ix != spec_nodes::M__IMPORT)
+	    /* Token we recognize when lexing, drop the trailing ' '.  */
+	    node = cpp_lookup (pfile, NODE_NAME (node), NODE_LEN (node) - 1);
+
+	  node->flags |= NODE_MODULE;
+	  pfile->spec_nodes.n_modules[ix][0] = node;
+	}
+    }
 }
diff --git c/libcpp/lex.c w/libcpp/lex.c
index f58a8828124..b9c10b399dc 100644
--- c/libcpp/lex.c
+++ w/libcpp/lex.c
@@ -2615,6 +2622,151 @@ _cpp_temp_token (cpp_reader *pfile)
   return result;
 }
 
+/* We're at the beginning of a logical line (so not in
+  directives-mode) and RESULT is a CPP_NAME with NODE_MODULE set.  See
+  if we should enter deferred_pragma mode to tokenize the rest of the
+  line as a module control-line.  */
+
+static void
+cpp_maybe_module_directive (cpp_reader *pfile, cpp_token *result)
+{
+  unsigned backup = 0; /* Tokens we peeked.  */
+  cpp_hashnode *node = result->val.node.node;
+  cpp_token *peek = result;
+  cpp_token *keyword = peek;
+  cpp_hashnode *(&n_modules)[spec_nodes::M_HWM][2] = pfile->spec_nodes.n_modules;
+  int header_count = 0;
+
+  /* Make sure the incoming state is as we expect it.  This way we
+     can restore it using constants.  */
+  gcc_checking_assert (!pfile->state.in_deferred_pragma
+		       && !pfile->state.skipping
+		       && !pfile->state.parsing_args
+		       && !pfile->state.angled_headers
+		       && (pfile->state.save_comments
+			   == !CPP_OPTION (pfile, discard_comments)));
+
+  /* Enter directives mode sufficiently for peeking.  We don't have
+     to actually set in_directive.  */
+  pfile->state.in_deferred_pragma = true;
+
+  /* These two fields are needed to process tokenization in deferred
+     pragma mode.  They are not used outside deferred pragma mode or
+     directives mode.  */
+  pfile->state.pragma_allow_expansion = true;
+  pfile->directive_line = result->src_loc;
+
+  /* Saving comments is incompatible with directives mode.   */
+  pfile->state.save_comments = 0;
+
+  if (node == n_modules[spec_nodes::M_EXPORT][0])
+    {
+      peek = _cpp_lex_direct (pfile);
+      keyword = peek;
+      backup++;
+      if (keyword->type != CPP_NAME)
+	goto not_module;
+      node = keyword->val.node.node;
+      if (!(node->flags & NODE_MODULE))
+	goto not_module;
+    }
+
+  if (node == n_modules[spec_nodes::M__IMPORT][0])
+    /* __import  */
+    header_count = backup + 2 + 16;
+  else if (node == n_modules[spec_nodes::M_IMPORT][0])
+    /* import  */
+    header_count = backup + 2 + (CPP_OPTION (pfile, preprocessed) ? 16 : 0);
+  else if (node == n_modules[spec_nodes::M_MODULE][0])
+    ; /* module  */
+  else
+    goto not_module;
+
+  /* We've seen [export] {module|import|__import}.  Check the next token.  */
+  if (header_count)
+    /* After '{,__}import' a header name may appear.  */
+    pfile->state.angled_headers = true;
+  peek = _cpp_lex_direct (pfile);
+  backup++;
+
+  /* ... import followed by identifier, ':', '<' or
+     header-name preprocessing tokens, or module
+     followed by cpp-identifier, ':' or ';' preprocessing
+     tokens.  C++ keywords are not yet relevant.  */
+  if (peek->type == CPP_NAME
+      || peek->type == CPP_COLON
+      ||  (header_count
+	   ? (peek->type == CPP_LESS
+	      || (peek->type == CPP_STRING && peek->val.str.text[0] != 'R')
+	      || peek->type == CPP_HEADER_NAME)
+	   : peek->type == CPP_SEMICOLON))
+    {
+      pfile->state.pragma_allow_expansion = !CPP_OPTION (pfile, preprocessed);
+      if (!pfile->state.pragma_allow_expansion)
+	pfile->state.prevent_expansion++;
+
+      if (!header_count && linemap_included_from
+	  (LINEMAPS_LAST_ORDINARY_MAP (pfile->line_table)))
+	cpp_error_with_line (pfile, CPP_DL_ERROR, keyword->src_loc, 0,
+			     "module control-line cannot be in included file");
+
+      /* The first one or two tokens cannot be macro names.  */
+      for (int ix = backup; ix--;)
+	{
+	  cpp_token *tok = ix ? keyword : result;
+	  cpp_hashnode *node = tok->val.node.node;
+
+	  /* Don't attempt to expand the token.  */
+	  tok->flags |= NO_EXPAND;
+	  if (_cpp_defined_macro_p (node)
+	      && _cpp_maybe_notify_macro_use (pfile, node, tok->src_loc)
+	      && !cpp_fun_like_macro_p (node))
+	    cpp_error_with_line (pfile, CPP_DL_ERROR, tok->src_loc, 0, 
+				 "module control-line \"%s\" cannot be"
+				 " an object-like macro",
+				 NODE_NAME (node));
+	}
+
+      /* Map to underbar variants.  */
+      keyword->val.node.node = n_modules[header_count
+					 ? spec_nodes::M_IMPORT
+					 : spec_nodes::M_MODULE][1];
+      if (backup != 1)
+	result->val.node.node = n_modules[spec_nodes::M_EXPORT][1];
+
+      /* Maybe tell the tokenizer we expect a header-name down the
+	 road.  */
+      pfile->state.directive_file_token = header_count;
+    }
+  else
+    {
+    not_module:
+      /* Drop out of directive mode.  */
+      /* We aaserted save_comments had this value upon entry.  */
+      pfile->state.save_comments
+	= !CPP_OPTION (pfile, discard_comments);
+      pfile->state.in_deferred_pragma = false;
+      /* Do not let this remain on.  */
+      pfile->state.angled_headers = false;
+    }
+
+  /* In either case we want to backup the peeked tokens.  */
+  if (backup)
+    {
+      /* If we saw EOL, we should drop it, because this isn't a module
+	 control-line after all.  */
+      bool eol = peek->type == CPP_PRAGMA_EOL;
+      if (!eol || backup > 1)
+	{
+	  /* Put put the peeked tokens back  */
+	  _cpp_backup_tokens_direct (pfile, backup);
+	  /* But if the last one was an EOL, forget it.  */
+	  if (eol)
+	    pfile->lookaheads--;
+	}
+    }
+}
+
 /* Lex a token into RESULT (external interface).  Takes care of issues
    like directive handling, token lookahead, multiple include
    optimization and skipping.  */
@@ -2663,6 +2815,21 @@ _cpp_lex_token (cpp_reader *pfile)
 	    }
 	  else if (pfile->state.in_deferred_pragma)
 	    result = &pfile->directive_result;
+	  else if (result->type == CPP_NAME
+		   && (result->val.node.node->flags & NODE_MODULE)
+		   && !pfile->state.skipping
+		   /* Unlike regular directives, we do not deal with
+		      tokenizing module directives as macro arguments.
+		      That's not permitted.  */
+		   && !pfile->state.parsing_args)
+	    {
+	      /* P1857.  Before macro expansion, At start of logical
+		 line ... */
+	      /* We don't have to consider lookaheads at this point.  */
+	      gcc_checking_assert (!pfile->lookaheads);
+
+	      cpp_maybe_module_directive (pfile, result);
+	    }
 
 	  if (pfile->cb.line_change && !pfile->state.skipping)
 	    pfile->cb.line_change (pfile, result, pfile->state.parsing_args);
@@ -3461,7 +3628,11 @@ cpp_output_token (const cpp_token *token, FILE *fp)
       break;
 
     case SPELL_LITERAL:
+      if (token->type == CPP_HEADER_NAME)
+	fputc ('"', fp);
       fwrite (token->val.str.text, 1, token->val.str.len, fp);
+      if (token->type == CPP_HEADER_NAME)
+	fputc ('"', fp);
       break;
 
     case SPELL_NONE:
@@ -3947,6 +4118,188 @@ do_peek_prev (const unsigned char *peek, const unsigned char *bound)
     return peek;
 }
 
+/* If PEEK[-1] is identifier MATCH, scan past it and trailing white
+   space.  Otherwise return NULL.  */
+
+static const unsigned char *
+do_peek_ident (const char *match, const unsigned char *peek,
+	       const unsigned char *limit)
+{
+  for (; *++match; peek++)
+    if (*peek != *match)
+      {
+	peek = do_peek_next (peek, limit);
+	if (*peek != *match)
+	  return NULL;
+      }
+
+  /* Must now not be looking at an identifier char.  */
+  peek = do_peek_next (peek, limit);
+  if (ISIDNUM (*peek))
+    return NULL;
+
+  /* Skip control-line whitespace.  */
+ ws:
+  while (*peek == ' ' || *peek == '\t')
+    peek++;
+  if (__builtin_expect (*peek == '\\', false))
+    {
+      peek = do_peek_backslash (peek, limit);
+      if (*peek != '\\')
+	goto ws;
+    }
+
+  return peek;
+}
+
+/* Are we looking at a module control line starting as PEEK - 1?  */
+
+static bool
+do_peek_module (cpp_reader *pfile, unsigned char c,
+		const unsigned char *peek, const unsigned char *limit)
+{
+  bool import = false;
+
+  if (__builtin_expect (c == 'e', false))
+    {
+      if (!((peek[0] == 'x' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("export", peek, limit))))
+	return false;
+
+      /* export, peek for import or module.  No need to peek __import
+	 here.  */
+      if (peek[0] == 'i')
+	{
+	  if (!((peek[1] == 'm' || peek[1] == '\\')
+		&& (peek = do_peek_ident ("import", peek + 1, limit))))
+	    return false;
+	  import = true;
+	}
+      else if (peek[0] == 'm')
+	{
+	  if (!((peek[1] == 'o' || peek[1] == '\\')
+		&& (peek = do_peek_ident ("module", peek + 1, limit))))
+	    return false;
+	}
+      else
+	return false;
+    }
+  else if (__builtin_expect (c == 'i', false))
+    {
+      if (!((peek[0] == 'm' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("import", peek, limit))))
+	return false;
+      import = true;
+    }
+  else if (__builtin_expect (c == '_', false))
+    {
+      /* Needed for translated includes.   */
+      if (!((peek[0] == '_' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("__import", peek, limit))))
+	return false;
+      import = true;
+    }
+  else if (__builtin_expect (c == 'm', false))
+    {
+      if (!((peek[0] == 'o' || peek[0] == '\\')
+	    && (peek = do_peek_ident ("module", peek, limit))))
+	return false;
+    }
+  else
+    return false;
+
+  /* Peek the next character to see if it's good enough.  We'll be at
+     the first non-whitespace char, including skipping an escaped
+     newline.  */
+  /* ... import followed by identifier, ':', '<' or header-name
+     preprocessing tokens, or module followed by identifier, ':' or
+     ';' preprocessing tokens.  */
+  unsigned char p = *peek++;
+      
+  /* A character literal is ... single quotes, ... optionally preceded
+     by u8, u, U, or L */
+  /* A string-literal is a ... double quotes, optionally prefixed by
+     R, u8, u8R, u, uR, U, UR, L, or LR */
+  if (p == 'u')
+    {
+      peek = do_peek_next (peek, limit);
+      if (*peek == '8')
+	{
+	  peek++;
+	  goto peek_u8;
+	}
+      goto peek_u;
+    }
+  else if (p == 'U' || p == 'L')
+    {
+    peek_u8:
+      peek = do_peek_next (peek, limit);
+    peek_u:
+      if (*peek == '\"' || *peek == '\'')
+	return false;
+
+      if (*peek == 'R')
+	goto peek_R;
+      /* Identifier. Ok.  */
+    }
+  else if (p == 'R')
+    {
+    peek_R:
+      if (CPP_OPTION (pfile, rliterals))
+	{
+	  peek = do_peek_next (peek, limit);
+	  if (*peek == '\"')
+	    return false;
+	}
+      /* Identifier. Ok.  */
+    }
+  else if ('Z' - 'A' == 25
+	   ? ((p >= 'A' && p <= 'Z') || (p >= 'a' && p <= 'z') || p == '_')
+	   : ISIDST (p))
+    {
+      /* Identifier.  Ok. */
+    }
+  else if (p == '<')
+    {
+      /* Maybe angle header, ok for import.  Reject
+	 '<=', '<<' digraph:'<:'.  */
+      if (!import)
+	return false;
+      peek = do_peek_next (peek, limit);
+      if (*peek == '=' || *peek == '<'
+	  || (*peek == ':' && CPP_OPTION (pfile, digraphs)))
+	return false;
+    }
+  else if (p == ';')
+    {
+      /* SEMICOLON, ok for module.  */
+      if (import)
+	return false;
+    }
+  else if (p == '"')
+    {
+      /* STRING, ok for import.  */
+      if (!import)
+	return false;
+    }
+  else if (p == ':')
+    {
+      /* Maybe COLON, ok.  Reject '::', digraph:':>'.  */
+      peek = do_peek_next (peek, limit);
+      if (*peek == ':' || (*peek == '>' && CPP_OPTION (pfile, digraphs)))
+	return false;
+    }
+  else
+    /* FIXME: Detect a unicode character, excluding those not
+       permitted as the initial character. [lex.name]/1.  I presume
+       we need to check the \[uU] spellings, and directly using
+       Unicode in say UTF8 form?  Or perhaps we do the phase-1
+       conversion of UTF8 to universal-character-names?  */
+    return false;
+
+  return true;
+}
+
 /* Directives-only scanning.  Somewhat more relaxed than correct
    parsing -- some ill-formed programs will not be rejected.  */
 
@@ -3955,6 +4308,8 @@ cpp_directive_only_process (cpp_reader *pfile,
 			    void *data,
 			    void (*cb) (cpp_reader *, CPP_DO_task, void *, ...))
 {
+  bool module_p = CPP_OPTION (pfile, module_directives);
+
   do
     {
     restart:
@@ -4347,6 +4702,51 @@ cpp_directive_only_process (cpp_reader *pfile,
 	      }
 	      goto dflt;
 
+	    case '_':
+	    case 'e':
+	    case 'i':
+	    case 'm':
+	      if (bol && module_p && !pfile->state.skipping
+		  && do_peek_module (pfile, c, pos, limit))
+		{
+		  /* We've seen the start of a module control line.
+		     Start up the tokenizer.  */
+		  pos--; /* Backup over the first character.  */
+
+		  /* Backup over whitespace to start of line.  */
+		  while (pos > line_start
+			 && (pos[-1] == ' ' || pos[-1] == '\t'))
+		    pos--;
+
+		  if (pos > base)
+		    cb (pfile, CPP_DO_print, data, line_count, base, pos - base);
+
+		  /* Prep things for directive handling. */
+		  buffer->next_line = pos;
+		  buffer->need_line = true;
+
+		  /* Now get tokens until the PRAGMA_EOL.  */
+		  do
+		    {
+		      location_t spelling;
+		      const cpp_token *tok
+			= cpp_get_token_with_location (pfile, &spelling);
+
+		      gcc_assert (pfile->state.in_deferred_pragma
+				  || tok->type == CPP_PRAGMA_EOL);
+		      cb (pfile, CPP_DO_token, data, tok, spelling);
+		    }
+		  while (pfile->state.in_deferred_pragma);
+
+		  if (pfile->buffer->next_line < pfile->buffer->rlimit)
+		    cb (pfile, CPP_DO_location, data,
+			pfile->line_table->highest_line);
+
+		  pfile->mi_valid = false;
+		  goto restart;
+		}
+	      goto dflt;
+
 	    default:
 	    dflt:
 	      bol = false;
diff --git c/libcpp/macro.c w/libcpp/macro.c
index e2cb89e4c43..d313341e91b 100644
--- c/libcpp/macro.c
+++ w/libcpp/macro.c
@@ -2959,6 +2961,85 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location)
     }
 
   pfile->about_to_expand_macro_p = saved_about_to_expand_macro;
+
+  if (pfile->state.directive_file_token
+      && !pfile->state.parsing_args
+      && !(result->type == CPP_PADDING || result->type == CPP_COMMENT)
+      && !(15 & --pfile->state.directive_file_token))
+    {
+      /* Do header-name frobbery.  Concatenate < ... > as approprate.
+	 Do header search if needed, and finally drop the outer <> or
+	 "".  */
+      pfile->state.angled_headers = false;
+
+      /* Do angle-header reconstitution.  Then do include searching.
+	 We'll always end up with a ""-quoted header-name in that
+	 case.  If searching finds nothing, we emit a diagnostic and
+	 an empty string.  */
+      size_t len = 0;
+      char *fname = NULL;
+
+      cpp_token *tmp = _cpp_temp_token (pfile);
+      *tmp = *result;
+
+      tmp->type = CPP_HEADER_NAME;
+      bool need_search = !pfile->state.directive_file_token;
+      pfile->state.directive_file_token = 0;
+
+      bool angle = result->type != CPP_STRING;
+      if (result->type == CPP_HEADER_NAME
+	  || (result->type == CPP_STRING && result->val.str.text[0] != 'R'))
+	{
+	  len = result->val.str.len - 2;
+	  fname = XNEWVEC (char, len + 1);
+	  memcpy (fname, result->val.str.text + 1, len);
+	  fname[len] = 0;
+	}
+      else if (result->type == CPP_LESS)
+	fname = _cpp_bracket_include (pfile);
+
+      if (fname)
+	{
+	  /* We have a header-name.  Look it up.  This will emit an
+	     unfound diagnostic.  Canonicalize the found name.  */
+	  const char *found = fname;
+
+	  if (need_search)
+	    {
+	      found = cpp_find_header_unit (pfile, fname, angle, tmp->src_loc);
+	      if (!found)
+		found = "";
+	      len = strlen (found);
+	    }
+	  /* Force a leading './' if it's not absolute.  */
+	  bool dotme = (found[0] == '.' ? !IS_DIR_SEPARATOR (found[1])
+			: found[0] && !IS_ABSOLUTE_PATH (found));
+
+	  if (BUFF_ROOM (pfile->u_buff) < len + 1 + dotme * 2)
+	    _cpp_extend_buff (pfile, &pfile->u_buff, len + 1 + dotme * 2);
+	  unsigned char *buf = BUFF_FRONT (pfile->u_buff);
+	  size_t pos = 0;
+	      
+	  if (dotme)
+	    {
+	      buf[pos++] = '.';
+	      /* Apparently '/' is unconditional.  */
+	      buf[pos++] = '/';
+	    }
+	  memcpy (&buf[pos], found, len);
+	  pos += len;
+	  buf[pos] = 0;
+
+	  tmp->val.str.len = pos;
+	  tmp->val.str.text = buf;
+
+	  tmp->type = CPP_HEADER_NAME;
+	  XDELETEVEC (fname);
+	  
+	  result = tmp;
+	}
+    }
+
   return result;
 }
 
diff --git c/gcc/c-family/c-lex.c w/gcc/c-family/c-lex.c
index e81e16ddc26..44575473719 100644
--- c/gcc/c-family/c-lex.c
+++ w/gcc/c-family/c-lex.c
@@ -654,8 +656,11 @@ c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags,
       *value = build_int_cst (integer_type_node, tok->val.pragma);
       break;
 
-      /* These tokens should not be visible outside cpplib.  */
     case CPP_HEADER_NAME:
+      *value = build_string (tok->val.str.len, (const char *)tok->val.str.text);
+      break;
+
+      /* This token should not be visible outside cpplib.  */
     case CPP_MACRO_ARG:
       gcc_unreachable ();
 

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

* Re: [24/32] module mapper
  2020-11-09  6:42                                               ` Boris Kolpackov
@ 2020-11-09 15:39                                                 ` Nathan Sidwell
  2020-11-24  7:18                                                   ` Boris Kolpackov
  0 siblings, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-09 15:39 UTC (permalink / raw)
  To: gcc-patches

On 11/9/20 1:42 AM, Boris Kolpackov wrote:
> I've noticed the following issues with the module mapper in the
> -fdirectives-only mode:
> 
> 
> 1. When partially preprocessing the module interface unit, the mapper
>     receives the MODULE-EXPORT request that's unnecessary (BMI is not
>     written):
> 
>     g++ ... -x c++ -E -fdirectives-only -o hello.gcm.ii hello.mxx
> 
>     Similarly, in this mode, the mapper receives MODULE-IMPORT for
>     (non-header) module imports. Again, this is not necessary and
>     replying with a non-existent BMI works.
> 
> 
> 2. When doing full preprocessing of a partially preprocessed unit,
>     the mapper again receives MODULE-EXPORT and MODULE-IMPORT for
>     non-header modules:
> 
>     g++-m ... -E -x c++ -fpreprocessed -fdirectives-only hello.gcm.ii
> 
>     These are also unnecessary.

These are needed as they also serve to inform the mapper of a dependency 
edge.

nathan

-- 
Nathan Sidwell

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

* Re: [07/32] cpp main
  2020-11-03 21:14             ` [07/32] cpp main Nathan Sidwell
@ 2020-11-10 23:18               ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-10 23:18 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:14 PM, Nathan Sidwell wrote:
> Here's the changes to starting the main file. I have added the ability
> to search the user or system include paths for the main file.  That's
> real helpful to users attempting to build header-units.  I'll discuss
> the CLI with the options patch.
>
> Also recording the location at which the main file starts is helpful
> for whole-file diagnostics.
>
>
>
> 07-cpp-main.diff
>
OK with ChangeLog entry.

jeff



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

* Re: [08/32] cpp mkdeps
  2020-11-03 21:14               ` [08/32] cpp mkdeps Nathan Sidwell
@ 2020-11-10 23:20                 ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-10 23:20 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:14 PM, Nathan Sidwell wrote:
> The final bit of preprocessor change is that to mkdeps.  We have a new
> kind of thing to be dependent upon -- a compiled module interface.
> That's also an additional target.
>
> This adds the logic to mkdeps to keep those dependencies separate from
> include file dependencies.  I made use of this when demonstrating a
> module-aware Make POC.
>
>
>
> 08-cpp-mkdeps.diff
>
OK with ChangeLog entry.

jeff



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

* Re: [24/32] module mapper
  2020-11-03 21:17                                             ` [24/32] module mapper Nathan Sidwell
  2020-11-09  6:42                                               ` Boris Kolpackov
@ 2020-11-12 16:24                                               ` Nathan Sidwell
  2020-11-30 16:18                                                 ` Jeff Law
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-12 16:24 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill, Richard Biener

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

On 11/3/20 4:17 PM, Nathan Sidwell wrote:
> this is the module mapper client and server pieces.  It features a 
> default resolver that can read a text file, or generate default mappings 
> from module name to cmi name.

Richard rightly suggested on IRC that the sample server for the module 
mapper shouldn't be in the gcc/cp dir.  It happened to be that way 
because it started out much more closely coupled, but then it grew legs.

So this patch creates a new c++tools toplevel directory and places the 
mapper-server and its default resolver there.  That means more changes 
to the toplevel Makefile.def and Makefile.tpl (I've not included the 
regenerated Makefile.in, nor other generated files in gcc/ and c++tools 
in this diff.)

We still need to build the default resolver when building cc1plus, and 
I've placed mapper-resolver.cc there, as a simple #include forwarder to 
the source in c++tools.  I also replace 'gcc/cp/mapper.h' with a 
client-specific 'gcc/cp/mapper-client.h'.  (mapper-client is only linked 
into cc1plus, so gcc/cp seems the right place for it.)

The sample server relies on gcc/version.o to pick up its version number, 
and I place it in the libexecsubdir that we place cc1plus.  I wasn't 
comfortable placing it in the install location of g++ itself.  I call it 
a sample server for a reason :)

I will of course provide changelog when committing.

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 24a-c++-mapper.diff --]
[-- Type: text/x-patch, Size: 56027 bytes --]

diff --git c/Makefile.def w/Makefile.def
index 36fd26b0367..6e98d2d3340 100644
--- c/Makefile.def
+++ w/Makefile.def
@@ -125,12 +134,13 @@ host_modules= { module= libtermcap; no_check=true;
                 missing=distclean;
                 missing=maintainer-clean; };
 host_modules= { module= utils; no_check=true; };
+host_modules= { module= c++tools; };
 host_modules= { module= gnattools; };
+host_modules= { module= gotools; };
 host_modules= { module= lto-plugin; bootstrap=true;
 		extra_configure_flags='--enable-shared @extra_linker_plugin_flags@ @extra_linker_plugin_configure_flags@';
 		extra_make_flags='@extra_linker_plugin_flags@'; };
 host_modules= { module= libcc1; extra_configure_flags=--enable-shared; };
-host_modules= { module= gotools; };
 host_modules= { module= libctf; no_install=true; no_check=true;
 		bootstrap=true; };
 
@@ -381,6 +392,8 @@ dependencies = { module=all-lto-plugin; on=all-libiberty-linker-plugin; };
 dependencies = { module=configure-libcc1; on=configure-gcc; };
 dependencies = { module=all-libcc1; on=all-gcc; };
 
+// we want version.o from gcc, and implicitly depend on libcody
+dependencies = { module=all-c++tools; on=all-gcc; };
 dependencies = { module=all-gotools; on=all-target-libgo; };
 
 dependencies = { module=all-utils; on=all-libiberty; };
diff --git c/Makefile.tpl w/Makefile.tpl
index efed1511750..3b88f351d5b 100644
--- c/Makefile.tpl
+++ w/Makefile.tpl
@@ -864,8 +864,8 @@ local-distclean:
 	-rm -f texinfo/doc/Makefile texinfo/po/POTFILES
 	-rmdir texinfo/doc texinfo/info texinfo/intl texinfo/lib 2>/dev/null
 	-rmdir texinfo/makeinfo texinfo/po texinfo/util 2>/dev/null
-	-rmdir fastjar gcc gnattools gotools libcc1 libiberty 2>/dev/null
-	-rmdir texinfo zlib 2>/dev/null
+	-rmdir c++tools fastjar gcc gnattools gotools 2>/dev/null
+	-rmdir libcc1 libiberty texinfo zlib 2>/dev/null
 	-find . -name config.cache -exec rm -f {} \; \; 2>/dev/null
 
 local-maintainer-clean:
diff --git c/c++tools/configure.ac w/c++tools/configure.ac
new file mode 100644
index 00000000000..8d882e541df
--- /dev/null
+++ w/c++tools/configure.ac
@@ -0,0 +1,210 @@
+# Configure script for c++tools
+#   Copyright (C) 2020 Free Software Foundation, Inc.
+#   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+#
+# This file 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# C++ has grown a C++20 mapper server.  This may be used to provide
+# and/or learn and/or build required modules.  This sample server
+# shows how the protocol introduced by wg21.link/p1184 may be used.
+# By default g++ uses an in-process mapper.
+
+sinclude(../config/acx.m4)
+
+AC_INIT(c++tools)
+
+AC_CONFIG_SRCDIR([server.cc])
+
+# Determine the noncanonical names used for directories.
+ACX_NONCANONICAL_HOST
+
+AC_CANONICAL_SYSTEM
+AC_PROG_INSTALL
+
+AC_PROG_CXX
+MISSING=`cd $ac_aux_dir && ${PWDCMD-pwd}`/missing
+AC_CHECK_PROGS([AUTOCONF], [autoconf], [$MISSING autoconf])
+AC_CHECK_PROGS([AUTOHEADER], [autoheader], [$MISSING autoheader])
+
+dnl Enabled by default
+AC_MSG_CHECKING([whether to build C++ tools])
+  AC_ARG_ENABLE(c++-tools, 
+    [AS_HELP_STRING([--enable-c++-tools],
+		    [build auxiliary c++ tools])],
+      cxx_aux_tools=$enableval,
+      cxx_aux_tools=yes)
+
+AC_MSG_RESULT($cxx_aux_tools)
+CXX_AUX_TOOLS="$cxx_aux_tools"
+AC_SUBST(CXX_AUX_TOOLS)
+
+AC_ARG_ENABLE([maintainer-mode],
+AS_HELP_STRING([--enable-maintainer-mode],
+[enable maintainer mode.  Add rules to rebuild configurey bits]),,
+[enable_maintainer_mode=no])
+case "$enable_maintainer_mode" in
+  ("yes") maintainer_mode=yes ;;
+  ("no") maintainer=no ;;
+  (*) AC_MSG_ERROR([unknown maintainer mode $enable_maintainer_mode]) ;;
+esac
+AC_MSG_CHECKING([maintainer-mode])
+AC_MSG_RESULT([$maintainer_mode])
+test "$maintainer_mode" = yes && MAINTAINER=yes
+AC_SUBST(MAINTAINER)
+
+# Check if O_CLOEXEC is defined by fcntl
+AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>]], [[
+return open ("/dev/null", O_RDONLY | O_CLOEXEC);]])],
+[ac_cv_o_cloexec=yes],[ac_cv_o_cloexec=no])])
+if test $ac_cv_o_cloexec = yes; then
+  AC_DEFINE(HOST_HAS_O_CLOEXEC, 1,
+  [Define if O_CLOEXEC supported by fcntl.])
+fi
+
+# C++ Modules would like some networking features to provide the mapping
+# server.  You can still use modules without them though.
+# The following network-related checks could probably do with some
+# Windows and other non-linux defenses and checking.
+
+# Local socket connectivity wants AF_UNIX networking
+# Check for AF_UNIX networking
+AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>]],[[
+sockaddr_un un;
+un.sun_family = AF_UNSPEC;
+int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&un, sizeof (un));]])],
+[ac_cv_af_unix=yes],
+[ac_cv_af_unix=no])])
+if test $ac_cv_af_unix = yes; then
+  AC_DEFINE(HAVE_AF_UNIX, 1,
+  [Define if AF_UNIX supported.])
+fi
+
+# Remote socket connectivity wants AF_INET6 networking
+# Check for AF_INET6 networking
+AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>]],[[
+sockaddr_in6 in6;
+in6.sin6_family = AF_UNSPEC;
+struct addrinfo *addrs = 0;
+struct addrinfo hints;
+hints.ai_flags = 0;
+hints.ai_family = AF_INET6;
+hints.ai_socktype = SOCK_STREAM;
+hints.ai_protocol = 0;
+hints.ai_canonname = 0;
+hints.ai_addr = 0;
+hints.ai_next = 0;
+int e = getaddrinfo ("localhost", 0, &hints, &addrs);
+const char *str = gai_strerror (e);
+freeaddrinfo (addrs);
+int fd = socket (AF_INET6, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&in6, sizeof (in6));]])],
+[ac_cv_af_inet6=yes],
+[ac_cv_af_inet6=no])])
+if test $ac_cv_af_inet6 = yes; then
+  AC_DEFINE(HAVE_AF_INET6, 1,
+  [Define if AF_INET6 supported.])
+fi
+
+# Efficient server response wants epoll
+# Check for epoll_create, epoll_ctl, epoll_pwait
+AC_CACHE_CHECK(for epoll, ac_cv_epoll, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/epoll.h>]],[[
+int fd = epoll_create (1);
+epoll_event ev;
+ev.events = EPOLLIN;
+ev.data.fd = 0;
+epoll_ctl (fd, EPOLL_CTL_ADD, 0, &ev);
+epoll_pwait (fd, 0, 0, -1, 0);]])],
+[ac_cv_epoll=yes],
+[ac_cv_epoll=no])])
+if test $ac_cv_epoll = yes; then
+  AC_DEFINE(HAVE_EPOLL, 1,
+  [Define if epoll_create, epoll_ctl, epoll_pwait provided.])
+fi
+
+# If we can't use epoll, try pselect.
+# Check for pselect
+AC_CACHE_CHECK(for pselect, ac_cv_pselect, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/select.h>]],[[
+pselect (0, 0, 0, 0, 0, 0);]])],
+[ac_cv_pselect=yes],
+[ac_cv_pselect=no])])
+if test $ac_cv_pselect = yes; then
+  AC_DEFINE(HAVE_PSELECT, 1,
+  [Define if pselect provided.])
+fi
+
+# And failing that, use good old select.
+# If we can't even use this, the server is serialized.
+# Check for select
+AC_CACHE_CHECK(for select, ac_cv_select, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/select.h>]],[[
+select (0, 0, 0, 0, 0);]])],
+[ac_cv_select=yes],
+[ac_cv_select=no])])
+if test $ac_cv_select = yes; then
+  AC_DEFINE(HAVE_SELECT, 1,
+  [Define if select provided.])
+fi
+
+# Avoid some fnctl calls by using accept4, when available.
+# Check for accept4
+AC_CACHE_CHECK(for accept4, ac_cv_accept4, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/socket.h>]],[[
+int err = accept4 (1, 0, 0, SOCK_NONBLOCK);]])],
+[ac_cv_accept4=yes],
+[ac_cv_accept4=no])])
+if test $ac_cv_accept4 = yes; then
+  AC_DEFINE(HAVE_ACCEPT4, 1,
+  [Define if accept4 provided.])
+fi
+
+# For better server messages, look for a way to stringize network addresses
+# Check for inet_ntop
+AC_CACHE_CHECK(for inet_ntop, ac_cv_inet_ntop, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <arpa/inet.h>
+#include <netinet/in.h>]],[[
+sockaddr_in6 in6;
+char buf[INET6_ADDRSTRLEN];
+const char *str = inet_ntop (AF_INET6, &in6, buf, sizeof (buf));]])],
+[ac_cv_inet_ntop=yes],
+[ac_cv_inet_ntop=no])])
+if test $ac_cv_inet_ntop = yes; then
+  AC_DEFINE(HAVE_INET_NTOP, 1,
+  [Define if inet_ntop provided.])
+fi
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
diff --git c/c++tools/resolver.cc w/c++tools/resolver.cc
new file mode 100644
index 00000000000..5028d2a4a37
--- /dev/null
+++ w/c++tools/resolver.cc
@@ -0,0 +1,272 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "resolver.h"
+// C++
+#include <algorithm>
+// C
+#include <cstring>
+// OS
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef DIR_SEPARATOR
+#define DIR_SEPARATOR '/'
+#endif
+
+module_resolver::module_resolver (bool map, bool xlate)
+  : default_map (map), default_translate (xlate)
+{
+}
+
+module_resolver::~module_resolver ()
+{
+  if (fd_repo >= 0)
+    close (fd_repo);
+}
+
+bool
+module_resolver::set_repo (std::string &&r, bool force)
+{
+  if (force || repo.empty ())
+    {
+      repo = std::move (r);
+      force = true;
+    }
+  return force;
+}
+
+bool
+module_resolver::add_mapping (std::string &&module, std::string &&file,
+			      bool force)
+{
+  auto res = map.emplace (std::move (module), std::move (file));
+  if (res.second)
+    force = true;
+  else if (force)
+    res.first->second = std::move (file);
+
+  return force;
+}
+
+int
+module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
+{
+  struct stat stat;
+  if (fstat (fd, &stat) < 0)
+    return -errno;
+
+  if (!stat.st_size)
+    return 0;
+
+  // Just map the file, we're gonna read all of it, so no need for
+  // line buffering
+  void *buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (buffer == MAP_FAILED)
+    return -errno;
+
+  size_t prefix_len = prefix ? strlen (prefix) : 0;
+  unsigned lineno = 0;
+
+  for (char const *begin = reinterpret_cast <char const *> (buffer),
+	 *end = begin + stat.st_size, *eol;
+       begin != end; begin = eol + 1)
+    {
+      lineno++;
+      eol = std::find (begin, end, '\n');
+      if (eol == end)
+	// last line has no \n, ignore the line, you lose
+	break;
+
+      auto *pos = begin;
+      bool pfx_search = prefix_len != 0;
+
+    pfx_search:
+      while (*pos == ' ' || *pos == '\t')
+	pos++;
+
+      auto *space = pos;
+      while (*space != '\n' && *space != ' ' && *space != '\t')
+	space++;
+
+      if (pos == space)
+	// at end of line, nothing here	
+	continue;
+
+      if (pfx_search)
+	{
+	  if (size_t (space - pos) == prefix_len
+	      && std::equal (pos, space, prefix))
+	    pfx_search = false;
+	  pos = space;
+	  goto pfx_search;
+	}
+
+      std::string module (pos, space);
+      while (*space == ' ' || *space == '\t')
+	space++;
+      std::string file (space, eol);
+
+      if (module[0] == '$')
+	{
+	  if (module == "$root")
+	    set_repo (std::move (file));
+	  else
+	    return lineno;
+	}
+      else
+	{
+	  if (file.empty ())
+	    file = GetCMIName (module);
+	  add_mapping (std::move (module), std::move (file), force);
+	}
+    }
+
+  munmap (buffer, stat.st_size);
+
+  return 0;
+}
+
+char const *
+module_resolver::GetCMISuffix ()
+{
+  return "gcm";
+}
+
+module_resolver *
+module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
+				 std::string &a, std::string &i)
+{
+  if (!version || version > Cody::Version)
+    s->ErrorResponse ("version mismatch");
+  else if (a != "GCC")
+    // Refuse anything but GCC
+    ErrorResponse (s, std::string ("only GCC supported"));
+  else if (!ident.empty () && ident != i)
+    // Failed ident check
+    ErrorResponse (s, std::string ("bad ident"));
+  else
+    // Success!
+    s->ConnectResponse ("gcc");
+
+  return this;
+}
+
+int
+module_resolver::ModuleRepoRequest (Cody::Server *s)
+{
+  s->PathnameResponse (repo);
+  return 0;
+}
+
+int
+module_resolver::cmi_response (Cody::Server *s, std::string &module)
+{
+  auto iter = map.find (module);
+  if (iter == map.end ())
+    {
+      std::string file;
+      if (default_map)
+	file = std::move (GetCMIName (module));
+      auto res = map.emplace (module, file);
+      iter = res.first;
+    }
+
+  if (iter->second.empty ())
+    s->ErrorResponse ("no such module");
+  else
+    s->PathnameResponse (iter->second);
+
+  return 0;
+}
+
+int
+module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
+				      std::string &module)
+{
+  return cmi_response (s, module);
+}
+
+int
+module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
+				      std::string &module)
+{
+  return cmi_response (s, module);
+}
+
+int
+module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
+					  std::string &include)
+{
+  auto iter = map.find (include);
+  if (iter == map.end () && default_translate)
+    {
+      // Not found, look for it
+      auto file = GetCMIName (include);
+      struct stat statbuf;
+      bool ok = true;
+
+#if HAVE_FSTATAT
+      int fd_dir = AT_FDCWD;
+      if (!repo.empty ())
+	{
+	  if (fd_repo == -1)
+	    {
+	      fd_repo = open (repo.c_str (),
+			      O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+	      if (fd_repo < 0)
+		fd_repo = -2;
+	    }
+	  fd_dir = fd_repo;
+	}
+
+      if (!repo.empty () && fd_repo < 0)
+	ok = false;
+      else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
+	       || !S_ISREG (statbuf.st_mode))
+	ok = false;
+#else
+      auto append = repo;
+      append.push_back (DIR_SEPARATOR);
+      append.append (file);
+      if (stat (append.c_str (), &statbuf) < 0
+	  || !S_ISREG (statbuf.st_mode))
+	ok = false;
+#endif
+      if (!ok)
+	// Mark as not present
+	file.clear ();
+      auto res = map.emplace (include, file);
+      iter = res.first;
+    }
+
+  if (iter == map.end () || iter->second.empty ())
+    s->BoolResponse (false);
+  else
+    s->PathnameResponse (iter->second);
+
+  return 0;
+}
+
diff --git c/c++tools/resolver.h w/c++tools/resolver.h
new file mode 100644
index 00000000000..19339125b26
--- /dev/null
+++ w/c++tools/resolver.h
@@ -0,0 +1,105 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 GXX_RESOLVER_H
+#define GXX_RESOLVER_H 1
+
+// Mapper interface for client and server bits
+#include "cody.hh"
+// C++
+#include <string>
+#include <map>
+
+// This is a GCC class, so GCC coding conventions on new bits.  
+class module_resolver : public Cody::Resolver
+{
+public:
+  using parent = Cody::Resolver;
+  using module_map = std::map<std::string, std::string>;
+
+private:
+  std::string repo;
+  std::string ident;
+  module_map map;
+  int fd_repo = -1;
+  bool default_map = true;
+  bool default_translate = true;
+
+public:
+  module_resolver (bool map = true, bool xlate = false);
+  virtual ~module_resolver () override;
+
+public:
+  void set_default_map (bool d)
+  {
+    default_map = d;
+  }
+  void set_default_translate (bool d)
+  {
+    default_translate = d;
+  }
+  void set_ident (char const *i)
+  {
+    ident = i;
+  }
+  bool set_repo (std::string &&repo, bool force = false);
+  bool add_mapping (std::string &&module, std::string &&file,
+		    bool force = false);
+
+  // Return +ve line number of error, or -ve errno
+  int read_tuple_file (int fd, char const *prefix, bool force = false);
+  int read_tuple_file (int fd, std::string const &prefix,
+			    bool force = false)
+  {
+    return read_tuple_file (fd, prefix.empty () ? nullptr : prefix.c_str (),
+			    force);
+  }
+
+public:
+  // Virtual overriders, names are controlled by Cody::Resolver
+  using parent::ConnectRequest;
+  virtual module_resolver *ConnectRequest (Cody::Server *, unsigned version,
+					   std::string &agent,
+					   std::string &ident)
+    override;
+  using parent::ModuleRepoRequest;
+  virtual int ModuleRepoRequest (Cody::Server *) override;
+  using parent::ModuleExportRequest;
+  virtual int ModuleExportRequest (Cody::Server *s, Cody::Flags,
+				   std::string &module)
+    override;
+  using parent::ModuleImportRequest;
+  virtual int ModuleImportRequest (Cody::Server *s, Cody::Flags,
+				   std::string &module)
+    override;
+  using parent::IncludeTranslateRequest;
+  virtual int IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
+				       std::string &include)
+    override;
+
+private:
+  using parent::GetCMISuffix;
+  virtual char const *GetCMISuffix () override;
+
+private:
+  int cmi_response (Cody::Server *s, std::string &module);
+};
+
+#endif
diff --git c/c++tools/server.cc w/c++tools/server.cc
new file mode 100644
index 00000000000..6457dc5b878
--- /dev/null
+++ w/c++tools/server.cc
@@ -0,0 +1,976 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2018-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "resolver.h"
+
+// C++
+#include <set>
+#include <vector>
+#include <map>
+// C
+#include <csignal>
+#include <cstring>
+#include <cstdarg>
+// OS
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+// Network
+/* Include network stuff first.  Excitingly OSX10.14 uses bcmp here, which
+   we poison later!  */
+#if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
+/* socket, bind, listen, accept{4}  */
+# define NETWORKING 1
+# include <sys/socket.h>
+# ifdef HAVE_AF_UNIX
+/* sockaddr_un  */
+#  include <sys/un.h>
+# endif
+# include <netinet/in.h>
+# ifdef HAVE_AF_INET6
+/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons.  */
+#  include <netdb.h>
+# endif
+#ifdef HAVE_INET_NTOP
+/* inet_ntop.  */
+#include <arpa/inet.h>
+#endif
+#endif
+#ifndef HAVE_AF_INET6
+# define gai_strerror(X) ""
+#endif
+
+#include <getopt.h>
+
+// Select or epoll
+#ifdef NETWORKING
+#ifdef HAVE_EPOLL
+/* epoll_create, epoll_ctl, epoll_pwait  */
+#include <sys/epoll.h>
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+/* pselect or select  */
+#include <sys/select.h>
+#endif
+#endif
+
+// GCC
+#include "version.h"
+#include "ansidecl.h"
+#define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac.  */
+#include "libiberty.h"
+
+#if !HOST_HAS_O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef IS_DIR_SEPARATOR
+#define IS_DIR_SEPARATOR(C) ((C) == '/')
+#endif
+#ifndef DIR_SEPARATOR
+#define DIR_SEPARATOR '/'
+#endif
+
+#ifdef NETWORKING
+struct netmask {
+  in6_addr addr;
+  unsigned bits;
+
+  netmask (const in6_addr &a, unsigned b)
+  {
+    if (b > sizeof (in6_addr) * 8)
+      b = sizeof (in6_addr) * 8;
+    bits = b;
+    unsigned byte = (b + 7) / 8;
+    unsigned ix = 0;
+    for (ix = 0; ix < byte; ix++)
+      addr.s6_addr[ix] = a.s6_addr[ix];
+    for (; ix != sizeof (in6_addr); ix++)
+      addr.s6_addr[ix] = 0;
+    if (b & 3)
+      addr.s6_addr[b/7] &= (255 << 8) >> (b & 3);
+  }
+
+  bool includes (const in6_addr &a) const
+  {
+    unsigned byte = bits / 8;
+    for (unsigned ix = 0; ix != byte; ix++)
+      if (addr.s6_addr[ix] != a.s6_addr[ix])
+	return false;
+    if (bits & 3)
+      if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3)))
+	return false;
+    return true;
+  }
+};
+
+/* Netmask comparison.  */
+struct netmask_cmp {
+  bool operator() (const netmask &a, const netmask &b) const
+  {
+    if (a.bits != b.bits)
+      return a.bits < b.bits;
+    for (unsigned ix = 0; ix != sizeof (in6_addr); ix++)
+      if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix])
+	return a.addr.s6_addr[ix] < b.addr.s6_addr[ix];
+    return false;
+  }
+};
+
+typedef std::set<netmask, netmask_cmp> netmask_set_t;
+typedef std::vector<netmask> netmask_vec_t;
+#endif
+
+const char *progname;
+
+/* Speak thoughts out loud.  */
+static bool flag_noisy = false;
+
+/* One and done.  */
+static bool flag_one = false;
+
+/* Serialize connections.  */
+static bool flag_sequential = false;
+
+/* Fallback to default if map file is unrewarding.  */
+static bool flag_map = false;
+
+/* Fallback to xlate if map file is unrewarding.  */
+static bool flag_xlate = false;
+
+/* Root binary directory.  */
+static const char *flag_root = "gcm.cache";
+
+#ifdef NETWORKING
+static netmask_set_t netmask_set;
+
+static netmask_vec_t accept_addrs;
+#endif
+
+/* Strip out the source directory from FILE.  */
+
+static const char *
+trim_src_file (const char *file)
+{
+  static const char me[] = __FILE__;
+  unsigned pos = 0;
+
+  while (file[pos] == me[pos] && me[pos])
+    pos++;
+  while (pos && !IS_DIR_SEPARATOR (me[pos-1]))
+    pos--;
+
+  return file + pos;
+}
+
+/* Die screaming.  */
+
+void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
+internal_error (const char *fmt, ...)
+{
+  fprintf (stderr, "%s:Internal error ", progname);
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  exit (2);
+}
+
+/* Hooked to from gcc_assert & gcc_unreachable.  */
+
+void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
+fancy_abort (const char *file, int line, const char *func)
+{
+  internal_error ("in %s, at %s:%d", func, trim_src_file (file), line);
+}
+
+/* Exploded on a signal.  */
+
+static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
+crash_signal (int sig)
+{
+  signal (sig, SIG_DFL);
+  internal_error ("signal %s", strsignal (sig));
+}
+
+/* A fatal error of some kind.  */
+
+static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
+error (const char *msg, ...)
+{
+  fprintf (stderr, "%s:error: ", progname);
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  exit (1);
+}
+
+#ifdef NETWORKING
+/* Progress messages to the user.  */
+static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
+noisy (const char *fmt, ...)
+{
+  fprintf (stderr, "%s:", progname);
+  va_list args;
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  va_end (args);
+  fprintf (stderr, "\n");
+
+  return false;
+}
+#endif
+
+/* More messages to the user.  */
+
+static void ATTRIBUTE_PRINTF_2
+fnotice (FILE *file, const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (file, fmt, args);
+  va_end (args);
+}
+
+static void ATTRIBUTE_NORETURN
+print_usage (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+  int status = error_p ? 1 : 0;
+
+  fnotice (file, "Usage: %s [OPTION...] [CONNECTION] [MAPPINGS...] \n\n",
+	   progname);
+  fnotice (file, "C++ Module Mapper.\n\n");
+  fnotice (file, "  -a, --accept     Netmask to accept from\n");
+  fnotice (file, "  -f, --fallback   Use fallback for missing mappings\n");
+  fnotice (file, "  -h, --help       Print this help, then exit\n");
+  fnotice (file, "  -n, --noisy      Print progress messages\n");
+  fnotice (file, "  -1, --one        One connection and then exit\n");
+  fnotice (file, "  -r, --root DIR   Root compiled module directory\n");
+  fnotice (file, "  -s, --sequential Process connections sequentially\n");
+  fnotice (file, "  -v, --version    Print version number, then exit\n");
+  fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM);
+  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
+	   bug_report_url);
+  exit (status);
+}
+
+/* Print version information and exit.  */
+
+static void ATTRIBUTE_NORETURN
+print_version (void)
+{
+  fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2018-2020 Free Software Foundation, Inc.\n",
+	   ("(C)"));
+  fnotice (stdout,
+	   ("This is free software; see the source for copying conditions.\n"
+	    "There is NO warranty; not even for MERCHANTABILITY or \n"
+	    "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
+  exit (0);
+}
+
+/* ARG is a netmask to accept from.  Add it to the table.  Return
+   false if we fail to resolve it.  */
+
+static bool
+accept_from (char *arg ATTRIBUTE_UNUSED)
+{
+  bool ok = true;
+#if HAVE_AF_INET6
+  unsigned bits = sizeof (in6_addr) * 8;
+  char *slash = strrchr (arg, '/');
+  if (slash)
+    {
+      *slash = 0;
+      if (slash[1])
+	{
+	  char *endp;
+	  bits = strtoul (slash + 1, &endp, 0);
+	}
+    }
+
+  addrinfo hints;
+
+  hints.ai_flags = AI_NUMERICSERV;
+  hints.ai_family = AF_INET6;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+  hints.ai_addrlen = 0;
+  hints.ai_addr = NULL;
+  hints.ai_canonname = NULL;
+  hints.ai_next = NULL;
+
+  struct addrinfo *addrs = NULL;
+  if (int e = getaddrinfo (slash == arg ? NULL : arg, "0", &hints, &addrs))
+    {
+      noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
+      ok = false;
+    }
+  else
+    for (addrinfo *next = addrs; next; next = next->ai_next)
+      if (next->ai_family == AF_INET6)
+	{
+	  netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
+	  netmask_set.insert (mask);
+	}
+  freeaddrinfo (addrs);
+#endif
+  return ok;
+}
+
+/* Process args, return index to first non-arg.  */
+
+static int
+process_args (int argc, char **argv)
+{
+  static const struct option options[] =
+    {
+     { "accept", required_argument, NULL, 'a' },
+     { "help",	no_argument,	NULL, 'h' },
+     { "map",   no_argument,	NULL, 'm' },
+     { "noisy",	no_argument,	NULL, 'n' },
+     { "one",	no_argument,	NULL, '1' },
+     { "root",	required_argument, NULL, 'r' },
+     { "sequential", no_argument, NULL, 's' },
+     { "translate",no_argument,	NULL, 't' },
+     { "version", no_argument,	NULL, 'v' },
+     { 0, 0, 0, 0 }
+    };
+  int opt;
+  bool bad_accept = false;
+  const char *opts = "a:fhmn1r:stv";
+  while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
+    {
+      switch (opt)
+	{
+	case 'a':
+	  if (!accept_from (optarg))
+	    bad_accept = true;
+	  break;
+	case 'h':
+	  print_usage (false);
+	  /* print_usage will exit.  */
+	case 'f': // deprecated alias
+	case 'm':
+	  flag_map = true;
+	  break;
+	case 'n':
+	  flag_noisy = true;
+	  break;
+	case '1':
+	  flag_one = true;
+	  break;
+	case 'r':
+	  flag_root = optarg;
+	  break;
+	case 's':
+	  flag_sequential = true;
+	  break;
+	case 't':
+	  flag_xlate = true;
+	  break;
+	case 'v':
+	  print_version ();
+	  /* print_version will exit.  */
+	default:
+	  print_usage (true);
+	  /* print_usage will exit.  */
+	}
+    }
+
+  if (bad_accept)
+    error ("failed to resolve all accept addresses");
+
+  return optind;
+}
+
+#ifdef NETWORKING
+
+/* Manipulate the EPOLL state, or do nothing, if there is epoll.  */
+
+#ifdef HAVE_EPOLL
+static inline void
+do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
+{
+  epoll_event ev;
+  ev.events = event;
+  ev.data.u32 = data;
+  if (epoll_ctl (epoll_fd, code, fd, &ev))
+    {
+      noisy ("epoll_ctl error:%s", xstrerror (errno));
+      gcc_unreachable ();
+    }
+}
+#define my_epoll_ctl(EFD,C,EV,FD,CL) \
+  ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
+#else
+#define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
+#endif
+
+/* We increment this to tell the server to shut down.  */
+static volatile int term = false;
+static volatile int kill_sock_fd = -1;
+#if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
+static int term_pipe[2] = {-1, -1};
+#else
+#define term_pipe ((int *)NULL)
+#endif
+
+/* A terminate signal.  Shutdown gracefully.  */
+
+static void
+term_signal (int sig)
+{
+  signal (sig, term_signal);
+  term = term + 1;
+  if (term_pipe && term_pipe[1] >= 0)
+    write (term_pipe[1], &term_pipe[1], 1);
+}
+
+/* A kill signal.  Shutdown immediately.  */
+
+static void
+kill_signal (int sig)
+{
+  signal (sig, SIG_DFL);
+  int sock_fd = kill_sock_fd;
+  if (sock_fd >= 0)
+    close (sock_fd);
+  exit (2);
+}
+
+bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
+{
+  switch (server->GetDirection ())
+    {
+    case Cody::Server::READING:
+      if (int err = server->Read ())
+	return !(err == EINTR || err == EAGAIN);
+      server->ProcessRequests ();
+      server->PrepareToWrite ();
+      break;
+
+    case Cody::Server::WRITING:
+      if (int err = server->Write ())
+	return !(err == EINTR || err == EAGAIN);
+      server->PrepareToRead ();
+      break;
+
+    default:
+      // We should never get here
+      return true;
+    }
+
+  // We've changed direction, so update epoll
+  gcc_assert (server->GetFDRead () == server->GetFDWrite ());
+  my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD,
+		server->GetDirection () == Cody::Server::READING
+		? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1);
+
+  return false;
+}
+
+void close_server (Cody::Server *server, int epoll_fd)
+{
+  my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
+
+  close (server->GetFDRead ());
+  
+  delete server;
+}
+
+int open_server (bool ip6, int sock_fd)
+{
+  sockaddr_in6 addr;
+  socklen_t addr_len = sizeof (addr);
+
+#ifdef HAVE_ACCEPT4
+  int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr,
+			   &addr_len, SOCK_NONBLOCK);
+#else
+  int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len);
+#endif
+  if (client_fd < 0)
+    {
+      error ("cannot accept: %s", xstrerror (errno));
+      flag_one = true;
+    }
+  else if (ip6)
+    {
+      const char *str = NULL;
+#if HAVE_INET_NTOP
+      char name[INET6_ADDRSTRLEN];
+      str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name));
+#endif
+      if (!accept_addrs.empty ())
+	{
+	  netmask_vec_t::iterator e = accept_addrs.end ();
+	  for (netmask_vec_t::iterator i = accept_addrs.begin ();
+	       i != e; ++i)
+	    if (i->includes (addr.sin6_addr))
+	      goto present;
+	  close (client_fd);
+	  client_fd = -1;
+	  noisy ("Rejecting connection from disallowed source '%s'",
+		 str ? str : "");
+	present:;
+	}
+      if (client_fd >= 0)
+	flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
+    }
+
+  return client_fd;
+}
+
+/* A server listening on bound socket SOCK_FD.  */
+
+static void
+server (bool ipv6, int sock_fd, module_resolver *resolver)
+{
+  int epoll_fd = -1;
+
+  signal (SIGTERM, term_signal);
+#ifdef HAVE_EPOLL
+  epoll_fd = epoll_create (1);
+#endif
+  if (epoll_fd >= 0)
+    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
+
+#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  sigset_t mask;
+  {
+    sigset_t block;
+    sigemptyset (&block);
+    sigaddset (&block, SIGTERM);
+    sigprocmask (SIG_BLOCK, &block, &mask);
+  }
+#endif
+
+#ifdef HAVE_EPOLL
+  const unsigned max_events = 20;
+  epoll_event events[max_events];
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  fd_set readers, writers;
+#endif
+  if (term_pipe)
+    pipe (term_pipe);
+
+  // We need stable references to servers, so this array can contain nulls
+  std::vector<Cody::Server *> connections;
+  unsigned live = 0;
+  while (sock_fd >= 0 || live)
+    {
+      /* Wait for one or more events.  */
+      bool eintr = false;
+      int event_count;
+
+      if (epoll_fd >= 0)
+	{
+#ifdef HAVE_EPOLL
+	  event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
+#endif
+	}
+      else
+	{
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+	  FD_ZERO (&readers);
+	  FD_ZERO (&writers);
+
+	  unsigned limit = 0;
+	  if (sock_fd >= 0
+	      && !(term || (live && (flag_one || flag_sequential))))
+	    {
+	      FD_SET (sock_fd, &readers);
+	      limit = sock_fd + 1;
+	    }
+
+	  if (term_pipe && term_pipe[0] >= 0)
+	    {
+	      FD_SET (term_pipe[0], &readers);
+	      if (unsigned (term_pipe[0]) >= limit)
+		limit = term_pipe[0] + 1;
+	    }
+
+	  for (auto iter = connections.begin ();
+	       iter != connections.end (); ++iter)
+	    if (auto *server = *iter)
+	      {
+		int fd = -1;
+		switch (server->GetDirection ())
+		  {
+		  case Cody::Server::READING:
+		    fd = server->GetFDRead ();
+		    FD_SET (fd, &readers);
+		    break;
+		  case Cody::Server::WRITING:
+		    fd = server->GetFDWrite ();
+		    FD_SET (fd, &writers);
+		    break;
+		  default:
+		    break;
+		  }
+
+		if (fd >= 0 && limit <= unsigned (fd))
+		  limit = fd + 1;
+	      }
+
+#ifdef HAVE_PSELECT
+	  event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask);
+#else
+	  event_count = select (limit, &readers, &writers, NULL, NULL);
+#endif
+	  if (term_pipe && FD_ISSET (term_pipe[0], &readers))
+	    {
+	      /* Fake up an interrupted system call.  */
+	      event_count = -1;
+	      errno = EINTR;
+	    }
+#endif
+	}
+
+      if (event_count < 0)
+	{
+	  // Error in waiting
+	  if (errno == EINTR)
+	    {
+	      flag_noisy && noisy ("Interrupted wait");
+	      eintr = true;
+	    }
+	  else
+	    error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait"
+#ifdef HAVE_PSELECT
+		   : "pselect",
+#else
+		   : "select",
+#endif
+		   xstrerror (errno));
+	  event_count = 0;
+	}
+
+      auto iter = connections.begin ();
+      while (event_count--)
+	{
+	  // Process an event
+	  int active = -2;
+
+	  if (epoll_fd >= 0)
+	    {
+#ifdef HAVE_EPOLL
+	      /* See PR c++/88664 for why a temporary is used.  */
+	      unsigned data = events[event_count].data.u32;
+	      active = int (data) - 1;
+#endif
+	    }
+	  else
+	    {
+	      for (; iter != connections.end (); ++iter)
+		if (auto *server = *iter)
+		  {
+		    bool found = false;
+		    switch (server->GetDirection ())
+		      {
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+		      case Cody::Server::READING:
+			found = FD_ISSET (server->GetFDRead (), &readers);
+			break;
+		      case Cody::Server::WRITING:
+			found = FD_ISSET (server->GetFDWrite (), &writers);
+			break;
+#endif
+		      default:
+			break;
+		      }
+
+		    if (found)
+		      {
+			active = iter - connections.begin ();
+			++iter;
+			break;
+		      }
+		  }
+
+	      if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
+		active = -1;
+	    }
+
+	  if (active >= 0)
+	    {
+	      // Do the action
+	      auto *server = connections[active];
+	      if (process_server (server, active, epoll_fd))
+		{
+		  connections[active] = nullptr;
+		  close_server (server, epoll_fd);
+		  live--;
+		  if (flag_sequential)
+		    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
+		}
+	    }
+	  else if (active == -1 && !eintr)
+	    {
+	      // New connection
+	      int fd = open_server (ipv6, sock_fd);
+	      if (fd >= 0)
+		{
+#if !defined (HAVE_ACCEPT4) \
+  && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
+		  int flags = fcntl (fd, F_GETFL, 0);
+		  fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+#endif
+		  auto *server = new Cody::Server (resolver, fd);
+
+		  unsigned slot = connections.size ();
+		  if (live == slot)
+		    connections.push_back (server);
+		  else
+		    for (auto iter = connections.begin (); ; ++iter)
+		      if (!*iter)
+			{
+			  *iter = server;
+			  slot = iter - connections.begin ();
+			  break;
+			}
+		  live++;
+		  my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
+		}
+	    }
+
+	  if (sock_fd >= 0
+	      && (term || (live && (flag_one || flag_sequential))))
+	    {
+	      /* Stop paying attention to sock_fd.  */
+	      my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
+	      if (flag_one || term)
+		{
+		  close (sock_fd);
+		  sock_fd = -1;
+		}
+	    }
+	}
+    }
+#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+  /* Restore the signal mask.  */
+  sigprocmask (SIG_SETMASK, &mask, NULL);
+#endif
+
+  gcc_assert (sock_fd < 0);
+  if (epoll_fd >= 0)
+    close (epoll_fd);
+
+  if (term_pipe && term_pipe[0] >= 0)
+    {
+      close (term_pipe[0]);
+      close (term_pipe[1]);
+    }
+}
+
+#endif
+
+static int maybe_parse_socket (std::string &option, module_resolver *r)
+{
+  /* Local or ipv6 address.  */
+  auto last = option.find_last_of ('?');
+  if (last != option.npos)
+    {
+      r->set_ident (option.c_str () + last + 1);
+      option.erase (last);
+    }
+  int fd = -2;
+  char const *errmsg = nullptr;
+
+  /* Does it look like a socket?  */
+  if (option[0] == '=')
+    {
+      /* A local socket.  */
+#if CODY_NETWORKING
+      fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
+#endif
+    }
+  else
+    {
+      auto colon = option.find_last_of (':');
+      if (colon != option.npos)
+	{
+	  /* Try a hostname:port address.  */
+	  char const *cptr = option.c_str () + colon;
+	  char *endp;
+	  unsigned port = strtoul (cptr + 1, &endp, 10);
+
+	  if (port && endp != cptr + 1 && !*endp)
+	    {
+	      /* Ends in ':number', treat as ipv6 domain socket.  */
+	      option.erase (colon);
+#if CODY_NETWORKING
+	      fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
+#endif
+	    }
+	}
+    }
+
+  if (errmsg)
+    error ("failed to open socket: %s", errmsg);
+
+  return fd;
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *p = argv[0] + strlen (argv[0]);
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
+  progname = p;
+
+#ifdef SIGSEGV
+  signal (SIGSEGV, crash_signal);
+#endif
+#ifdef SIGILL
+  signal (SIGILL, crash_signal);
+#endif
+#ifdef SIGBUS
+  signal (SIGBUS, crash_signal);
+#endif
+#ifdef SIGABRT
+  signal (SIGABRT, crash_signal);
+#endif
+#ifdef SIGFPE
+  signal (SIGFPE, crash_signal);
+#endif
+#ifdef SIGPIPE
+  /* Ignore sigpipe, so read/write get an error.  */
+  signal (SIGPIPE, SIG_IGN);
+#endif
+#ifdef NETWORKING
+#ifdef SIGINT
+  signal (SIGINT, kill_signal);
+#endif
+#endif
+
+  int argno = process_args (argc, argv);
+
+  std::string name;
+  int sock_fd = -1; /* Socket fd, otherwise stdin/stdout.  */
+  module_resolver r (flag_map, flag_xlate);
+
+  if (argno != argc)
+    {
+      name = argv[argno];
+      sock_fd = maybe_parse_socket (name, &r);
+      if (!name.empty ())
+	argno++;
+    }
+
+  if (argno != argc)
+    for (; argno != argc; argno++)
+      {
+	std::string option = argv[argno];
+	char const *prefix = nullptr;
+	auto ident = option.find_last_of ('?');
+	if (ident != option.npos)
+	  {
+	    prefix = option.c_str () + ident + 1;
+	    option[ident] = 0;
+	  }
+	int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
+	int err = 0;
+	if (fd < 0)
+	  err = errno;
+	else
+	  {
+	    err = r.read_tuple_file (fd, prefix, false);
+	    close (fd);
+	  }
+
+	if (err)
+	  error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
+      }
+  else
+    r.set_default_map (true);
+
+  if (flag_root)
+    r.set_repo (flag_root);
+
+#ifdef HAVE_AF_INET6
+  netmask_set_t::iterator end = netmask_set.end ();
+  for (netmask_set_t::iterator iter = netmask_set.begin ();
+       iter != end; ++iter)
+    {
+      netmask_vec_t::iterator e = accept_addrs.end ();
+      for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
+	if (i->includes (iter->addr))
+	  goto present;
+      accept_addrs.push_back (*iter);
+    present:;
+    }
+#endif
+
+#ifdef NETWORKING
+  if (sock_fd >= 0)
+    {
+      server (name[0] != '=', sock_fd, &r);
+      if (name[0] == '=')
+	unlink (name.c_str () + 1);
+    }
+  else
+#endif
+    {
+      auto server = Cody::Server (&r, 0, 1);
+
+      int err = 0;
+      for (;;)
+	{
+	  server.PrepareToRead ();
+	  while ((err = server.Read ()))
+	    {
+	      if (err == EINTR || err == EAGAIN)
+		continue;
+	      goto done;
+	    }
+
+	  server.ProcessRequests ();
+
+	  server.PrepareToWrite ();
+	  while ((err = server.Write ()))
+	    {
+	      if (err == EINTR || err == EAGAIN)
+		continue;
+	      goto done;
+	    }
+	}
+    done:;
+      if (err > 0)
+	error ("communication error:%s", xstrerror (err));
+    }
+
+  return 0;
+}
diff --git c/gcc/configure.ac w/gcc/configure.ac
index 73034bb902b..d1c980dd64a 100644
--- c/gcc/configure.ac
+++ w/gcc/configure.ac
@@ -1440,6 +1442,10 @@ fi
 
 AC_CHECK_TYPE(ssize_t, int)
 AC_CHECK_TYPE(caddr_t, char *)
+AC_CHECK_TYPE(sighander_t,
+  AC_DEFINE(HAVE_SIGHANDLER_T, 1,
+    [Define if <sys/signal.h> defines sighandler_t]),
+    ,signal.h)
 
 GCC_AC_FUNC_MMAP_BLACKLIST
 
@@ -1585,6 +1591,72 @@ if test $ac_cv_f_setlkw = yes; then
   [Define if F_SETLKW supported by fcntl.])
 fi
 
+# Check if O_CLOEXEC is defined by fcntl
+AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>]], [[
+return open ("/dev/null", O_RDONLY | O_CLOEXEC);]])],
+[ac_cv_o_cloexec=yes],[ac_cv_o_cloexec=no])])
+if test $ac_cv_o_cloexec = yes; then
+  AC_DEFINE(HOST_HAS_O_CLOEXEC, 1,
+  [Define if O_CLOEXEC supported by fcntl.])
+fi
+
+# C++ Modules would like some networking features to provide the mapping
+# server.  You can still use modules without them though.
+# The following network-related checks could probably do with some
+# Windows and other non-linux defenses and checking.
+
+# Local socket connectivity wants AF_UNIX networking
+# Check for AF_UNIX networking
+AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>]],[[
+sockaddr_un un;
+un.sun_family = AF_UNSPEC;
+int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&un, sizeof (un));]])],
+[ac_cv_af_unix=yes],
+[ac_cv_af_unix=no])])
+if test $ac_cv_af_unix = yes; then
+  AC_DEFINE(HAVE_AF_UNIX, 1,
+  [Define if AF_UNIX supported.])
+fi
+
+# Remote socket connectivity wants AF_INET6 networking
+# Check for AF_INET6 networking
+AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>]],[[
+sockaddr_in6 in6;
+in6.sin6_family = AF_UNSPEC;
+struct addrinfo *addrs = 0;
+struct addrinfo hints;
+hints.ai_flags = 0;
+hints.ai_family = AF_INET6;
+hints.ai_socktype = SOCK_STREAM;
+hints.ai_protocol = 0;
+hints.ai_canonname = 0;
+hints.ai_addr = 0;
+hints.ai_next = 0;
+int e = getaddrinfo ("localhost", 0, &hints, &addrs);
+const char *str = gai_strerror (e);
+freeaddrinfo (addrs);
+int fd = socket (AF_INET6, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&in6, sizeof (in6));]])],
+[ac_cv_af_inet6=yes],
+[ac_cv_af_inet6=no])])
+if test $ac_cv_af_inet6 = yes; then
+  AC_DEFINE(HAVE_AF_INET6, 1,
+  [Define if AF_INET6 supported.])
+fi
+
 # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests.
 CFLAGS="$saved_CFLAGS"
 CXXFLAGS="$saved_CXXFLAGS"
diff --git c/gcc/cp/mapper-client.cc w/gcc/cp/mapper-client.cc
new file mode 100644
index 00000000000..22605ca8cf9
--- /dev/null
+++ w/gcc/cp/mapper-client.cc
@@ -0,0 +1,327 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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 "line-map.h"
+#include "diagnostic-core.h"
+#include "mapper-client.h"
+#include "intl.h"
+
+#include "../../c++tools/resolver.h"
+
+module_client::module_client (pex_obj *p, int fd_from, int fd_to)
+  : Client (fd_from, fd_to), pex (p)
+{
+}
+
+static module_client *
+spawn_mapper_program (char const **errmsg, std::string &name,
+		      char const *full_program_name)
+{
+  /* Split writable at white-space.  No space-containing args for
+     you!  */
+  // At most every other char could be an argument
+  char **argv = new char *[name.size () / 2 + 2];
+  unsigned arg_no = 0;
+  char *str = new char[name.size ()];
+  memcpy (str, name.c_str () + 1, name.size ());
+
+  for (auto ptr = str; ; ++ptr)
+    {
+      while (*ptr == ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+
+      if (!arg_no)
+	{
+	  /* @name means look in the compiler's install dir.  */
+	  if (ptr[0] == '@')
+	    ptr++;
+	  else
+	    full_program_name = nullptr;
+	}
+
+      argv[arg_no++] = ptr;
+      while (*ptr && *ptr != ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+      *ptr = 0;
+    }
+  argv[arg_no] = nullptr;
+
+  auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
+  FILE *to = pex_input_pipe (pex, false);
+  name = argv[0];
+  if (!to)
+    *errmsg = "connecting input";
+  else
+    {
+      int flags = PEX_SEARCH;
+
+      if (full_program_name)
+	{
+	  /* Prepend the invoking path, if the mapper is a simple
+	     file name.  */
+	  size_t dir_len = progname - full_program_name;
+	  std::string argv0;
+	  argv0.reserve (dir_len + name.size ());
+	  argv0.append (full_program_name, dir_len).append (name);
+	  name = std::move (argv0);
+	  argv[0] = const_cast <char *> (name.c_str ());
+	  flags = 0;
+	}
+      int err;
+      *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
+    }
+  delete[] str;
+  delete[] argv;
+
+  int fd_from = -1, fd_to = -1;
+  if (!*errmsg)
+    {
+      FILE *from = pex_read_output (pex, false);
+      if (from && (fd_to = dup (fileno (to))) >= 0)
+	fd_from = fileno (from);
+      else
+	*errmsg = "connecting output";
+      fclose (to);
+    }
+
+  if (*errmsg)
+    {
+      pex_free (pex);
+      return nullptr;
+    }
+
+  return new module_client (pex, fd_from, fd_to);
+}
+
+module_client *
+module_client::open_module_client (location_t loc, const char *o,
+				   void (*set_repo) (const char *),
+				   char const *full_program_name)
+{
+  module_client *c = nullptr;
+  std::string ident;
+  std::string name;
+  char const *errmsg = nullptr;
+  unsigned line = 0;
+
+  if (o && o[0])
+    {
+      /* Maybe a local or ipv6 address.  */
+      name = o;
+      auto last = name.find_last_of ('?');
+      if (last != name.npos)
+	{
+	  ident = name.substr (last + 1);
+	  name.erase (last);
+	}
+
+      if (name.size ())
+	{
+	  switch (name[0])
+	    {
+	    case '<':
+	      // <from>to or <>fromto, or <>
+	      {
+		int fd_from = -1, fd_to = -1;
+		char const *ptr = name.c_str ();
+		char *eptr;
+
+		fd_from = strtoul (++ptr, &eptr, 0);
+		if (*eptr == '>')
+		  {
+		    ptr = eptr;
+		    fd_to = strtoul (++ptr, &eptr, 0);
+		    if (eptr != ptr && ptr == name.c_str () + 1)
+		      fd_from = fd_to;
+		  }
+
+		if (*eptr)
+		  errmsg = "parsing";
+		else
+		  {
+		    if (name.size () == 2)
+		      {
+			fd_from = fileno (stdin);
+			fd_to = fileno (stdout);
+		      }
+		    c = new module_client (fd_from, fd_to);
+		  }
+	      }
+	      break;
+
+	    case '=':
+	      // =localsocket
+	      {
+		int fd = -1;
+#if CODY_NETWORKING
+		fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
+#endif
+		if (fd >= 0)
+		  c = new module_client (fd, fd);
+	      }
+	      break;
+
+	    case '|':
+	      // |program and args
+	      c = spawn_mapper_program (&errmsg, name, full_program_name);
+	      break;
+
+	    default:
+	      // file or hostname:port
+	      {
+		auto colon = name.find_last_of (':');
+		if (colon != name.npos)
+		  {
+		    char const *cptr = name.c_str () + colon;
+		    char *endp;
+		    unsigned port = strtoul (cptr + 1, &endp, 10);
+
+		    if (port && endp != cptr + 1 && !*endp)
+		      {
+			name[colon] = 0;
+			int fd = 01;
+#if CODY_NETWORKING
+			fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
+#endif
+			name[colon] = ':';
+
+			if (fd >= 0)
+			  c = new module_client (fd, fd);
+		      }
+		  }
+		
+	      }
+	      break;
+	    }
+	}
+    }
+
+  if (!c)
+    {
+      // Make a default in-process client
+      bool file = !errmsg && !name.empty ();
+      auto r = new module_resolver (!file, true);
+
+      if (file)
+	{
+	int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
+	if (fd < 0)
+	  errmsg = "opening";
+	else
+	  {
+	    if (int l = r->read_tuple_file (fd, ident, false))
+	      {
+		if (l > 0)
+		  line = l;
+		errmsg = "reading";
+	      }
+	      
+	    close (fd);
+	  }
+	}
+      else
+	r->set_repo ("gcm.cache");
+
+      auto *s = new Cody::Server (r);
+      c = new module_client (s);
+    }
+
+#ifdef SIGPIPE
+  if (!c->IsDirect ())
+    /* We need to ignore sig pipe for a while.  */
+    c->sigpipe = signal (SIGPIPE, SIG_IGN);
+#endif
+
+  if (errmsg)
+    error_at (loc, line ? G_("failed %s mapper %qs line %u")
+	      : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
+
+  // now wave hello!
+  c->Cork ();
+  c->Connect (std::string ("GCC"), ident);
+  c->ModuleRepo ();
+  auto packets = c->Uncork ();
+
+  auto &connect = packets[0];
+  if (connect.GetCode () == Cody::Client::PC_CONNECT)
+    c->flags = Cody::Flags (connect.GetInteger ());
+  else if (connect.GetCode () == Cody::Client::PC_ERROR)
+    error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
+
+  auto &repo = packets[1];
+  if (repo.GetCode () == Cody::Client::PC_PATHNAME)
+    set_repo (repo.GetString ().c_str ());
+
+  return c;
+}
+
+void
+module_client::close_module_client (location_t loc, module_client *mapper)
+{
+  if (mapper->IsDirect ())
+    {
+      auto *s = mapper->GetServer ();
+      auto *r = s->GetResolver ();
+      delete s;
+      delete r;
+    }
+  else
+    {
+      if (mapper->pex)
+	{
+	  int fd_write = mapper->GetFDWrite ();
+	  if (fd_write >= 0)
+	    close (fd_write);
+
+	  int status;
+	  pex_get_status (mapper->pex, 1, &status);
+
+	  pex_free (mapper->pex);
+	  mapper->pex = NULL;
+
+	  if (WIFSIGNALED (status))
+	    error_at (loc, "mapper died by signal %s",
+		      strsignal (WTERMSIG (status)));
+	  else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
+	    error_at (loc, "mapper exit status %d",
+		      WEXITSTATUS (status));
+	}
+      else
+	{
+	  int fd_read = mapper->GetFDRead ();
+	  close (fd_read);
+	}
+
+#ifdef SIGPIPE
+      // Restore sigpipe
+      if (mapper->sigpipe != SIG_IGN)
+	signal (SIGPIPE, mapper->sigpipe);
+#endif
+    }
+
+  delete mapper;
+}
diff --git c/gcc/cp/mapper-client.h w/gcc/cp/mapper-client.h
new file mode 100644
index 00000000000..ca1a0aa5509
--- /dev/null
+++ w/gcc/cp/mapper-client.h
@@ -0,0 +1,63 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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/>.  */
+
+/* Forward to the header in c++tools.  */
+
+#ifndef MAPPER_CLIENT_H
+#define MAPPER_CLIENT_H 1
+
+#include "cody.hh"
+
+#ifndef HAVE_SIGHANDLER_T
+typedef void (*sighandler_t) (int);
+#endif
+
+class module_client : public Cody::Client
+{
+  pex_obj *pex = nullptr;
+  sighandler_t sigpipe = SIG_IGN;
+  Cody::Flags flags = Cody::Flags::None;
+
+public:
+  module_client (Cody::Server *s)
+    : Client (s)
+  {
+  }
+  module_client (pex_obj *pex, int fd_from, int fd_to);
+
+  module_client (int fd_from, int fd_to)
+    : Client (fd_from, fd_to)
+  {
+  }
+
+public:
+  Cody::Flags get_flags () const
+  {
+    return flags;
+  }
+
+public:
+  static module_client *open_module_client (location_t loc, const char *option,
+					    void (*set_repo) (const char *),
+					    char const *);
+  static void close_module_client (location_t loc, module_client *);
+};
+
+#endif
diff --git c/gcc/cp/mapper-resolver.cc w/gcc/cp/mapper-resolver.cc
new file mode 100644
index 00000000000..02ec48c61ea
--- /dev/null
+++ w/gcc/cp/mapper-resolver.cc
@@ -0,0 +1,27 @@
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   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/>.  */
+
+/* Forward to the resolver in c++tools.  */
+
+#include "config.h"
+#define INCLUDE_ALGORITHM
+#include "system.h"
+
+#include "../../c++tools/resolver.cc"

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

* Re: [22/32] miscelaneous c++ bits
  2020-11-03 21:16                                         ` [22/32] miscelaneous c++ bits Nathan Sidwell
@ 2020-11-13 13:41                                           ` Nathan Sidwell
  2020-11-13 14:03                                           ` [22.2/32] module flags Nathan Sidwell
  1 sibling, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-13 13:41 UTC (permalink / raw)
  To: GCC Patches

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

On 11/3/20 4:16 PM, Nathan Sidwell wrote:
> This is probably the messiest diff.

Let's break this diff apart a bit more, for digestibility.

Here's the MODULE_VECTOR piece.   This is a sparse array used for name 
lookup.  A namespace symbol table entry may contain one of these, which 
holds the bindings for each loaded module.  If a module doesn't bind 
that name, there'll be no slot for it.  The current TU always uses slot 
0.  The Global Module uses slot 1, and in a named-module partitions 
share slot 2.  Slots 1 & 2 are used for duplicate declaration matching.
[These slots are managed entirely in name-lookup].

For data layout purposes, the vector is partitioned into clusters.  Each 
cluster holds two slots.  A slot is the pointer to the bound value 
(often an OVERLOAD) or a lazy cookie, a slot index, and a slot span. 
The span is usually '1', except for namespaces which are module-spanning 
entities.  It is '0' in the fixed slots, for simplicity.  Both the index 
and spans are 16-bit 'unsigned short', and the slot is a 32 or 64 bit 
pointer.  Thus on a 64-bit host we can pack 2 indices, 2 spans and 2 
pointers without padding.  (the slots are not adjacent to their index/span).

The slot itself can contain either a pointer or a load cookie.  These 
are distinguished using bit 0 -- 1 for cookie, zero for pointer.  Thus 
pointers have their natural representation (they are 4 or 8 byte 
aligned).  The lazy cookie happens to be partitioned into two pieces.  A 
pair of bits concerning pending template instantiations and member 
definitions, with the remaining bits (29 or 61) being a section number 
in that module's CMI.  When we do name lookup we see if there's a lazy 
cookie in a slot of interest, and if so load that section before 
proceeding.  (the same cookie may be present in several slots [in 
different bindings], loading will populate all those slots.)  There's a 
preceding check to determine whether the contents of that slot are 
visible to us (the containing module has been exported to us).  the 
result is that importing a module is a cheap operation with an amortized 
load cost depending what you look at.  (there's a flag to turn lazy 
loading off)

As you can guess, there's a limit of 16383 imported modules (there is an 
independent limit of 2^31 imported entities)

Indices are always in increasing order, and by construction we only need 
to append to the array.

nathan


-- 
Nathan Sidwell

[-- Attachment #2: 22.1-c++-module-vec.diff --]
[-- Type: text/x-patch, Size: 5566 bytes --]

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 63724c0e84f..4752ddef898 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -929,6 +959,84 @@ struct named_decl_hash : ggc_remove <tree> {
   static void mark_deleted (value_type) { gcc_unreachable (); }
 };
 
+/* Bindings for modules are held in a sparse array.  There is always a
+   current TU slot, others are allocated as needed.  By construction
+   of the importing mechanism we only ever need to append to the
+   array.  Rather than have straight index/slot tuples, we bunch them
+   up for greater packing.
+
+   The cluster representation packs well on a 64-bit system.  */
+
+#define MODULE_VECTOR_SLOTS_PER_CLUSTER 2
+struct mc_index {
+  unsigned short base;
+  unsigned short span;
+};
+
+struct GTY(()) module_cluster
+{
+  mc_index GTY((skip)) indices[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+  mc_slot slots[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+};
+
+/* These two fields overlay lang flags.  So don't use those.  */
+#define MODULE_VECTOR_ALLOC_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.clique)
+#define MODULE_VECTOR_NUM_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.base)
+#define MODULE_VECTOR_CLUSTER_BASE(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec)
+#define MODULE_VECTOR_CLUSTER_LAST(NODE) \
+  (&MODULE_VECTOR_CLUSTER (NODE, MODULE_VECTOR_NUM_CLUSTERS (NODE) - 1))
+#define MODULE_VECTOR_CLUSTER(NODE,IX) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec[IX])
+
+struct GTY(()) tree_module_vec {
+  struct tree_base base;
+  tree name;
+  module_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1];
+};
+
+/* The name of a module vector.  */
+#define MODULE_VECTOR_NAME(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->name)
+
+/* tree_module_vec does uses  base.u.dependence_info.base field for
+   length.  It does not have lang_flag etc available!  */
+
+/* These two flags note if a module-vector contains deduplicated
+   bindings (i.e. multiple declarations in different imports).  */
+/* This binding contains duplicate references to a global module
+   entity.  */
+#define MODULE_VECTOR_GLOBAL_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.static_flag)
+/* This binding contains duplicate references to a partioned module
+   entity.  */
+#define MODULE_VECTOR_PARTITION_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.volatile_flag)
+
+/* These two flags indicate the provenence of the bindings on this
+   particular vector slot.  We can of course determine this from slot
+   number, but that's a relatively expensive lookup.  This avoids
+   that when iterating.  */
+/* This slot is part of the global module (a header unit).  */
+#define MODULE_BINDING_GLOBAL_P(NODE) \
+  (OVERLOAD_CHECK (NODE)->base.static_flag)
+/* This slot is part of the current module (a partition or primary).  */
+#define MODULE_BINDING_PARTITION_P(NODE)		\
+  (OVERLOAD_CHECK (NODE)->base.volatile_flag)
+
+/* There are specializations of a template keyed to this binding.  */
+#define MODULE_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.public_flag)
+/* The key is in a header unit (not a named module partition or
+   primary).  */
+#define MODULE_VECTOR_PENDING_IS_HEADER_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.protected_flag)
+/* The key is in a named module (primary or partition).  */
+#define MODULE_VECTOR_PENDING_IS_PARTITION_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.private_flag)
+
 /* Simplified unique_ptr clone to release a tree vec on exit.  */
 
 class releasing_vec
@@ -7349,6 +7657,7 @@ extern tree hash_tree_cons			(tree, tree, tree);
 extern tree hash_tree_chain			(tree, tree);
 extern tree build_qualified_name		(tree, tree, tree, bool);
 extern tree build_ref_qualified_type		(tree, cp_ref_qualifier);
+extern tree make_module_vec			(tree, unsigned clusters);
 inline tree ovl_first				(tree) ATTRIBUTE_PURE;
 extern tree ovl_make				(tree fn,
 						 tree next = NULL_TREE);
@@ -7972,14 +8295,16 @@ type_unknown_p (const_tree expr)
 inline hashval_t
 named_decl_hash::hash (const value_type decl)
 {
-  tree name = OVL_NAME (decl);
+  tree name = (TREE_CODE (decl) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (decl) : OVL_NAME (decl));
   return name ? IDENTIFIER_HASH_VALUE (name) : 0;
 }
 
 inline bool
 named_decl_hash::equal (const value_type existing, compare_type candidate)
 {
-  tree name = OVL_NAME (existing);
+  tree name = (TREE_CODE (existing) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (existing) : OVL_NAME (existing));
   return candidate == name;
 }
 
diff --git c/gcc/cp/tree.c w/gcc/cp/tree.c
index 28e591086b3..9979fd4f828 100644
--- c/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -2218,6 +2226,23 @@ build_ref_qualified_type (tree type, cp_ref_qualifier rqual)
   return build_cp_fntype_variant (type, rqual, raises, late);
 }
 
+tree
+make_module_vec (tree name, unsigned clusters MEM_STAT_DECL)
+{
+  /* Stored in an unsigned short, but we're limited to the number of
+     modules anyway.  */
+  gcc_checking_assert (clusters <= (unsigned short)(~0));
+  size_t length = (clusters * sizeof (module_cluster)
+		   + sizeof (tree_module_vec) - sizeof (module_cluster));
+  tree vec = ggc_alloc_cleared_tree_node_stat (length PASS_MEM_STAT);
+  TREE_SET_CODE (vec, MODULE_VECTOR);
+  MODULE_VECTOR_NAME (vec) = name;
+  MODULE_VECTOR_ALLOC_CLUSTERS (vec) = clusters;
+  MODULE_VECTOR_NUM_CLUSTERS (vec) = 0;
+
+  return vec;
+}
+
 /* Make a raw overload node containing FN.  */
 
 tree

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

* Re: [22.2/32] module flags
  2020-11-03 21:16                                         ` [22/32] miscelaneous c++ bits Nathan Sidwell
  2020-11-13 13:41                                           ` Nathan Sidwell
@ 2020-11-13 14:03                                           ` Nathan Sidwell
  2020-11-13 14:27                                             ` Richard Biener
  1 sibling, 1 reply; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-13 14:03 UTC (permalink / raw)
  To: GCC Patches

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

Here are the pieces of patch 22 that add new flag bits to tree nodes and 
lang_decl structs, along with a new global indicating what fragment of a 
module we may be processing.

be aware that header-units, although part of the Global Module, are 
treated as-if they are named modules but with some interesting 'may have 
duplicate' rules.  In particular all their entities are exported, and 
marked as having a purview

There was a free LANG_DECL flag, which I use for DECL_MODULE_EXPORT_P, 
such a decl is being exported from somewhere.

I needed to mark typeinfo types, so DECL_TINFO_P is extended to TYPE_DECLs.

OVL_EXPORT_P is added to indicate that a particular member is an export. 
  This is duplicating DECL_MODULE_EXPORT_P, but it is convenient to have 
it in the overload.

DECL_MODULE_PURVIEW_P -- the decl is in the purview of a module

DECL_MODULE_IMPORT_P -- we got this decl from an import

DECL_MODULE_ENTITY_P -- this decl is in the imported entity array & 
hash.  It may be true even if DECL_MODULE_IMPORT_P is false, because the 
current TU might be defining it.

DECL_MODULE_PENDING_SPECIALIZATIONs, this template decl has 
specializations that we have not loaded (they must be loaded before we 
can instantiate the template)  such specializations can be in arbitray 
modules, not necessarily the one defining the template

DECL_MODULE_PENDING_MEMBERS, likewise, we can define members in other 
modules (partitions or header units), or instantiate implicit members 
anywhere.  These need to be loaded before we can look inside this class.

DECL_ATTACHED_DECLS_P, this namespace-scope decl has a set of attached 
decls for ODR purposes.  The case we handle comes from ranges:

template<something> constexpr T var = [] () { return something; }

That lambda is attached to 'var', it's not a different lambda in each TU.

Nearly all those new flags are added to lang_decl_base.  Originally I 
had the module index there, which is why I drastically shrank 
'selector'.  I keep the shrinkage because I don't really think it's a 
bottleneck.

class module_state is defined inside module.cc, but we need to expose 
its incomplete tag.  'modules_p' is true if we're supporting modules. 
IIRC I had one bug during development where a modules-disabled 
compilation crashed.  So I'm reasonably certain that, when disabled, the 
compiler is still as stable as ever.

module_kind is a set of bits indicating what kind of module we're 
processing.  non-module code will have it zero.  In module purview 
MK_MODULE will be set.  In the GMF of a named module MK_GLOBAL will be 
set (and MK_MODULE clear).  In a header unit, both are set.

MK_EXPORTING is set if we're inside an 'export' either a {...} region, 
or a single decl.  MK_INTERFACE is true if we're in the interface of a 
named module (as opposed to implementation), and MK_PARTITION is true if 
we're in a partition of a named module (interface or implementation).

There are a bunch of inline predicate functions to decode the various 
combinations that are useful.

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 22.2-c++-module-flags.diff --]
[-- Type: text/x-patch, Size: 8809 bytes --]

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 63724c0e84f..4752ddef898 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -479,13 +488,14 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
       DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE)
       CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR)
+      DECL_MODULE_EXPORT_P (in _DECL)
       OVL_NESTED_P (in OVERLOAD)
       LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR)
       Reserved for DECL_MODULE_EXPORT (in DECL_)
    4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
       TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
 	  CALL_EXPR, or FIELD_DECL).
-      DECL_TINFO_P (in VAR_DECL)
+      DECL_TINFO_P (in VAR_DECL, TYPE_DECL)
       FUNCTION_REF_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       OVL_LOOKUP_P (in OVERLOAD)
       LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
@@ -493,6 +503,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
       CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
+      OVL_EXPORT_P (in OVL_USING_P OVERLOAD)
    6: TYPE_MARKED_P (in _TYPE)
       DECL_NONTRIVIALLY_INITIALIZED_P (in VAR_DECL)
       RANGE_FOR_IVDEP (in RANGE_FOR_STMT)
@@ -768,6 +780,8 @@ typedef struct ptrmem_cst * ptrmem_cst_t;
 #define OVL_NESTED_P(NODE)	TREE_LANG_FLAG_3 (OVERLOAD_CHECK (NODE))
 /* If set, this overload was constructed during lookup.  */
 #define OVL_LOOKUP_P(NODE)	TREE_LANG_FLAG_4 (OVERLOAD_CHECK (NODE))
+/* If set, this OVL_USING_P overload is exported.  */
+#define OVL_EXPORT_P(NODE)	TREE_LANG_FLAG_5 (OVERLOAD_CHECK (NODE))
 
 /* The first decl of an overload.  */
 #define OVL_FIRST(NODE)	ovl_first (NODE)
@@ -835,6 +854,12 @@ class ovl_iterator {
     return (TREE_CODE (ovl) == USING_DECL
 	    || (TREE_CODE (ovl) == OVERLOAD && OVL_USING_P (ovl)));
   }
+  /* Whether this using is being exported.  */
+  bool exporting_p () const
+  {
+    return OVL_EXPORT_P (get_using ());
+  }
+  
   bool hidden_p () const
   {
     return TREE_CODE (ovl) == OVERLOAD && OVL_HIDDEN_P (ovl);
@@ -1634,6 +1742,48 @@ check_constraint_info (tree t)
 #define CONSTRAINED_PARM_PROTOTYPE(NODE) \
   DECL_INITIAL (TYPE_DECL_CHECK (NODE))
 
+/* Module defines.  */
+// Too many _DECLS: FUNCTION,VAR,TYPE,TEMPLATE,CONCEPT or NAMESPACE
+#define DECL_MODULE_CHECK(NODE) (NODE)
+
+/* In the purview of a module (including header unit).  */
+#define DECL_MODULE_PURVIEW_P(N) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (N))->u.base.module_purview_p)
+
+/* True if the live version of the decl was imported.  */
+#define DECL_MODULE_IMPORT_P(NODE) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_import_p)
+
+/* True if this decl is in the entity hash & array.  This means that
+   some variant was imported, even if DECL_MODULE_IMPORT_P is false.  */
+#define DECL_MODULE_ENTITY_P(NODE) \
+  (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_entity_p)
+
+/* True if there are unloaded specializations keyed to this template.  */
+#define DECL_MODULE_PENDING_SPECIALIZATIONS_P(NODE)	\
+  (DECL_LANG_SPECIFIC (TEMPLATE_DECL_CHECK (NODE))	\
+   ->u.base.module_pending_specializations_p)
+
+/* True if this class has unloaded members.  These should be loaded
+   before we do member lookups.  While this may be better on the
+   classtype, I think we can get this behaviour for enums too.  But
+   perhaps those need to be immediately loaded?  (Particularly if
+   unscoped).  */
+#define DECL_MODULE_PENDING_MEMBERS_P(NODE)		\
+  (DECL_LANG_SPECIFIC (TYPE_DECL_CHECK (NODE))		\
+   ->u.base.module_pending_members_p)
+
+/* Whether this is an exported DECL.  Held on any decl that can appear
+   at namespace scope (function, var, type, template, const or
+   namespace).  templates copy from their template_result, consts have
+   it for unscoped enums.  */
+#define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE)
+
+/* DECL that has attached decls for ODR-relatedness.  */
+#define DECL_ATTACHED_DECLS_P(NODE)			\
+  (DECL_LANG_SPECIFIC (NODE)->u.base.attached_decls_p)
+
+\f
 /* The list of local parameters introduced by this requires-expression,
    in the form of a chain of PARM_DECLs.  */
 #define REQUIRES_EXPR_PARMS(NODE) \
@@ -2643,15 +2795,15 @@ enum lang_decl_selector
 /* Flags shared by all forms of DECL_LANG_SPECIFIC.
 
    Some of the flags live here only to make lang_decl_min/fn smaller.  Do
-   not make this struct larger than 32 bits; instead, make sel smaller.  */
+   not make this struct larger than 32 bits.  */
 
 struct GTY(()) lang_decl_base {
-  /* Larger than necessary for faster access.  */
-  ENUM_BITFIELD(lang_decl_selector) selector : 16;
+  ENUM_BITFIELD(lang_decl_selector) selector : 3;
   ENUM_BITFIELD(languages) language : 1;
   unsigned use_template : 2;
   unsigned not_really_extern : 1;	   /* var or fn */
   unsigned initialized_in_class : 1;	   /* var or fn */
+
   unsigned threadprivate_or_deleted_p : 1; /* var or fn */
   /* anticipated_p is no longer used for anticipated_decls (fn, type
      or template).  It is used as DECL_OMP_PRIVATIZED_MEMBER in
@@ -2660,11 +2812,22 @@ struct GTY(()) lang_decl_base {
   unsigned friend_or_tls : 1;		   /* var, fn, type or template */
   unsigned unknown_bound_p : 1;		   /* var */
   unsigned odr_used : 1;		   /* var or fn */
-  unsigned spare : 1;
   unsigned concept_p : 1;                  /* applies to vars and functions */
   unsigned var_declared_inline_p : 1;	   /* var */
   unsigned dependent_init_p : 1;	   /* var */
-  /* 2 spare bits */
+
+  unsigned module_purview_p : 1;	   /* in module purview (not GMF) */
+  unsigned module_import_p : 1;     	   /* from an import */
+  unsigned module_entity_p : 1;		   /* is in the entitity ary &
+					      hash.  */
+  /* Has specializations or members yet to load.  */
+  unsigned module_pending_specializations_p : 1;
+  unsigned module_pending_members_p : 1;
+
+  /* Is in the decl-attached hash table, (with attached decls).  */
+  unsigned attached_decls_p : 1;
+  
+  /* 10 spare bits.  */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -3338,7 +3508,8 @@ struct GTY(()) lang_decl {
 
 /* 1 iff VAR_DECL node NODE is a type-info decl.  This flag is set for
    both the primary typeinfo object and the associated NTBS name.  */
-#define DECL_TINFO_P(NODE) TREE_LANG_FLAG_4 (VAR_DECL_CHECK (NODE))
+#define DECL_TINFO_P(NODE) \
+  TREE_LANG_FLAG_4 (TREE_CHECK2 (NODE,VAR_DECL,TYPE_DECL))
 
 /* 1 iff VAR_DECL node NODE is virtual table or VTT.  We forward to
    DECL_VIRTUAL_P from the common code, as that has the semantics we
@@ -6773,6 +6973,51 @@ extern bool ctor_omit_inherited_parms		(tree);
 extern tree locate_ctor				(tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
 						 bool, tree, tree);
+/* In module.cc  */
+class module_state; /* Forward declare.  */
+inline bool modules_p () { return flag_modules != 0; }
+
+#define MK_MODULE (1 << 0)     /* This TU is a module.  */
+#define MK_GLOBAL (1 << 1)     /* Entities are in the global module.  */
+#define MK_INTERFACE (1 << 2)  /* This TU is an interface.  */
+#define MK_PARTITION (1 << 3)  /* This TU is a partition.  */
+#define MK_EXPORTING (1 << 4)  /* We are in an export region.  */
+extern unsigned module_kind;
+
+/*  MK_MODULE & MK_GLOBAL have the following combined meanings:
+ MODULE GLOBAL
+   0	  0    not a module
+   0      1    GMF of named module (we've not yet seen module-decl)
+   1      0    purview of named module
+   1      1    header unit.   */
+
+inline bool module_purview_p ()
+{ return module_kind & MK_MODULE; }
+inline bool global_purview_p ()
+{ return module_kind & MK_GLOBAL; }
+
+inline bool not_module_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == 0; }
+inline bool named_module_p ()
+{ /* This is a named module if exactly one of MODULE and GLOBAL is
+     set.  */
+  /* The divides are constant shifts!  */
+  return ((module_kind / MK_MODULE) ^ (module_kind / MK_GLOBAL)) & 1;
+}
+inline bool header_module_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == (MK_MODULE | MK_GLOBAL); }
+inline bool named_module_purview_p ()
+{ return (module_kind & (MK_MODULE | MK_GLOBAL)) == MK_MODULE; }
+inline bool module_interface_p ()
+{ return module_kind & MK_INTERFACE; }
+inline bool module_partition_p ()
+{ return module_kind & MK_PARTITION; }
+inline bool module_has_cmi_p ()
+{ return module_kind & (MK_INTERFACE | MK_PARTITION); }
+
+/* We're currently exporting declarations.  */
+inline bool module_exporting_p ()
+{ return module_kind & MK_EXPORTING; }
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);

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

* Re: [22.2/32] module flags
  2020-11-13 14:03                                           ` [22.2/32] module flags Nathan Sidwell
@ 2020-11-13 14:27                                             ` Richard Biener
  2020-11-13 16:25                                               ` Nathan Sidwell
  0 siblings, 1 reply; 68+ messages in thread
From: Richard Biener @ 2020-11-13 14:27 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: GCC Patches

On Fri, Nov 13, 2020 at 3:04 PM Nathan Sidwell <nathan@acm.org> wrote:
>
> Here are the pieces of patch 22 that add new flag bits to tree nodes and
> lang_decl structs, along with a new global indicating what fragment of a
> module we may be processing.
>
> be aware that header-units, although part of the Global Module, are
> treated as-if they are named modules but with some interesting 'may have
> duplicate' rules.  In particular all their entities are exported, and
> marked as having a purview
>
> There was a free LANG_DECL flag, which I use for DECL_MODULE_EXPORT_P,
> such a decl is being exported from somewhere.
>
> I needed to mark typeinfo types, so DECL_TINFO_P is extended to TYPE_DECLs.
>
> OVL_EXPORT_P is added to indicate that a particular member is an export.
>   This is duplicating DECL_MODULE_EXPORT_P, but it is convenient to have
> it in the overload.
>
> DECL_MODULE_PURVIEW_P -- the decl is in the purview of a module
>
> DECL_MODULE_IMPORT_P -- we got this decl from an import
>
> DECL_MODULE_ENTITY_P -- this decl is in the imported entity array &
> hash.  It may be true even if DECL_MODULE_IMPORT_P is false, because the
> current TU might be defining it.
>
> DECL_MODULE_PENDING_SPECIALIZATIONs, this template decl has
> specializations that we have not loaded (they must be loaded before we
> can instantiate the template)  such specializations can be in arbitray
> modules, not necessarily the one defining the template
>
> DECL_MODULE_PENDING_MEMBERS, likewise, we can define members in other
> modules (partitions or header units), or instantiate implicit members
> anywhere.  These need to be loaded before we can look inside this class.
>
> DECL_ATTACHED_DECLS_P, this namespace-scope decl has a set of attached
> decls for ODR purposes.  The case we handle comes from ranges:
>
> template<something> constexpr T var = [] () { return something; }
>
> That lambda is attached to 'var', it's not a different lambda in each TU.
>
> Nearly all those new flags are added to lang_decl_base.  Originally I
> had the module index there, which is why I drastically shrank
> 'selector'.  I keep the shrinkage because I don't really think it's a
> bottleneck.
>
> class module_state is defined inside module.cc, but we need to expose
> its incomplete tag.  'modules_p' is true if we're supporting modules.
> IIRC I had one bug during development where a modules-disabled
> compilation crashed.  So I'm reasonably certain that, when disabled, the
> compiler is still as stable as ever.
>
> module_kind is a set of bits indicating what kind of module we're
> processing.  non-module code will have it zero.  In module purview
> MK_MODULE will be set.  In the GMF of a named module MK_GLOBAL will be
> set (and MK_MODULE clear).  In a header unit, both are set.
>
> MK_EXPORTING is set if we're inside an 'export' either a {...} region,
> or a single decl.  MK_INTERFACE is true if we're in the interface of a
> named module (as opposed to implementation), and MK_PARTITION is true if
> we're in a partition of a named module (interface or implementation).
>
> There are a bunch of inline predicate functions to decode the various
> combinations that are useful.

 struct GTY(()) lang_decl_base {
-  /* Larger than necessary for faster access.  */
-  ENUM_BITFIELD(lang_decl_selector) selector : 16;
+  ENUM_BITFIELD(lang_decl_selector) selector : 3;
...
+  unsigned attached_decls_p : 1;
+
+  /* 10 spare bits.  */

so for "faster access' you could still make selector 8 bits, reducing
spare bits to 5.

Can you add comments (like on some other bits var / fn / type)
what kind of decls the new bits are used on?  Maybe some
bits can be overloaded if spare bits are needed.

Thanks,
Richard.

> nathan
>
> --
> Nathan Sidwell

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

* Re: [22.2/32] module flags
  2020-11-13 14:27                                             ` Richard Biener
@ 2020-11-13 16:25                                               ` Nathan Sidwell
  0 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-13 16:25 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On 11/13/20 9:27 AM, Richard Biener wrote:
> On Fri, Nov 13, 2020 at 3:04 PM Nathan Sidwell <nathan@acm.org> wrote:
>>

>   struct GTY(()) lang_decl_base {
> -  /* Larger than necessary for faster access.  */
> -  ENUM_BITFIELD(lang_decl_selector) selector : 16;
> +  ENUM_BITFIELD(lang_decl_selector) selector : 3;
> ...
> +  unsigned attached_decls_p : 1;
> +
> +  /* 10 spare bits.  */
> 
> so for "faster access' you could still make selector 8 bits, reducing
> spare bits to 5.

could do -- we always know what kind of lang_decl to expect from the 
originating tree's code.  It's only for the garbage collector that we 
need the selector. (+ the checkers)
> Can you add comments (like on some other bits var / fn / type)
> what kind of decls the new bits are used on?  Maybe some
> bits can be overloaded if spare bits are needed.

sure.  For the record it's VAR_DECL, TYPE_DECL, FUNCTION_DECL, 
CONCEPT_DECL, TEMPLATE_DECL, NAMESPACE_DECL (that's to many for a 
TREE_CHECK, we only go to 5).

> 
> Thanks,
> Richard.
> 
>> nathan
>>
>> --
>> Nathan Sidwell


-- 
Nathan Sidwell

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

* Re: [05/32] cpp files
  2020-11-03 21:14         ` [05/32] cpp files Nathan Sidwell
@ 2020-11-17  1:27           ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-17  1:27 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener


On 11/3/20 2:14 PM, Nathan Sidwell wrote:
> As I mentioned in patch 03, include translation is a thing.  This
> amends the file handling to determine that, and for resolving explicit
> imports of header-units
>
> The logic for locating a header unit is the same as locating a
> header-file.  Except there's a final step of mapping the header-file
> name to a compiled module interface.  That latter step is handled
> elsewhere.
>
> Also, as one needs an explicit compile step to build a header-unit, we
> need a way of telling the preprocessor that the main file is itself a
> header, and it should locate the appropriate place on the INCLUDE path
> so INCLUDE_NEXT works.
>
> nathan
>
>
> 05-cpp-files.diff
>
OK with a ChangeLog entry, of course.


jeff



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

* Re: [32/32] fixinclude
  2020-11-04  0:04                                                               ` David Edelsohn
@ 2020-11-18 18:36                                                                 ` Nathan Sidwell
  0 siblings, 0 replies; 68+ messages in thread
From: Nathan Sidwell @ 2020-11-18 18:36 UTC (permalink / raw)
  To: GCC Patches; +Cc: David Edelsohn, Richard Biener

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

This is what I've pushed.

This fixes an ODR violation in the AIX headers that is detected by C++
modules.  While unnamed structs with typedef names for linkage
purposes are accepted, this case is an anonymous struct	without	such a
typedef name -- the typedef is attached to the pointer-to-struct type.
Fixed by naming the struct.

         fixincludes/
         * inclhack.def (aix_physaddr_t): New.
         * fixincl.x: Regenerated.

nathan

-- 
Nathan Sidwell

[-- Attachment #2: 32-aix-fixincl.diff --]
[-- Type: text/x-patch, Size: 3955 bytes --]

diff --git c/fixincludes/fixincl.x w/fixincludes/fixincl.x
index 758d5620641..21439652bce 100644
--- c/fixincludes/fixincl.x
+++ w/fixincludes/fixincl.x
@@ -2,11 +2,11 @@
  *
  * DO NOT EDIT THIS FILE   (fixincl.x)
  *
- * It has been AutoGen-ed  October  3, 2020 at 11:40:52 PM by AutoGen 5.18
+ * It has been AutoGen-ed  October 21, 2020 at 10:43:22 AM by AutoGen 5.18.16
  * From the definitions    inclhack.def
  * and the template file   fixincl
  */
-/* DO NOT SVN-MERGE THIS FILE, EITHER Sat Oct  3 23:40:52 UTC 2020
+/* DO NOT SVN-MERGE THIS FILE, EITHER Wed Oct 21 10:43:22 EDT 2020
  *
  * You must regenerate it.  Use the ./genfixes script.
  *
@@ -15,7 +15,7 @@
  * certain ANSI-incompatible system header files which are fixed to work
  * correctly with ANSI C and placed in a directory that GNU C will search.
  *
- * This file contains 259 fixup descriptions.
+ * This file contains 260 fixup descriptions.
  *
  * See README for more information.
  *
@@ -1247,6 +1247,43 @@ static const char* apzAix_Rwlock_Initializer_1Patch[] = {
 {{ \\\n",
     (char*)NULL };
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Description of Aix_Physadr_T fix
+ */
+tSCC zAix_Physadr_TName[] =
+     "aix_physadr_t";
+
+/*
+ *  File name selection pattern
+ */
+tSCC zAix_Physadr_TList[] =
+  "sys/types.h\0";
+/*
+ *  Machine/OS name selection pattern
+ */
+tSCC* apzAix_Physadr_TMachs[] = {
+        "*-*-aix*",
+        (const char*)NULL };
+
+/*
+ *  content selection pattern - do fix if pattern found
+ */
+tSCC zAix_Physadr_TSelect0[] =
+       "typedef[ \t]*struct[ \t]*([{][^}]*[}][ \t]*\\*[ \t]*physadr_t;)";
+
+#define    AIX_PHYSADR_T_TEST_CT  1
+static tTestDesc aAix_Physadr_TTests[] = {
+  { TT_EGREP,    zAix_Physadr_TSelect0, (regex_t*)NULL }, };
+
+/*
+ *  Fix Command Arguments for Aix_Physadr_T
+ */
+static const char* apzAix_Physadr_TPatch[] = {
+    "format",
+    "typedef struct __physadr_s %1",
+    (char*)NULL };
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * *
  *
  *  Description of Aix_Pthread fix
@@ -10521,9 +10558,9 @@ static const char* apzX11_SprintfPatch[] = {
  *
  *  List of all fixes
  */
-#define REGEX_COUNT          297
+#define REGEX_COUNT          298
 #define MACH_LIST_SIZE_LIMIT 187
-#define FIX_COUNT            259
+#define FIX_COUNT            260
 
 /*
  *  Enumerate the fixes
@@ -10555,6 +10592,7 @@ typedef enum {
     AIX_MUTEX_INITIALIZER_1_FIXIDX,
     AIX_COND_INITIALIZER_1_FIXIDX,
     AIX_RWLOCK_INITIALIZER_1_FIXIDX,
+    AIX_PHYSADR_T_FIXIDX,
     AIX_PTHREAD_FIXIDX,
     AIX_STDINT_1_FIXIDX,
     AIX_STDINT_2_FIXIDX,
@@ -10921,6 +10959,11 @@ tFixDesc fixDescList[ FIX_COUNT ] = {
      AIX_RWLOCK_INITIALIZER_1_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
      aAix_Rwlock_Initializer_1Tests,   apzAix_Rwlock_Initializer_1Patch, 0 },
 
+  {  zAix_Physadr_TName,    zAix_Physadr_TList,
+     apzAix_Physadr_TMachs,
+     AIX_PHYSADR_T_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
+     aAix_Physadr_TTests,   apzAix_Physadr_TPatch, 0 },
+
   {  zAix_PthreadName,    zAix_PthreadList,
      apzAix_PthreadMachs,
      AIX_PTHREAD_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
diff --git c/fixincludes/inclhack.def w/fixincludes/inclhack.def
index 47eb236586c..80c9adfb07c 100644
--- c/fixincludes/inclhack.def
+++ w/fixincludes/inclhack.def
@@ -720,6 +720,20 @@ fix = {
 		"{ \\\\\n";
 };
 
+
+/* On AIX 'typedef struct {<stuff>} * physadr_t;' needs to give the struct a
+   name for linkage purposes.  Fortunately it is on exactly one
+   line.  */
+fix = {
+    hackname  = aix_physadr_t;
+    mach      = "*-*-aix*";
+    files     = sys/types.h;
+    select    = "typedef[ \t]*struct[ \t]*([{][^}]*[}][ \t]*\\*[ \t]*physadr_t;)";
+    c_fix     = format;
+    c_fix_arg = "typedef struct __physadr_s %1";
+    test_text = "typedef struct __physadr_s {";
+};
+
 /*
  *  pthread.h on AIX 4.3.3 tries to define a macro without whitspace
  *  which violates a requirement of ISO C.

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

* Re: [09/33] core diagnostics
  2020-11-03 20:03                   ` [09/33] " Nathan Sidwell
@ 2020-11-21 16:56                     ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-21 16:56 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/3/20 1:03 PM, Nathan Sidwell wrote:
> Here are the changes for gcc/configure.ac (config.h.in and configure
> get rebuilt).  This is adding smarts to check for networking features,
> so that a network-aware module mapper can be built.
>
>
>
> 10-core-config.diff
>
OK with a ChangeLog.

jeff


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

* Re: [09/32] core diagnostics
  2020-11-03 21:14                 ` [09/32] core diagnostics Nathan Sidwell
@ 2020-11-21 16:57                   ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-21 16:57 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/3/20 2:14 PM, Nathan Sidwell wrote:
> The 'included from ...' chain that one gets at the start of a
> diagnostic needs extending to include importing.  There are a few
> combinations to handle, but nothing particularly exciting.
>
>
> 09-core-diag.diff
>
OK with a ChangeLog
jeff


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

* Re: [06/32] cpp macros
  2020-11-03 21:14           ` [06/32] cpp macros Nathan Sidwell
@ 2020-11-23 21:13             ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-23 21:13 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/3/20 2:14 PM, Nathan Sidwell wrote:
>
> Header units can provide macros to an importer.  For efficiency that
> is done lazily.  When we import a header unit we mark the identifers
> it defines as significant.  It's only when we need the macro
> definition (including in #ifdef &| defined (X) processing) that we
> resolve the macro.  We already had lazy (builtin) macros.  This
> extends it to deferred macros.  Each deferred macro has an index the
> resolver can use.   on an LP64 host that index does not inlarge the
> node structure.
>
> As a macro can be undefined by this mechanism, the users of this
> deferred interface must be prepared to handle 'not a macro after all'
>
>
>
>
> 06-cpp-macro.diff
>
>
OK with the usual ChangeLog note.
jeff


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

* Re: [24/32] module mapper
  2020-11-09 15:39                                                 ` Nathan Sidwell
@ 2020-11-24  7:18                                                   ` Boris Kolpackov
  0 siblings, 0 replies; 68+ messages in thread
From: Boris Kolpackov @ 2020-11-24  7:18 UTC (permalink / raw)
  To: gcc-patches

Nathan Sidwell <nathan@acm.org> writes:

> These are needed as they also serve to inform the mapper of a dependency 
> edge.

Ok, that makes sense (and thanks for making it optional via a flag).

One thing that is still missing in this area is the dependency on
stdc-predef.h. In other words, we now can get everything via the
mapper as we can get with -M* except for this file.

Do you think it would be possible to report it with INCLUDE-TRANSLATE
(with the only valid response being BOOL FALSE)? There could also be
a flag to indicate that it's not really "translatable".

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

* Re: [12/32] user documentation
  2020-11-03 21:15                     ` [12/32] user documentation Nathan Sidwell
@ 2020-11-25 19:17                       ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-25 19:17 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/3/20 2:15 PM, Nathan Sidwell wrote:
> This is the user documentation.
>
>
> 12-core-doc.diff
>
I think this is fine. 
jeff


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

* Re: [13/32] new options
  2020-11-03 21:15                       ` [13/32] new options Nathan Sidwell
@ 2020-11-25 19:27                         ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-25 19:27 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/3/20 2:15 PM, Nathan Sidwell wrote:
> Here are the new options, along with the C++ lang-spec changes.
>
> Modules is enabled by -fmodules-ts, it is not implicitly enabled by
> -std=c++20.  Usually that's the only option you need to add for a
> module-aware build.
>
> to build a header unit you can either add -fmodule-header to a c++
> build, or you can set the language to be c++-header and add -fmodules-ts:
>
> g++ -x c++-header -fmodules-ts my-header-file
>
> to search the user or system include paths select c++-user-header of
> c++-system-header as the language.
>
> enabling -fmodules-ts will disable PCH, they do not play well
> together. There is a potential issue down the road when we implicitly
> enable modules.  At that point building a header-unit could become
> indistinguishable from building a PCH.  Perhaps we should consider
> phasing in an explicit PCH option?
In a modules capable world, how much value still exists with the old PCH
scheme?   The old PCH scheme can't work in some enviroments anyway
(anyone using ASLR & PIE).

I'm going to assume the spec changs are OK.  My brain always melts when
I start looking at them.

OK

Jeff


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

* Re: [24/32] module mapper
  2020-11-12 16:24                                               ` Nathan Sidwell
@ 2020-11-30 16:18                                                 ` Jeff Law
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff Law @ 2020-11-30 16:18 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches, Jason Merrill, Richard Biener



On 11/12/20 9:24 AM, Nathan Sidwell wrote:
> On 11/3/20 4:17 PM, Nathan Sidwell wrote:
>> this is the module mapper client and server pieces.  It features a
>> default resolver that can read a text file, or generate default
>> mappings from module name to cmi name.
>
> Richard rightly suggested on IRC that the sample server for the module
> mapper shouldn't be in the gcc/cp dir.  It happened to be that way
> because it started out much more closely coupled, but then it grew legs.
>
> So this patch creates a new c++tools toplevel directory and places the
> mapper-server and its default resolver there.  That means more changes
> to the toplevel Makefile.def and Makefile.tpl (I've not included the
> regenerated Makefile.in, nor other generated files in gcc/ and
> c++tools in this diff.)
>
> We still need to build the default resolver when building cc1plus, and
> I've placed mapper-resolver.cc there, as a simple #include forwarder
> to the source in c++tools.  I also replace 'gcc/cp/mapper.h' with a
> client-specific 'gcc/cp/mapper-client.h'.  (mapper-client is only
> linked into cc1plus, so gcc/cp seems the right place for it.)
>
> The sample server relies on gcc/version.o to pick up its version
> number, and I place it in the libexecsubdir that we place cc1plus.  I
> wasn't comfortable placing it in the install location of g++ itself. 
> I call it a sample server for a reason :)
>
> I will of course provide changelog when committing.
>
> nathan
>
>
> 24a-c++-mapper.diff
>


So I think you should just own these bits.  You're going to be far more
familiar with them than anyone else involved in GCC work :-)  So, OK for
the trunk as well as any followups into the module mapper.

jeff


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

end of thread, other threads:[~2020-11-30 16:19 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <7fc9e868-3db9-4972-ed67-6ff249f549c3@acm.org>
2020-11-03 21:13 ` [01/32] langhooks Nathan Sidwell
2020-11-06 19:58   ` Jeff Law
     [not found] ` <c14417b3-4286-d877-f6e2-925bfc754684@acm.org>
2020-11-03 19:39   ` [02/33] linemaps Nathan Sidwell
2020-11-03 21:13   ` [02/32] linemaps Nathan Sidwell
2020-11-06 20:10     ` Jeff Law
     [not found]   ` <0bdf00a8-d8ad-9e97-134d-6668c0e8c86b@acm.org>
2020-11-03 21:13     ` [03/32] cpp-callbacks Nathan Sidwell
2020-11-06 20:10       ` Jeff Law
     [not found]     ` <c55d12d9-e966-5b36-1538-98c9c4b418c8@acm.org>
2020-11-03 21:13       ` [04/32] cpp lexer Nathan Sidwell
2020-11-03 23:08         ` Joseph Myers
2020-11-03 23:21           ` Nathan Sidwell
2020-11-06 20:23         ` Jeff Law
2020-11-06 21:06           ` Nathan Sidwell
2020-11-09  6:15             ` Boris Kolpackov
2020-11-09 13:54             ` Nathan Sidwell
     [not found]       ` <85996243-86cf-88b4-5b3b-451eaf3a0df6@acm.org>
2020-11-03 21:14         ` [05/32] cpp files Nathan Sidwell
2020-11-17  1:27           ` Jeff Law
     [not found]         ` <ca22e13a-6869-bc48-d7c4-a04128f3fcb8@acm.org>
2020-11-03 21:14           ` [06/32] cpp macros Nathan Sidwell
2020-11-23 21:13             ` Jeff Law
     [not found]           ` <4d14b230-3263-9a13-3159-c4853f282761@acm.org>
2020-11-03 21:14             ` [07/32] cpp main Nathan Sidwell
2020-11-10 23:18               ` Jeff Law
     [not found]             ` <688bd28f-5998-0def-8c40-03b817832d63@acm.org>
2020-11-03 21:14               ` [08/32] cpp mkdeps Nathan Sidwell
2020-11-10 23:20                 ` Jeff Law
     [not found]               ` <89819c10-e86d-9b01-5673-5223a525a135@acm.org>
2020-11-03 21:14                 ` [09/32] core diagnostics Nathan Sidwell
2020-11-21 16:57                   ` Jeff Law
     [not found]                 ` <35879e15-d74a-c664-4d44-15f4b3783d77@acm.org>
2020-11-03 20:03                   ` [09/33] " Nathan Sidwell
2020-11-21 16:56                     ` Jeff Law
2020-11-03 21:14                   ` [11/32] instrumentation Nathan Sidwell
2020-11-06 20:28                     ` Jeff Law
     [not found]                   ` <9ae23c4c-67a5-a267-c939-5a96e9488612@acm.org>
2020-11-03 21:15                     ` [12/32] user documentation Nathan Sidwell
2020-11-25 19:17                       ` Jeff Law
     [not found]                     ` <c1c50ae5-44d5-4b2d-dba8-76b459b7d994@acm.org>
2020-11-03 21:15                       ` [13/32] new options Nathan Sidwell
2020-11-25 19:27                         ` Jeff Law
     [not found]                       ` <c0a0b999-f96e-177c-b393-9eaf8d1aa520@acm.org>
2020-11-03 21:15                         ` [14/32] new keywords Nathan Sidwell
2020-11-06 20:29                           ` Jeff Law
     [not found]                         ` <6666545b-0583-4812-4745-d51994465818@acm.org>
2020-11-03 21:15                           ` [15/32] new C++ lexer Nathan Sidwell
     [not found]                           ` <d6ae6a29-0da0-06a5-d86c-1b0dd4bd96e4@acm.org>
2020-11-03 21:15                             ` [16/32] new C++ infrastructure Nathan Sidwell
     [not found]                             ` <d3149435-8b5e-7c79-f4bb-80238fdfcf72@acm.org>
2020-11-03 21:15                               ` [17/32] new C++ constexpr bits Nathan Sidwell
     [not found]                               ` <1ab99df5-3997-0895-c979-f8529f476df7@acm.org>
2020-11-03 21:16                                 ` [18/32] new C++ template bits Nathan Sidwell
     [not found]                                 ` <97e9477b-7173-b7f9-a884-616b972c57ba@acm.org>
2020-11-03 21:16                                   ` [19/32] global trees Nathan Sidwell
2020-11-06 20:29                                     ` Jeff Law
     [not found]                                   ` <01f091a5-cd8b-60b6-9552-2318ecd07025@acm.org>
2020-11-03 21:16                                     ` [20/32] global constructor Nathan Sidwell
     [not found]                                     ` <a70c9177-136e-0d52-1c96-c8093588f57b@acm.org>
2020-11-03 21:16                                       ` [21/32] miscelaneous Nathan Sidwell
2020-11-05 13:30                                         ` Richard Biener
2020-11-06 17:08                                           ` Nathan Sidwell
     [not found]                                       ` <5c533ebe-440d-188e-5bdb-38c14898852c@acm.org>
2020-11-03 21:16                                         ` [22/32] miscelaneous c++ bits Nathan Sidwell
2020-11-13 13:41                                           ` Nathan Sidwell
2020-11-13 14:03                                           ` [22.2/32] module flags Nathan Sidwell
2020-11-13 14:27                                             ` Richard Biener
2020-11-13 16:25                                               ` Nathan Sidwell
     [not found]                                         ` <9e6ec23c-6b36-de70-7630-55562583696f@acm.org>
2020-11-03 21:17                                           ` [23/32] libcody Nathan Sidwell
     [not found]                                           ` <c839f3f8-9ce6-0c28-b981-d81f236a3a34@acm.org>
2020-11-03 21:17                                             ` [24/32] module mapper Nathan Sidwell
2020-11-09  6:42                                               ` Boris Kolpackov
2020-11-09 15:39                                                 ` Nathan Sidwell
2020-11-24  7:18                                                   ` Boris Kolpackov
2020-11-12 16:24                                               ` Nathan Sidwell
2020-11-30 16:18                                                 ` Jeff Law
     [not found]                                             ` <efe68976-5562-ef6a-7e86-b1daeae73670@acm.org>
2020-11-03 21:17                                               ` [25/32] modules! Nathan Sidwell
2020-11-04 12:48                                                 ` Nathan Sidwell
     [not found]                                               ` <3d138aa4-df19-df5d-54c6-ec7299749f0f@acm.org>
2020-11-03 21:17                                                 ` [26/33] name-lookup Nathan Sidwell
     [not found]                                                 ` <df8d76cf-b00f-6b4c-3b8c-132843c15d62@acm.org>
2020-11-03 21:17                                                   ` [27/32] parser Nathan Sidwell
     [not found]                                                   ` <e67492d5-3be5-0993-538d-b798875c5e89@acm.org>
2020-11-03 21:17                                                     ` [28/32] lang hook implementation Nathan Sidwell
     [not found]                                                     ` <b378cdb3-fe6f-956a-84a1-23af87ce6695@acm.org>
2020-11-03 21:18                                                       ` [29/32] make-lang.in Nathan Sidwell
     [not found]                                                       ` <bedd1f52-8a2c-b257-74bc-caf0cc1d0589@acm.org>
2020-11-03 21:18                                                         ` [30/32] test harness Nathan Sidwell
2020-11-06 20:30                                                           ` Jeff Law
     [not found]                                                         ` <0a62f316-7c23-3492-f7c3-9c1653a61a75@acm.org>
2020-11-03 21:18                                                           ` [31/32] test suite Nathan Sidwell
     [not found]                                                           ` <f4bda23c-7fa1-5a1b-e970-9fae9b2575b6@acm.org>
2020-11-03 21:18                                                             ` [32/32] fixinclude Nathan Sidwell
2020-11-04  0:04                                                               ` David Edelsohn
2020-11-18 18:36                                                                 ` Nathan Sidwell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).