along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
-/* This file intentionally left empty of all but barest minium. */
+/* 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 binding_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
#include "attribs.h"
#include "intl.h"
#include "langhooks.h"
+#include "mapper-client.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));
+}
/* During duplicate detection we need to tell some comparators that
these are equivalent. */
/* Id for dumping module information. */
int module_dump_id;
-/* What the current TU is. */
-unsigned module_kind;
+/* We have a special module owner. */
+#define MODULE_UNKNOWN (~0U) /* Not yet known. */
-module_state *
-get_module (tree, module_state *, bool)
+/* 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)
{
- return nullptr;
+ 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);
}
-const char *
-module_name (unsigned, bool)
+/* 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)
{
- return nullptr;
+ 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;
}
-bitmap
-get_import_bitmap ()
+/* 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 nullptr;
+ return XRESIZEVAR (char, ptr, needed);
}
void
-mangle_module (int, bool)
+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
-mangle_module_fini ()
+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;
+ }
}
-int
-module_initializer_kind ()
+/* Finish a set of bools. */
+
+void
+bytes_out::bflush ()
{
- return 0;
+ if (bit_pos)
+ {
+ u32 (bit_val);
+ lengths[2] += bit_flush ();
+ }
+ spans[2]++;
+ is_set = -1;
}
void
-module_add_import_initializers ()
+bytes_in::bflush ()
{
+ if (bit_pos)
+ bit_flush ();
}
-int
-get_originating_module (tree, bool)
+/* 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 ()
{
- return 0;
+ bit_val = u32 ();
}
-unsigned
-get_importing_module (tree, bool)
+/* 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)
{
- return 0;
+ 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
-module_may_redeclare (tree)
+bytes_in::b ()
{
- return true;
+ 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
-set_instantiating_module (tree)
+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
-set_defining_module (tree)
+bytes_out::c (unsigned char v)
{
+ if (char *ptr = write (1))
+ *ptr = v;
}
-tree
-get_originating_module_decl (tree decl)
+int
+bytes_in::c ()
{
- return decl;
+ 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
-set_originating_module (tree, bool)
+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;
+ }
+ }
}
-void
-maybe_attach_decl (tree, tree)
+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
-lazy_load_binding (unsigned, tree, tree, binding_slot *)
+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;
+ }
+ }
}
-void
-lazy_load_specializations (tree)
+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
-lazy_load_members (tree)
+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;
+ }
+ }
}
-bool
-lazy_specializations_p (unsigned, bool, bool)
+HOST_WIDE_INT
+bytes_in::wi ()
{
- return false;
+ 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;
}
-bitmap
-visible_instantiation_path (bitmap *)
+/* unsigned wide ints are just written as signed wide ints. */
+
+inline void
+bytes_out::wu (unsigned HOST_WIDE_INT v)
{
- return nullptr;
+ wi ((HOST_WIDE_INT) v);
}
-void
-import_module (module_state *, location_t, bool, tree, cpp_reader *)
+inline unsigned HOST_WIDE_INT
+bytes_in::wu ()
{
+ return (unsigned HOST_WIDE_INT) wi ();
}
-void
-declare_module (module_state *, location_t, bool, tree, cpp_reader *)
+/* 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);
}
-module_state *
-preprocess_module (module_state *, unsigned, bool, bool, bool, cpp_reader *)
+inline size_t
+bytes_in::z ()
{
- return nullptr;
+ if (sizeof (size_t) == sizeof (unsigned))
+ return u ();
+ else
+ return wu ();
}
-void
-preprocessed_module (cpp_reader *)
+/* Buffer simply memcpied. */
+void *
+bytes_out::buf (size_t len)
{
+ align (sizeof (void *) * 2);
+ return write (len);
}
void
-module_begin_main_file (cpp_reader *, line_maps *, const line_map_ordinary *)
+bytes_out::buf (const void *src, size_t len)
{
+ if (void *ptr = buf (len))
+ memcpy (ptr, src, len);
}
-void
-init_modules (cpp_reader *)
+const void *
+bytes_in::buf (size_t len)
{
- /* Do not turn on yet. */
- if (modules_p ())
- fatal_error (input_location,
- "Shtopp! What are you doing? This is not ready yet.");
+ 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
-maybe_check_all_macros (cpp_reader *)
+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
-finish_module_processing (cpp_reader *)
+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
-fini_modules ()
+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
-handle_module_option (unsigned, const char *, int)
+elf::end ()
{
- return false;
+ /* Close the stream and free the section table. */
+ if (fd >= 0 && close (fd))
+ set_error (errno);
+ fd = -1;
+
+ return !get_error ();
}
-void
-module_preprocess_options (cpp_reader *)
+/* 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 *>
+ (§ab.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 *> (§ab.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 (§ab, 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 (§ab, 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_PARTIAL, /* A partial 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_DIRECT_HWM = EK_PARTIAL + 1,
+
+ 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_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_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:
+ 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_partial_entities (vec<tree, va_gc> *);
+ 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", "partial", "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 = 0x4, /* In decl table. */
+ MK_tmpl_alias_mask = 0x2, /* Also in type table */
+
+ 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_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, NULL,
+
+ "decl spec", "decl tmpl spec", /* 20,21 decl (template). */
+ "alias spec", NULL, /* 22,23 alias. */
+ NULL, NULL, NULL, NULL,
+ 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, binding_slot *mslot);
+ /* Lazily read a section. */
+ bool lazy_load (unsigned index, binding_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, cpp_reader *,
+ 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<binding_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. */
+static GTY(()) vec<tree, va_gc> *class_members;
+
+/* The same problem exists for class template partial
+ specializations. Now that we have constraints, the invariant of
+ expecting them in the instantiation table no longer holds. One of
+ the constrained partial specializations will be there, but the
+ others not so much. It's not even an unconstrained partial
+ spacialization in the table :( so any partial template declaration
+ is added to this list too. */
+static GTY(()) vec<tree, va_gc> *partial_specializations;
+
+/********************************************************************/
+
+/* 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);
+ if (VAR_OR_FUNCTION_DECL_P (t))
+ WB (lang->u.base.module_pending_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);
+ if (VAR_OR_FUNCTION_DECL_P (t))
+ RB (lang->u.base.module_pending_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))
+ {
+ /* The only types we write also have TYPE_NON_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. I think we'll need to walk the blocks
+ // of the owning function_decl's abstract origin in tandem, to
+ // generate the locating data needed?
+ 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)
+ {
+ // FIXME: These are from tpl_parm_value's 'type' writing.
+ // Perhaps it should just be doing them directly?
+ gcc_checking_assert (code == TEMPLATE_TYPE_PARM
+ || code == TEMPLATE_TEMPLATE_PARM
+ || code == BOUND_TEMPLATE_TEMPLATE_PARM);
+ gcc_checking_assert (!TYPE_CACHED_VALUES_P (t));
+ WT (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 BINDING_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 type that doesn't have 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 BINDING_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;
+ binding_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
+ && VAR_OR_FUNCTION_DECL_P (inner)
+ && DECL_LANG_SPECIFIC (inner)
+ && DECL_MODULE_ATTACHMENTS_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
+ && VAR_OR_FUNCTION_DECL_P (inner)
+ && DECL_LANG_SPECIFIC (inner)
+ && DECL_MODULE_ATTACHMENTS_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);
+ spec_entry elt =
+ {TI_TEMPLATE (ti), TI_ARGS (ti), TREE_TYPE (inner)};
+ tree texist = match_mergeable_specialization (false, &elt);
+ 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->get_entity_kind () == depset::EK_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 ())
+ {
+ binding_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_PARTIAL:
+ mk = MK_partial;
+ break;
+
+ case depset::EK_DECL:
+ {
+ 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_MODULE_ATTACHMENTS_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 = MK_type_spec;
+ else if (dep->is_alias ())
+ mk = MK_alias_spec;
+ else
+ mk = 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);
+ }
+ }
+ break;
+ }
+
+ return mk;
+}
+
+
+/* The container of DECL -- not necessarily its context! */
+
+tree
+trees_out::decl_container (tree decl)
+{
+ int use_tpl;
+ tree tpl = NULL_TREE;
+ if (tree template_info = node_template_info (decl, use_tpl))
+ tpl = TI_TEMPLATE (template_info);
+ if (tpl == decl)
+ tpl = nullptr;
+
+ /* Stream the template we're instantiated from. */
+ tree_node (tpl);
+
+ 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);
+
+ tree_node (container);
+
+ return container;
+}
+
+tree
+trees_in::decl_container ()
+{
+ /* The maybe-template. */
+ (void)tree_node ();
+
+ tree container = tree_node ();
+
+ return 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));
+ if (mk & MK_tmpl_decl_mask)
+ if (flag_concepts && TREE_CODE (inner) == VAR_DECL)
+ {
+ /* Variable template partial specializations might need
+ constraints (see spec_hasher::equal). It's simpler to
+ write NULL when we don't need them. */
+ tree constraints = NULL_TREE;
+
+ if (uses_template_parms (entry->args))
+ constraints = get_constraints (inner);
+ tree_node (constraints);
+ }
+
+ if (CHECKING_P)
+ {
+ /* Make sure we can locate the decl. */
+ tree existing = match_mergeable_specialization
+ (bool (mk & MK_tmpl_decl_mask), entry, false);
+
+ gcc_assert (existing);
+ if (mk & MK_tmpl_decl_mask)
+ {
+ if (mk & MK_tmpl_alias_mask)
+ /* It should be in both tables. */
+ gcc_assert (match_mergeable_specialization (false, entry, false)
+ == 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 (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)
+{
+ tree found = NULL_TREE;
+ for (ovl_iterator iter (ovl); !found && 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. */
+ found = 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))
+ {
+ found = match;
+ break;
+ }
+ goto again;
+ }
+ break;
+
+ case FUNCTION_DECL:
+ map_context_from = d_inner;
+ map_context_to = m_inner;
+ 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_UNDECLARED_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 (key.constraints, m_reqs))
+ found = match;
+ }
+ map_context_from = map_context_to = NULL_TREE;
+ 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))
+ found = match;
+ }
+ break;
+
+ default:
+ found = match;
+ break;
+ }
+ }
+
+ return found;
+}
+
+/* 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)
+ {
+ spec_entry spec;
+ spec.tmpl = tree_node ();
+ spec.args = tree_node ();
+ unsigned flags = u ();
+
+ DECL_NAME (decl) = DECL_NAME (spec.tmpl);
+ DECL_CONTEXT (decl) = DECL_CONTEXT (spec.tmpl);
+ DECL_NAME (inner) = DECL_NAME (decl);
+ DECL_CONTEXT (inner) = DECL_CONTEXT (decl);
+
+ spec.spec = decl;
+ if (mk & MK_tmpl_tmpl_mask)
+ {
+ if (inner == decl)
+ return error_mark_node;
+ spec.spec = inner;
+ }
+ tree constr = NULL_TREE;
+ bool is_decl = mk & MK_tmpl_decl_mask;
+ if (is_decl)
+ {
+ if (flag_concepts && TREE_CODE (inner) == VAR_DECL)
+ {
+ constr = tree_node ();
+ if (constr)
+ set_constraints (inner, constr);
+ }
+ }
+ else
+ {
+ if (mk == MK_type_spec && inner != decl)
+ return error_mark_node;
+ spec.spec = type;
+ }
+ existing = match_mergeable_specialization (is_decl, &spec);
+ if (constr)
+ /* We'll add these back later, if this is the new decl. */
+ remove_constraints (inner);
+
+ if (!existing)
+ add_mergeable_specialization (spec.tmpl, spec.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 (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)
+ && VAR_OR_FUNCTION_DECL_P (name)
+ && DECL_MODULE_ATTACHMENTS_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)
+ BINDING_VECTOR_PARTITION_DUPS_P (mvec) = true;
+ else
+ BINDING_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)
+ {
+ spec_entry spec;
+ spec.tmpl = tree_node ();
+ spec.args = tree_node ();
+ spec.spec = decl;
+ unsigned flags = u ();
+
+ tree e = match_mergeable_specialization (true, &spec);
+ if (!e)
+ add_mergeable_specialization (spec.tmpl, spec.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));
+ }
+
+ gcc_checking_assert (!map_context_from);
+ /* This mapping requres the new decl on the lhs and the existing
+ entity on the rhs of the comparitors below. */
+ map_context_from = inner;
+ map_context_to = STRIP_TEMPLATE (existing);
+
+ 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 (d_ret, e_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 (d_args), TREE_VALUE (e_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 (d_spec, e_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 (decl), TREE_TYPE (existing)))
+ {
+ mismatch:
+ map_context_from = map_context_to = NULL_TREE;
+ if (DECL_IS_UNDECLARED_BUILTIN (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;
+ }
+ }
+
+ map_context_from = map_context_to = NULL_TREE;
+
+ if (DECL_IS_UNDECLARED_BUILTIN (existing)
+ && !DECL_IS_UNDECLARED_BUILTIN (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);
+ /* Mark the class members that are not type-decls and cannot have
+ independent definitions. */
+ for (tree member = TYPE_FIELDS (type); member; member = DECL_CHAIN (member))
+ if (TREE_CODE (member) == FIELD_DECL
+ || TREE_CODE (member) == USING_DECL
+ /* A cloned enum-decl from 'using enum unrelated;' */
+ || (TREE_CODE (member) == CONST_DECL
+ && DECL_CONTEXT (member) == type))
+ {
+ 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)
+ {
+ 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 might not be in the hash table, because
+ there can be multiple differently-constrained variants.
+
+ 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)
+ {
+ /* Eagerly create an empty redirect. The following
+ make_dependency call could cause hash reallocation,
+ and invalidate slot's value. */
+ depset *redirect = make_entity (decl, EK_REDIRECT);
+
+ /* Redirects are never reached -- always snap to their target. */
+ redirect->set_flag_bit<DB_UNREACHED_BIT> ();
+
+ *slot = redirect;
+
+ depset *tmpl_dep = make_dependency (partial, EK_PARTIAL);
+ gcc_checking_assert (tmpl_dep->get_entity_kind () == EK_PARTIAL);
+
+ redirect->deps.safe_push (tmpl_dep);
+
+ return redirect;
+ }
+ }
+
+ 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 if (CHECKING_P)
+ /* The template_result should otherwise not be in the
+ table, or be an empty redirect (created above). */
+ if (auto *eslot = entity_slot (DECL_TEMPLATE_RESULT (decl), false))
+ gcc_checking_assert ((*eslot)->get_entity_kind () == EK_REDIRECT
+ && !(*eslot)->deps.length ());
+ }
+
+ 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 as-if 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;
+}
+
+void
+depset::hash::add_partial_entities (vec<tree, va_gc> *partial_classes)
+{
+ for (unsigned ix = 0; ix != partial_classes->length (); ix++)
+ {
+ tree inner = (*partial_classes)[ix];
+
+ depset *dep = make_dependency (inner, depset::EK_DECL);
+
+ if (dep->get_entity_kind () == depset::EK_REDIRECT)
+ /* We should have recorded the template as a partial
+ specialization. */
+ gcc_checking_assert (dep->deps[0]->get_entity_kind ()
+ == depset::EK_PARTIAL);
+ else
+ /* It was an explicit specialization, not a partial one. */
+ gcc_checking_assert (dep->get_entity_kind ()
+ == depset::EK_SPECIALIZATION);
+ }
+}
+
+/* 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
+ (!match_mergeable_specialization (true, entry, false)
+ == (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;
+}
+
+/* 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;
+ 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 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);
+ if (dep->get_entity_kind () == depset::EK_REDIRECT)
+ dep = dep->deps[0];
+ else if (dep->get_entity_kind () == depset::EK_SPECIALIZATION)
+ {
+ dep->set_special ();
+ dep->deps.safe_push (reinterpret_cast<depset *> (entry));
+ if (!decl_p)
+ dep->set_flag_bit<DB_TYPE_SPEC_BIT> ();
+ }
+
+ if (needs_reaching)
+ dep->set_flag_bit<DB_UNREACHED_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_DIRECT_HWM);
+
+ 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];
+ 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:
+ case depset::EK_PARTIAL:
+ 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);
+ binding_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, cpp_reader *reader,
+ 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. We should have a way of overriding that. */
+ 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 stampy;
+ auto kind = cpp_get_date (reader, &stampy);
+ if (kind != CPP_time_kind::UNKNOWN)
+ {
+ struct tm *time;
+
+ time = gmtime (&stampy);
+ readme.print_time ("build", time, "UTC");
+
+ if (kind == CPP_time_kind::DYNAMIC)
+ {
+ time = localtime (&stampy);
+ readme.print_time ("local", time,
+#if defined (__USE_MISC) || defined (__USE_BSD) /* Is there a better way? */
+ time->tm_zone
+#else
+ ""
+#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! */
+ case depset::EK_PARTIAL: /* Hey, let's do it again! */
+ 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_PARTIAL:
+ 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_PARTIAL:
+ 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 structural equality. */
+ comparing_specializations++;
+
+ /* 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_specializations--;
+
+ 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)
+ {
+ binding_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:
+ case depset::EK_PARTIAL:
+ 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;
+
+ binding_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 & partials are keyed to their primary template.
+ Members are keyed to their context.
+
+ For specializations & partials, primary templates are keyed to the
+ (namespace name) of their originating decl (because that's the only
+ handle we have). */
+
+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 = false;
+
+
+ if (kind == depset::EK_SPECIALIZATION)
+ {
+ is_spec = true;
+ key = reinterpret_cast <spec_entry *> (d->deps[0])->tmpl;
+ }
+ else if (kind == depset::EK_PARTIAL)
+ {
+ is_spec = true;
+ key = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (d->get_entity ()));
+ }
+ 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))
+ {
+ binding_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 = ¯o->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 = ¯o->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_p = 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 = ¯o->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 = ¯o->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_p
+ && !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, ¯os);
+
+ 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_p)
+ {
+ 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++)
+ {
+ binding_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);
+ if (partial_specializations)
+ {
+ table.add_partial_entities (partial_specializations);
+ partial_specializations = NULL;
+ }
+ 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, reader, 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, binding_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
+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);
+ }
+ }
+ else if (DECL_IMPLICIT_TYPEDEF_P (decl)
+ && CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl)))
+ /* This is a partial or explicit specialization. */
+ vec_safe_push (partial_specializations, 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 (!modules_p ())
+ 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_MODULE_ATTACHMENTS_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, binding_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, binding_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);
+}
+
+/* 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, Cody::Flags::None, len);
+ int xlate = false;
+ if (packet.GetCode () == Cody::Client::PC_BOOL)
+ xlate = -int (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 = +1;
+ }
+ 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_yes && xlate > 1)
+ note = true;
+ else if (note_include_translate_no && xlate == 0)
+ 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 > 0))
+ 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 not-yet-loaded direct imports, and fill in
+ their file names. We'll have already loaded up the direct header
+ unit wavefront. */
+
+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 and informing of an export (if that's what we
+ are). Notice these are emitted even when preprocessing as they
+ inform the server of dependency edges. */
+ timevar_start (TV_MODULE_MAPPER);
+
+ dump.push (NULL);
+ dump () && dump ("Resolving direct import names");
+
+ if (!flag_preprocess_only
+ || bool (mapper->get_flags () & Cody::Flags::NameOnly)
+ || cpp_get_deps (reader))
+ {
+ 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)
+ {
+ Cody::Flags flags
+ = (flag_preprocess_only ? Cody::Flags::None
+ : Cody::Flags::NameOnly);
+
+ if (module->module_p
+ && (module->is_partition () || module->exported_p))
+ mapper->ModuleExport (module->get_flatname (), flags);
+ else
+ mapper->ModuleImport (module->get_flatname (), flags);
+ }
+ }
+
+ 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 ())
+ {
+ if (module->is_module ()
+ && (module->is_interface () || module->is_partition ()))
+ deps_add_module_target (deps, module->get_flatname (),
+ maybe_add_cmi_prefix (module->filename),
+ module->is_header());
+ else
+ deps_add_module_dep (deps, module->get_flatname ());
+ }
+ }
+ }
+
+ 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 ())
+ {
+ auto loc = input_location;
+ /* So crashes finger point the module decl. */
+ input_location = state->loc;
+ state->write (&to, reader);
+ input_location = loc;
+ }
+ 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)
+{
+ auto hdr = CMS_header;
+
+ switch (opt_code (code))
+ {
+ case OPT_fmodule_mapper_:
+ module_mapper_name = str;
+ return true;
+
+ case OPT_fmodule_header_:
+ {
+ if (!strcmp (str, "user"))
+ hdr = CMS_user;
+ else if (!strcmp (str, "system"))
+ hdr = CMS_system;
+ else
+ error ("unknown header kind %qs", str);
+ }
+ /* Fallthrough. */
+
+ case OPT_fmodule_header:
+ flag_header_unit = hdr;
+ 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)
+{
+ gcc_checking_assert (!lang_hooks.preprocess_undef);
+ if (modules_p ())
+ {
+ auto *cb = cpp_get_callbacks (reader);
+
+ cb->translate_include = maybe_translate_include;
+ cb->user_deferred_macro = module_state::deferred_macro;
+ if (flag_header_unit)
+ {
+ /* If the preprocessor hook is already in use, that
+ implementation will call the undef langhook. */
+ if (cb->undef)
+ lang_hooks.preprocess_undef = module_state::undef_macro;
+ else
+ cb->undef = module_state::undef_macro;
+ }
+ auto *opt = cpp_get_options (reader);
+ opt->module_directives = true;
+ opt->main_search = cpp_main_search (flag_header_unit);
+ }
+}
+
+#include "gt-cp-module.h"