blob: f27f4d091e5e3a95a6ba55a18e67b7003861b39d [file] [log] [blame]
/* C++ modules. Experimental!
Copyright (C) 2017-2022 Free Software Foundation, Inc.
Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* Comments in this file have a non-negligible chance of being wrong
or at least inaccurate. Due to (a) my misunderstanding, (b)
ambiguities that I have interpretted differently to original intent
(c) changes in the specification, (d) my poor wording, (e) source
changes. */
/* (Incomplete) Design Notes
A hash table contains all module names. Imported modules are
present in a modules array, which by construction places an
import's dependencies before the import itself. The single
exception is the current TU, which always occupies slot zero (even
when it is not a module).
Imported decls occupy an entity_ary, an array of 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_MODULE_EXPORT_P, DECL_MODULE_PURVIEW_P, DECL_MODULE_ATTACH_P
and DECL_MODULE_IMPORT_P. The first indicates whether it is
exported, the second whether it is in module or header-unit
purview. The third indicates it is attached to the named module in
whose purview it resides and the fourth indicates whether it was an
import into this TU or not. DECL_MODULE_ATTACH_P will be false for
all decls in a header-unit, and for those in a named module inside
a linkage declaration.
The more detailed flags are DECL_MODULE_PARTITION_P,
DECL_MODULE_ENTITY_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.
Header units are module-like.
For namespace-scope lookup, the decls for a particular module are
held located in a sparse array hanging off the binding of the name.
This is partitioned into two: a few fixed slots at the start
followed by the sparse slots afterwards. By construction we only
need to append new slots to the end -- there is never a need to
insert in the middle. The fixed slots are MODULE_SLOT_CURRENT for
the current TU (regardless of whether it is a module or not),
MODULE_SLOT_GLOBAL and MODULE_SLOT_PARTITION. These latter two
slots are used for merging entities across the global module and
module partitions respectively. MODULE_SLOT_PARTITION is only
present in a module. Neither of those two slots is searched during
name lookup -- they are internal use only. This vector is created
lazily once we require it, if there is only a declaration from the
current TU, a regular binding is present. It is converted on
demand.
OPTIMIZATION: Outside of the current TU, we only need ADL to work.
We could optimize regular lookup for the current TU by glomming all
the visible decls on its slot. Perhaps wait until design is a
little more settled though.
There is only one instance of each extern-linkage namespace. It
appears in every module slot that makes it visible. It also
appears in MODULE_SLOT_GLOBAL. (It is an ODR violation if they
collide with some other global module entity.) We also have an
optimization that shares the slot for adjacent modules that declare
the same such namespace.
A module interface compilation produces a Compiled Module Interface
(CMI). The format used is Encapsulated Lazy Records Of Numbered
Declarations, which is essentially ELF's section encapsulation. (As
all good nerds are aware, Elrond is half Elf.) Some sections are
named, and contain information about the module as a whole (indices
etc), and other sections are referenced by number. Although I
don't defend against actively hostile CMIs, there is some
checksumming involved to verify data integrity. When dumping out
an interface, we generate a graph of all the
independently-redeclarable DECLS that are needed, and the decls
they reference. From that we determine the strongly connected
components (SCC) within this TU. Each SCC is dumped to a separate
numbered section of the CMI. We generate a binding table section,
mapping each namespace&name to a defining section. This allows
lazy loading.
Lazy loading employs mmap to map a read-only image of the CMI.
It thus only occupies address space and is paged in on demand,
backed by the CMI file itself. If mmap is unavailable, regular
FILEIO is used. Also, there's a bespoke ELF reader/writer here,
which implements just the section table and sections (including
string sections) of a 32-bit ELF in host byte-order. You can of
course inspect it with readelf. I figured 32-bit is sufficient,
for a single module. I detect running out of section numbers, but
do not implement the ELF overflow mechanism. At least you'll get
an error if that happens.
We do not separate declarations and definitions. My guess is that
if you refer to the declaration, you'll also need the definition
(template body, inline function, class definition etc). But this
does mean we can get larger SCCs than if we separated them. It is
unclear whether this is a win or not.
Notice that we embed section indices into the contents of other
sections. Thus random manipulation of the CMI file by ELF tools
may well break it. The kosher way would probably be to introduce
indirection via section symbols, but that would require defining a
relocation type.
Notice that lazy loading of one module's decls can cause lazy
loading of other decls in the same or another module. Clearly we
want to avoid loops. In a correct program there can be no loops in
the module dependency graph, and the above-mentioned SCC algorithm
places all intra-module circular dependencies in the same SCC. It
also orders the SCCs wrt each other, so dependent SCCs come first.
As we load dependent modules first, we know there can be no
reference to a higher-numbered module, and because we write out
dependent SCCs first, likewise for SCCs within the module. This
allows us to immediately detect broken references. When loading,
we must ensure the rest of the compiler doesn't cause some
unconnected load to occur (for instance, instantiate a template).
Classes used:
dumper - logger
data - buffer
bytes - data streamer
bytes_in : bytes - scalar reader
bytes_out : bytes - scalar writer
elf - ELROND format
elf_in : elf - ELROND reader
elf_out : elf - ELROND writer
trees_in : bytes_in - tree reader
trees_out : bytes_out - tree writer
depset - dependency set
depset::hash - hash table of depsets
depset::tarjan - SCC determinator
uidset<T> - set T's related to a UID
uidset<T>::hash hash table of uidset<T>
loc_spans - location map data
module_state - module object
slurping - data needed during loading
macro_import - imported macro data
macro_export - exported macro data
The ELROND objects use mmap, for both reading and writing. If mmap
is unavailable, fileno IO is used to read and write blocks of data.
The mapper object uses fileno IO to communicate with the server or
program. */
/* In expermental (trunk) sources, MODULE_VERSION is a #define passed
in from the Makefile. It records the modification date of the
source directory -- that's the only way to stay sane. In release
sources, we (plan to) use the compiler's major.minor versioning.
While the format might not change between at minor versions, it
seems simplest to tie the two together. There's no concept of
inter-version compatibility. */
#define IS_EXPERIMENTAL(V) ((V) >= (1U << 20))
#define MODULE_MAJOR(V) ((V) / 10000)
#define MODULE_MINOR(V) ((V) % 10000)
#define EXPERIMENT(A,B) (IS_EXPERIMENTAL (MODULE_VERSION) ? (A) : (B))
#ifndef MODULE_VERSION
#include "bversion.h"
#define MODULE_VERSION (BUILDING_GCC_MAJOR * 10000U + BUILDING_GCC_MINOR)
#elif !IS_EXPERIMENTAL (MODULE_VERSION)
#error "This is not the version I was looking for."
#endif
#define _DEFAULT_SOURCE 1 /* To get TZ field of struct tm, if available. */
#include "config.h"
#define INCLUDE_MEMORY
#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
#include "timevar.h"
#include "stringpool.h"
#include "dumpfile.h"
#include "bitmap.h"
#include "cgraph.h"
#include "tree-iterator.h"
#include "cpplib.h"
#include "mkdeps.h"
#include "incpath.h"
#include "libiberty.h"
#include "stor-layout.h"
#include "version.h"
#include "tree-diagnostic.h"
#include "toplev.h"
#include "opts.h"
#include "attribs.h"
#include "intl.h"
#include "langhooks.h"
/* This TU doesn't need or want to see the networking. */
#define CODY_NETWORKING 0
#include "mapper-client.h"
#if 0 // 1 for testing no mmap
#define MAPPED_READING 0
#define MAPPED_WRITING 0
#else
#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
#endif
/* Some open(2) flag differences, what a colourful world it is! */
#if defined (O_CLOEXEC)
// OK
#elif defined (_O_NOINHERIT)
/* Windows' _O_NOINHERIT matches O_CLOEXEC flag */
#define O_CLOEXEC _O_NOINHERIT
#else
#define O_CLOEXEC 0
#endif
#if defined (O_BINARY)
// Ok?
#elif defined (_O_BINARY)
/* Windows' open(2) call defaults to text! */
#define O_BINARY _O_BINARY
#else
#define O_BINARY 0
#endif
static inline cpp_hashnode *cpp_node (tree id)
{
return CPP_HASHNODE (GCC_IDENT_TO_HT_IDENT (id));
}
static inline tree identifier (const cpp_hashnode *node)
{
/* HT_NODE() expands to node->ident that HT_IDENT_TO_GCC_IDENT()
then subtracts a nonzero constant, deriving a pointer to
a different member than ident. That's strictly undefined
and detected by -Warray-bounds. Suppress it. See PR 101372. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
return HT_IDENT_TO_GCC_IDENT (HT_NODE (const_cast<cpp_hashnode *> (node)));
#pragma GCC diagnostic pop
}
/* Id for dumping module information. */
int module_dump_id;
/* We have a special module owner. */
#define MODULE_UNKNOWN (~0U) /* Not yet known. */
/* Prefix for section names. */
#define MOD_SNAME_PFX ".gnu.c++"
/* Format a version for user consumption. */
typedef char verstr_t[32];
static void
version2string (unsigned version, verstr_t &out)
{
unsigned major = MODULE_MAJOR (version);
unsigned minor = MODULE_MINOR (version);
if (IS_EXPERIMENTAL (version))
sprintf (out, "%04u/%02u/%02u-%02u:%02u%s",
2000 + major / 10000, (major / 100) % 100, (major % 100),
minor / 100, minor % 100,
EXPERIMENT ("", " (experimental)"));
else
sprintf (out, "%u.%u", major, minor);
}
/* Include files to note translation for. */
static vec<const char *, va_heap, vl_embed> *note_includes;
/* Modules to note CMI pathames. */
static vec<const char *, va_heap, vl_embed> *note_cmis;
/* Traits to hash an arbitrary pointer. Entries are not deletable,
and removal is a noop (removal needed upon destruction). */
template <typename T>
struct nodel_ptr_hash : pointer_hash<T>, typed_noop_remove <T *> {
/* Nothing is deletable. Everything is insertable. */
static bool is_deleted (T *) { return false; }
static void mark_deleted (T *) { gcc_unreachable (); }
};
/* Map from pointer to signed integer. */
typedef simple_hashmap_traits<nodel_ptr_hash<void>, int> ptr_int_traits;
typedef hash_map<void *,signed,ptr_int_traits> ptr_int_hash_map;
/********************************************************************/
/* Basic streaming & ELF. Serialization is usually via mmap. For
writing we slide a buffer over the output file, syncing it
approproiately. For reading we simply map the whole file (as a
file-backed read-only map -- it's just address space, leaving the
OS pager to deal with getting the data to us). Some buffers need
to be more conventional malloc'd contents. */
/* Variable length buffer. */
class data {
public:
class allocator {
public:
/* Tools tend to moan if the dtor's not virtual. */
virtual ~allocator () {}
public:
void grow (data &obj, unsigned needed, bool exact);
void shrink (data &obj);
public:
virtual char *grow (char *ptr, unsigned needed);
virtual void shrink (char *ptr);
};
public:
char *buffer; /* Buffer being transferred. */
/* Although size_t would be the usual size, we know we never get
more than 4GB of buffer -- because that's the limit of the
encapsulation format. And if you need bigger imports, you're
doing it wrong. */
unsigned size; /* Allocated size of buffer. */
unsigned pos; /* Position in buffer. */
public:
data ()
:buffer (NULL), size (0), pos (0)
{
}
~data ()
{
/* Make sure the derived and/or using class know what they're
doing. */
gcc_checking_assert (!buffer);
}
protected:
char *use (unsigned count)
{
if (size < pos + count)
return NULL;
char *res = &buffer[pos];
pos += count;
return res;
}
public:
void unuse (unsigned count)
{
pos -= count;
}
public:
static allocator simple_memory;
};
/* The simple data allocator. */
data::allocator data::simple_memory;
/* Grow buffer to at least size NEEDED. */
void
data::allocator::grow (data &obj, unsigned needed, bool exact)
{
gcc_checking_assert (needed ? needed > obj.size : !obj.size);
if (!needed)
/* Pick a default size. */
needed = EXPERIMENT (100, 1000);
if (!exact)
needed *= 2;
obj.buffer = grow (obj.buffer, needed);
if (obj.buffer)
obj.size = needed;
else
obj.pos = obj.size = 0;
}
/* Free a buffer. */
void
data::allocator::shrink (data &obj)
{
shrink (obj.buffer);
obj.buffer = NULL;
obj.size = 0;
}
char *
data::allocator::grow (char *ptr, unsigned needed)
{
return XRESIZEVAR (char, ptr, needed);
}
void
data::allocator::shrink (char *ptr)
{
XDELETEVEC (ptr);
}
/* Byte streamer base. Buffer with read/write position and smarts
for single bits. */
class bytes : public data {
public:
typedef data parent;
protected:
uint32_t bit_val; /* Bit buffer. */
unsigned bit_pos; /* Next bit in bit buffer. */
public:
bytes ()
:parent (), bit_val (0), bit_pos (0)
{}
~bytes ()
{
}
protected:
unsigned calc_crc (unsigned) const;
protected:
/* Finish bit packet. Rewind the bytes not used. */
unsigned bit_flush ()
{
gcc_assert (bit_pos);
unsigned bytes = (bit_pos + 7) / 8;
unuse (4 - bytes);
bit_pos = 0;
bit_val = 0;
return bytes;
}
};
/* Calculate the crc32 of the buffer. Note the CRC is stored in the
first 4 bytes, so don't include them. */
unsigned
bytes::calc_crc (unsigned l) const
{
unsigned crc = 0;
for (size_t ix = 4; ix < l; ix++)
crc = crc32_byte (crc, buffer[ix]);
return crc;
}
class elf_in;
/* Byte stream reader. */
class bytes_in : public bytes {
typedef bytes parent;
protected:
bool overrun; /* Sticky read-too-much flag. */
public:
bytes_in ()
: parent (), overrun (false)
{
}
~bytes_in ()
{
}
public:
/* Begin reading a named section. */
bool begin (location_t loc, elf_in *src, const char *name);
/* Begin reading a numbered section with optional name. */
bool begin (location_t loc, elf_in *src, unsigned, const char * = NULL);
/* Complete reading a buffer. Propagate errors and return true on
success. */
bool end (elf_in *src);
/* Return true if there is unread data. */
bool more_p () const
{
return pos != size;
}
public:
/* Start reading at OFFSET. */
void random_access (unsigned offset)
{
if (offset > size)
set_overrun ();
pos = offset;
bit_pos = bit_val = 0;
}
public:
void align (unsigned boundary)
{
if (unsigned pad = pos & (boundary - 1))
read (boundary - pad);
}
public:
const char *read (unsigned count)
{
char *ptr = use (count);
if (!ptr)
set_overrun ();
return ptr;
}
public:
bool check_crc () const;
/* We store the CRC in the first 4 bytes, using host endianness. */
unsigned get_crc () const
{
return *(const unsigned *)&buffer[0];
}
public:
/* Manipulate the overrun flag. */
bool get_overrun () const
{
return overrun;
}
void set_overrun ()
{
overrun = true;
}
public:
unsigned u32 (); /* Read uncompressed integer. */
public:
bool b (); /* Read a bool. */
void bflush (); /* Completed a block of bools. */
private:
void bfill (); /* Get the next block of bools. */
public:
int c (); /* Read a char. */
int i (); /* Read a signed int. */
unsigned u (); /* Read an unsigned int. */
size_t z (); /* Read a size_t. */
HOST_WIDE_INT wi (); /* Read a HOST_WIDE_INT. */
unsigned HOST_WIDE_INT wu (); /* Read an unsigned HOST_WIDE_INT. */
const char *str (size_t * = NULL); /* Read a string. */
const void *buf (size_t); /* Read a fixed-length buffer. */
cpp_hashnode *cpp_node (); /* Read a cpp node. */
};
/* Verify the buffer's CRC is correct. */
bool
bytes_in::check_crc () const
{
if (size < 4)
return false;
unsigned c_crc = calc_crc (size);
if (c_crc != get_crc ())
return false;
return true;
}
class elf_out;
/* Byte stream writer. */
class bytes_out : public bytes {
typedef bytes parent;
public:
allocator *memory; /* Obtainer of memory. */
public:
bytes_out (allocator *memory)
: parent (), memory (memory)
{
}
~bytes_out ()
{
}
public:
bool streaming_p () const
{
return memory != NULL;
}
public:
void set_crc (unsigned *crc_ptr);
public:
/* Begin writing, maybe reserve space for CRC. */
void begin (bool need_crc = true);
/* Finish writing. Spill to section by number. */
unsigned end (elf_out *, unsigned, unsigned *crc_ptr = NULL);
public:
void align (unsigned boundary)
{
if (unsigned pad = pos & (boundary - 1))
write (boundary - pad);
}
public:
char *write (unsigned count, bool exact = false)
{
if (size < pos + count)
memory->grow (*this, pos + count, exact);
return use (count);
}
public:
void u32 (unsigned); /* Write uncompressed integer. */
public:
void b (bool); /* Write bool. */
void bflush (); /* Finish block of bools. */
public:
void c (unsigned char); /* Write unsigned char. */
void i (int); /* Write signed int. */
void u (unsigned); /* Write unsigned int. */
void z (size_t s); /* Write size_t. */
void wi (HOST_WIDE_INT); /* Write HOST_WIDE_INT. */
void wu (unsigned HOST_WIDE_INT); /* Write unsigned HOST_WIDE_INT. */
void str (const char *ptr)
{
str (ptr, strlen (ptr));
}
void cpp_node (const cpp_hashnode *node)
{
str ((const char *)NODE_NAME (node), NODE_LEN (node));
}
void str (const char *, size_t); /* Write string of known length. */
void buf (const void *, size_t); /* Write fixed length buffer. */
void *buf (size_t); /* Create a writable buffer */
public:
/* Format a NUL-terminated raw string. */
void printf (const char *, ...) ATTRIBUTE_PRINTF_2;
void print_time (const char *, const tm *, const char *);
public:
/* Dump instrumentation. */
static void instrument ();
protected:
/* Instrumentation. */
static unsigned spans[4];
static unsigned lengths[4];
static int is_set;
};
/* Instrumentation. */
unsigned bytes_out::spans[4];
unsigned bytes_out::lengths[4];
int bytes_out::is_set = -1;
/* If CRC_PTR non-null, set the CRC of the buffer. Mix the CRC into
that pointed to by CRC_PTR. */
void
bytes_out::set_crc (unsigned *crc_ptr)
{
if (crc_ptr)
{
gcc_checking_assert (pos >= 4);
unsigned crc = calc_crc (pos);
unsigned accum = *crc_ptr;
/* Only mix the existing *CRC_PTR if it is non-zero. */
accum = accum ? crc32_unsigned (accum, crc) : crc;
*crc_ptr = accum;
/* Buffer will be sufficiently aligned. */
*(unsigned *)buffer = crc;
}
}
/* Finish a set of bools. */
void
bytes_out::bflush ()
{
if (bit_pos)
{
u32 (bit_val);
lengths[2] += bit_flush ();
}
spans[2]++;
is_set = -1;
}
void
bytes_in::bflush ()
{
if (bit_pos)
bit_flush ();
}
/* When reading, we don't know how many bools we'll read in. So read
4 bytes-worth, and then rewind when flushing if we didn't need them
all. You can't have a block of bools closer than 4 bytes to the
end of the buffer. */
void
bytes_in::bfill ()
{
bit_val = u32 ();
}
/* Bools are packed into bytes. You cannot mix bools and non-bools.
You must call bflush before emitting another type. So batch your
bools.
It may be worth optimizing for most bools being zero. Some kind of
run-length encoding? */
void
bytes_out::b (bool x)
{
if (is_set != x)
{
is_set = x;
spans[x]++;
}
lengths[x]++;
bit_val |= unsigned (x) << bit_pos++;
if (bit_pos == 32)
{
u32 (bit_val);
lengths[2] += bit_flush ();
}
}
bool
bytes_in::b ()
{
if (!bit_pos)
bfill ();
bool v = (bit_val >> bit_pos++) & 1;
if (bit_pos == 32)
bit_flush ();
return v;
}
/* Exactly 4 bytes. Used internally for bool packing and a few other
places. We can't simply use uint32_t because (a) alignment and
(b) we need little-endian for the bool streaming rewinding to make
sense. */
void
bytes_out::u32 (unsigned val)
{
if (char *ptr = write (4))
{
ptr[0] = val;
ptr[1] = val >> 8;
ptr[2] = val >> 16;
ptr[3] = val >> 24;
}
}
unsigned
bytes_in::u32 ()
{
unsigned val = 0;
if (const char *ptr = read (4))
{
val |= (unsigned char)ptr[0];
val |= (unsigned char)ptr[1] << 8;
val |= (unsigned char)ptr[2] << 16;
val |= (unsigned char)ptr[3] << 24;
}
return val;
}
/* Chars are unsigned and written as single bytes. */
void
bytes_out::c (unsigned char v)
{
if (char *ptr = write (1))
*ptr = v;
}
int
bytes_in::c ()
{
int v = 0;
if (const char *ptr = read (1))
v = (unsigned char)ptr[0];
return v;
}
/* Ints 7-bit as a byte. Otherwise a 3bit count of following bytes in
big-endian form. 4 bits are in the first byte. */
void
bytes_out::i (int v)
{
if (char *ptr = write (1))
{
if (v <= 0x3f && v >= -0x40)
*ptr = v & 0x7f;
else
{
unsigned bytes = 0;
int probe;
if (v >= 0)
for (probe = v >> 8; probe > 0x7; probe >>= 8)
bytes++;
else
for (probe = v >> 8; probe < -0x8; probe >>= 8)
bytes++;
*ptr = 0x80 | bytes << 4 | (probe & 0xf);
if ((ptr = write (++bytes)))
for (; bytes--; v >>= 8)
ptr[bytes] = v & 0xff;
}
}
}
int
bytes_in::i ()
{
int v = 0;
if (const char *ptr = read (1))
{
v = *ptr & 0xff;
if (v & 0x80)
{
unsigned bytes = (v >> 4) & 0x7;
v &= 0xf;
if (v & 0x8)
v |= -1 ^ 0x7;
/* unsigned necessary due to left shifts of -ve values. */
unsigned uv = unsigned (v);
if ((ptr = read (++bytes)))
while (bytes--)
uv = (uv << 8) | (*ptr++ & 0xff);
v = int (uv);
}
else if (v & 0x40)
v |= -1 ^ 0x3f;
}
return v;
}
void
bytes_out::u (unsigned v)
{
if (char *ptr = write (1))
{
if (v <= 0x7f)
*ptr = v;
else
{
unsigned bytes = 0;
unsigned probe;
for (probe = v >> 8; probe > 0xf; probe >>= 8)
bytes++;
*ptr = 0x80 | bytes << 4 | probe;
if ((ptr = write (++bytes)))
for (; bytes--; v >>= 8)
ptr[bytes] = v & 0xff;
}
}
}
unsigned
bytes_in::u ()
{
unsigned v = 0;
if (const char *ptr = read (1))
{
v = *ptr & 0xff;
if (v & 0x80)
{
unsigned bytes = (v >> 4) & 0x7;
v &= 0xf;
if ((ptr = read (++bytes)))
while (bytes--)
v = (v << 8) | (*ptr++ & 0xff);
}
}
return v;
}
void
bytes_out::wi (HOST_WIDE_INT v)
{
if (char *ptr = write (1))
{
if (v <= 0x3f && v >= -0x40)
*ptr = v & 0x7f;
else
{
unsigned bytes = 0;
HOST_WIDE_INT probe;
if (v >= 0)
for (probe = v >> 8; probe > 0x7; probe >>= 8)
bytes++;
else
for (probe = v >> 8; probe < -0x8; probe >>= 8)
bytes++;
*ptr = 0x80 | bytes << 4 | (probe & 0xf);
if ((ptr = write (++bytes)))
for (; bytes--; v >>= 8)
ptr[bytes] = v & 0xff;
}
}
}
HOST_WIDE_INT
bytes_in::wi ()
{
HOST_WIDE_INT v = 0;
if (const char *ptr = read (1))
{
v = *ptr & 0xff;
if (v & 0x80)
{
unsigned bytes = (v >> 4) & 0x7;
v &= 0xf;
if (v & 0x8)
v |= -1 ^ 0x7;
/* unsigned necessary due to left shifts of -ve values. */
unsigned HOST_WIDE_INT uv = (unsigned HOST_WIDE_INT) v;
if ((ptr = read (++bytes)))
while (bytes--)
uv = (uv << 8) | (*ptr++ & 0xff);
v = (HOST_WIDE_INT) uv;
}
else if (v & 0x40)
v |= -1 ^ 0x3f;
}
return v;
}
/* unsigned wide ints are just written as signed wide ints. */
inline void
bytes_out::wu (unsigned HOST_WIDE_INT v)
{
wi ((HOST_WIDE_INT) v);
}
inline unsigned HOST_WIDE_INT
bytes_in::wu ()
{
return (unsigned HOST_WIDE_INT) wi ();
}
/* size_t written as unsigned or unsigned wide int. */
inline void
bytes_out::z (size_t s)
{
if (sizeof (s) == sizeof (unsigned))
u (s);
else
wu (s);
}
inline size_t
bytes_in::z ()
{
if (sizeof (size_t) == sizeof (unsigned))
return u ();
else
return wu ();
}
/* Buffer simply memcpied. */
void *
bytes_out::buf (size_t len)
{
align (sizeof (void *) * 2);
return write (len);
}
void
bytes_out::buf (const void *src, size_t len)
{
if (void *ptr = buf (len))
memcpy (ptr, src, len);
}
const void *
bytes_in::buf (size_t len)
{
align (sizeof (void *) * 2);
const char *ptr = read (len);
return ptr;
}
/* strings as an size_t length, followed by the buffer. Make sure
there's a NUL terminator on read. */
void
bytes_out::str (const char *string, size_t len)
{
z (len);
if (len)
{
gcc_checking_assert (!string[len]);
buf (string, len + 1);
}
}
const char *
bytes_in::str (size_t *len_p)
{
size_t len = z ();
/* We're about to trust some user data. */
if (overrun)
len = 0;
if (len_p)
*len_p = len;
const char *str = NULL;
if (len)
{
str = reinterpret_cast<const char *> (buf (len + 1));
if (!str || str[len])
{
set_overrun ();
str = NULL;
}
}
return str ? str : "";
}
cpp_hashnode *
bytes_in::cpp_node ()
{
size_t len;
const char *s = str (&len);
if (!len)
return NULL;
return ::cpp_node (get_identifier_with_length (s, len));
}
/* Format a string directly to the buffer, including a terminating
NUL. Intended for human consumption. */
void
bytes_out::printf (const char *format, ...)
{
va_list args;
/* Exercise buffer expansion. */
size_t len = EXPERIMENT (10, 500);
while (char *ptr = write (len))
{
va_start (args, format);
size_t actual = vsnprintf (ptr, len, format, args) + 1;
va_end (args);
if (actual <= len)
{
unuse (len - actual);
break;
}
unuse (len);
len = actual;
}
}
void
bytes_out::print_time (const char *kind, const tm *time, const char *tz)
{
printf ("%stime: %4u/%02u/%02u %02u:%02u:%02u %s",
kind, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec, tz);
}
/* Encapsulated Lazy Records Of Named Declarations.
Header: Stunningly Elf32_Ehdr-like
Sections: Sectional data
[1-N) : User data sections
N .strtab : strings, stunningly ELF STRTAB-like
Index: Section table, stunningly ELF32_Shdr-like. */
class elf {
protected:
/* Constants used within the format. */
enum private_constants {
/* File kind. */
ET_NONE = 0,
EM_NONE = 0,
OSABI_NONE = 0,
/* File format. */
EV_CURRENT = 1,
CLASS32 = 1,
DATA2LSB = 1,
DATA2MSB = 2,
/* Section numbering. */
SHN_UNDEF = 0,
SHN_LORESERVE = 0xff00,
SHN_XINDEX = 0xffff,
/* Section types. */
SHT_NONE = 0, /* No contents. */
SHT_PROGBITS = 1, /* Random bytes. */
SHT_STRTAB = 3, /* A string table. */
/* Section flags. */
SHF_NONE = 0x00, /* Nothing. */
SHF_STRINGS = 0x20, /* NUL-Terminated strings. */
/* I really hope we do not get CMI files larger than 4GB. */
MY_CLASS = CLASS32,
/* It is host endianness that is relevant. */
MY_ENDIAN = DATA2LSB
#ifdef WORDS_BIGENDIAN
^ DATA2LSB ^ DATA2MSB
#endif
};
public:
/* Constants visible to users. */
enum public_constants {
/* Special error codes. Breaking layering a bit. */
E_BAD_DATA = -1, /* Random unexpected data errors. */
E_BAD_LAZY = -2, /* Badly ordered laziness. */
E_BAD_IMPORT = -3 /* A nested import failed. */
};
protected:
/* File identification. On-disk representation. */
struct ident {
uint8_t magic[4]; /* 0x7f, 'E', 'L', 'F' */
uint8_t klass; /* 4:CLASS32 */
uint8_t data; /* 5:DATA2[LM]SB */
uint8_t version; /* 6:EV_CURRENT */
uint8_t osabi; /* 7:OSABI_NONE */
uint8_t abiver; /* 8: 0 */
uint8_t pad[7]; /* 9-15 */
};
/* File header. On-disk representation. */
struct header {
struct ident ident;
uint16_t type; /* ET_NONE */
uint16_t machine; /* EM_NONE */
uint32_t version; /* EV_CURRENT */
uint32_t entry; /* 0 */
uint32_t phoff; /* 0 */
uint32_t shoff; /* Section Header Offset in file */
uint32_t flags;
uint16_t ehsize; /* ELROND Header SIZE -- sizeof (header) */
uint16_t phentsize; /* 0 */
uint16_t phnum; /* 0 */
uint16_t shentsize; /* Section Header SIZE -- sizeof (section) */
uint16_t shnum; /* Section Header NUM */
uint16_t shstrndx; /* Section Header STRing iNDeX */
};
/* File section. On-disk representation. */
struct section {
uint32_t name; /* String table offset. */
uint32_t type; /* SHT_* */
uint32_t flags; /* SHF_* */
uint32_t addr; /* 0 */
uint32_t offset; /* OFFSET in file */
uint32_t size; /* SIZE of section */
uint32_t link; /* 0 */
uint32_t info; /* 0 */
uint32_t addralign; /* 0 */
uint32_t entsize; /* ENTry SIZE, usually 0 */
};
protected:
data hdr; /* The header. */
data sectab; /* The section table. */
data strtab; /* String table. */
int fd; /* File descriptor we're reading or writing. */
int err; /* Sticky error code. */
public:
/* Construct from STREAM. E is errno if STREAM NULL. */
elf (int fd, int e)
:hdr (), sectab (), strtab (), fd (fd), err (fd >= 0 ? 0 : e)
{}
~elf ()
{
gcc_checking_assert (fd < 0 && !hdr.buffer
&& !sectab.buffer && !strtab.buffer);
}
public:
/* Return the error, if we have an error. */
int get_error () const
{
return err;
}
/* Set the error, unless it's already been set. */
void set_error (int e = E_BAD_DATA)
{
if (!err)
err = e;
}
/* Get an error string. */
const char *get_error (const char *) const;
public:
/* Begin reading/writing file. Return false on error. */
bool begin () const
{
return !get_error ();
}
/* Finish reading/writing file. Return false on error. */
bool end ();
};
/* Return error string. */
const char *
elf::get_error (const char *name) const
{
if (!name)
return "Unknown CMI mapping";
switch (err)
{
case 0:
gcc_unreachable ();
case E_BAD_DATA:
return "Bad file data";
case E_BAD_IMPORT:
return "Bad import dependency";
case E_BAD_LAZY:
return "Bad lazy ordering";
default:
return xstrerror (err);
}
}
/* Finish file, return true if there's an error. */
bool
elf::end ()
{
/* Close the stream and free the section table. */
if (fd >= 0 && close (fd))
set_error (errno);
fd = -1;
return !get_error ();
}
/* ELROND reader. */
class elf_in : public elf {
typedef elf parent;
private:
/* For freezing & defrosting. */
#if !defined (HOST_LACKS_INODE_NUMBERS)
dev_t device;
ino_t inode;
#endif
public:
elf_in (int fd, int e)
:parent (fd, e)
{
}
~elf_in ()
{
}
public:
bool is_frozen () const
{
return fd < 0 && hdr.pos;
}
bool is_freezable () const
{
return fd >= 0 && hdr.pos;
}
void freeze ();
bool defrost (const char *);
/* If BYTES is in the mmapped area, allocate a new buffer for it. */
void preserve (bytes_in &bytes ATTRIBUTE_UNUSED)
{
#if MAPPED_READING
if (hdr.buffer && bytes.buffer >= hdr.buffer
&& bytes.buffer < hdr.buffer + hdr.pos)
{
char *buf = bytes.buffer;
bytes.buffer = data::simple_memory.grow (NULL, bytes.size);
memcpy (bytes.buffer, buf, bytes.size);
}
#endif
}
/* If BYTES is not in SELF's mmapped area, free it. SELF might be
NULL. */
static void release (elf_in *self ATTRIBUTE_UNUSED, bytes_in &bytes)
{
#if MAPPED_READING
if (!(self && self->hdr.buffer && bytes.buffer >= self->hdr.buffer
&& bytes.buffer < self->hdr.buffer + self->hdr.pos))
#endif
data::simple_memory.shrink (bytes.buffer);
bytes.buffer = NULL;
bytes.size = 0;
}
public:
static void grow (data &data, unsigned needed)
{
gcc_checking_assert (!data.buffer);
#if !MAPPED_READING
data.buffer = XNEWVEC (char, needed);
#endif
data.size = needed;
}
static void shrink (data &data)
{
#if !MAPPED_READING
XDELETEVEC (data.buffer);
#endif
data.buffer = NULL;
data.size = 0;
}
public:
const section *get_section (unsigned s) const
{
if (s * sizeof (section) < sectab.size)
return reinterpret_cast<const section *>
(&sectab.buffer[s * sizeof (section)]);
else
return NULL;
}
unsigned get_section_limit () const
{
return sectab.size / sizeof (section);
}
protected:
const char *read (data *, unsigned, unsigned);
public:
/* Read section by number. */
bool read (data *d, const section *s)
{
return s && read (d, s->offset, s->size);
}
/* Find section by name. */
unsigned find (const char *name);
/* Find section by index. */
const section *find (unsigned snum, unsigned type = SHT_PROGBITS);
public:
/* Release the string table, when we're done with it. */
void release ()
{
shrink (strtab);
}
public:
bool begin (location_t);
bool end ()
{
release ();
#if MAPPED_READING
if (hdr.buffer)
munmap (hdr.buffer, hdr.pos);
hdr.buffer = NULL;
#endif
shrink (sectab);
return parent::end ();
}
public:
/* Return string name at OFFSET. Checks OFFSET range. Always
returns non-NULL. We know offset 0 is an empty string. */
const char *name (unsigned offset)
{
return &strtab.buffer[offset < strtab.size ? offset : 0];
}
};
/* ELROND writer. */
class elf_out : public elf, public data::allocator {
typedef elf parent;
/* Desired section alignment on disk. */
static const int SECTION_ALIGN = 16;
private:
ptr_int_hash_map identtab; /* Map of IDENTIFIERS to strtab offsets. */
unsigned pos; /* Write position in file. */
#if MAPPED_WRITING
unsigned offset; /* Offset of the mapping. */
unsigned extent; /* Length of mapping. */
unsigned page_size; /* System page size. */
#endif
public:
elf_out (int fd, int e)
:parent (fd, e), identtab (500), pos (0)
{
#if MAPPED_WRITING
offset = extent = 0;
page_size = sysconf (_SC_PAGE_SIZE);
if (page_size < SECTION_ALIGN)
/* Something really strange. */
set_error (EINVAL);
#endif
}
~elf_out ()
{
data::simple_memory.shrink (hdr);
data::simple_memory.shrink (sectab);
data::simple_memory.shrink (strtab);
}
#if MAPPED_WRITING
private:
void create_mapping (unsigned ext, bool extending = true);
void remove_mapping ();
#endif
protected:
using allocator::grow;
char *grow (char *, unsigned needed) final override;
#if MAPPED_WRITING
using allocator::shrink;
void shrink (char *) final override;
#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 | O_BINARY);
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) != ssize_t (length))
{
set_error (errno);
shrink (*data);
return NULL;
}
#endif
return data->buffer;
}
/* Read section SNUM of TYPE. Return section pointer or NULL on error. */
const elf::section *
elf_in::find (unsigned snum, unsigned type)
{
const section *sec = get_section (snum);
if (!snum || !sec || sec->type != type)
return NULL;
return sec;
}
/* Find a section NAME and TYPE. Return section number, or zero on
failure. */
unsigned
elf_in::find (const char *sname)
{
for (unsigned pos = sectab.size; pos -= sizeof (section); )
{
const section *sec
= reinterpret_cast<const section *> (&sectab.buffer[pos]);
if (0 == strcmp (sname, name (sec->name)))
return pos / sizeof (section);
}
return 0;
}
/* Begin reading file. Verify header. Pull in section and string
tables. Return true on success. */
bool
elf_in::begin (location_t loc)
{
if (!parent::begin ())
return false;
struct stat stat;
unsigned size = 0;
if (!fstat (fd, &stat))
{
#if !defined (HOST_LACKS_INODE_NUMBERS)
device = stat.st_dev;
inode = stat.st_ino;
#endif
/* Never generate files > 4GB, check we've not been given one. */
if (stat.st_size == unsigned (stat.st_size))
size = unsigned (stat.st_size);
}
#if MAPPED_READING
/* MAP_SHARED so that the file is backing store. If someone else
concurrently writes it, they're wrong. */
void *mapping = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (mapping == MAP_FAILED)
{
fail:
set_error (errno);
return false;
}
/* We'll be hopping over this randomly. Some systems declare the
first parm as char *, and other declare it as void *. */
if (madvise (reinterpret_cast <char *> (mapping), size, MADV_RANDOM))
goto fail;
hdr.buffer = (char *)mapping;
#else
read (&hdr, 0, sizeof (header));
#endif
hdr.pos = size; /* Record size of the file. */
const header *h = reinterpret_cast<const header *> (hdr.buffer);
if (!h)
return false;
if (h->ident.magic[0] != 0x7f
|| h->ident.magic[1] != 'E'
|| h->ident.magic[2] != 'L'
|| h->ident.magic[3] != 'F')
{
error_at (loc, "not Encapsulated Lazy Records of Named Declarations");
failed:
shrink (hdr);
return false;
}
/* We expect a particular format -- the ELF is not intended to be
distributable. */
if (h->ident.klass != MY_CLASS
|| h->ident.data != MY_ENDIAN
|| h->ident.version != EV_CURRENT
|| h->type != ET_NONE
|| h->machine != EM_NONE
|| h->ident.osabi != OSABI_NONE)
{
error_at (loc, "unexpected encapsulation format or type");
goto failed;
}
int e = -1;
if (!h->shoff || h->shentsize != sizeof (section))
{
malformed:
set_error (e);
error_at (loc, "encapsulation is malformed");
goto failed;
}
unsigned strndx = h->shstrndx;
unsigned shnum = h->shnum;
if (shnum == SHN_XINDEX)
{
if (!read (&sectab, h->shoff, sizeof (section)))
{
section_table_fail:
e = errno;
goto malformed;
}
shnum = get_section (0)->size;
/* Freeing does mean we'll re-read it in the case we're not
mapping, but this is going to be rare. */
shrink (sectab);
}
if (!shnum)
goto malformed;
if (!read (&sectab, h->shoff, shnum * sizeof (section)))
goto section_table_fail;
if (strndx == SHN_XINDEX)
strndx = get_section (0)->link;
if (!read (&strtab, find (strndx, SHT_STRTAB)))
goto malformed;
/* The string table should be at least one byte, with NUL chars
at either end. */
if (!(strtab.size && !strtab.buffer[0]
&& !strtab.buffer[strtab.size - 1]))
goto malformed;
#if MAPPED_READING
/* Record the offsets of the section and string tables. */
sectab.pos = h->shoff;
strtab.pos = shnum * sizeof (section);
#else
shrink (hdr);
#endif
return true;
}
/* Create a new mapping. */
#if MAPPED_WRITING
void
elf_out::create_mapping (unsigned ext, bool extending)
{
#ifndef HAVE_POSIX_FALLOCATE
#define posix_fallocate(fd,off,len) ftruncate (fd, off + len)
#endif
void *mapping = MAP_FAILED;
if (extending && ext < 1024 * 1024)
{
if (!posix_fallocate (fd, offset, ext * 2))
mapping = mmap (NULL, ext * 2, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, offset);
if (mapping != MAP_FAILED)
ext *= 2;
}
if (mapping == MAP_FAILED)
{
if (!extending || !posix_fallocate (fd, offset, ext))
mapping = mmap (NULL, ext, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, offset);
if (mapping == MAP_FAILED)
{
set_error (errno);
mapping = NULL;
ext = 0;
}
}
#undef posix_fallocate
hdr.buffer = (char *)mapping;
extent = ext;
}
#endif
/* Flush out the current mapping. */
#if MAPPED_WRITING
void
elf_out::remove_mapping ()
{
if (hdr.buffer)
{
/* MS_ASYNC dtrt with the removed mapping, including a
subsequent overlapping remap. */
if (msync (hdr.buffer, extent, MS_ASYNC)
|| munmap (hdr.buffer, extent))
/* We're somewhat screwed at this point. */
set_error (errno);
}
hdr.buffer = NULL;
}
#endif
/* Grow a mapping of PTR to be NEEDED bytes long. This gets
interesting if the new size grows the EXTENT. */
char *
elf_out::grow (char *data, unsigned needed)
{
if (!data)
{
/* First allocation, check we're aligned. */
gcc_checking_assert (!(pos & (SECTION_ALIGN - 1)));
#if MAPPED_WRITING
data = hdr.buffer + (pos - offset);
#endif
}
#if MAPPED_WRITING
unsigned off = data - hdr.buffer;
if (off + needed > extent)
{
/* We need to grow the mapping. */
unsigned lwm = off & ~(page_size - 1);
unsigned hwm = (off + needed + page_size - 1) & ~(page_size - 1);
gcc_checking_assert (hwm > extent);
remove_mapping ();
offset += lwm;
create_mapping (extent < hwm - lwm ? hwm - lwm : extent);
data = hdr.buffer + (off - lwm);
}
#else
data = allocator::grow (data, needed);
#endif
return data;
}
#if MAPPED_WRITING
/* Shrinking is a NOP. */
void
elf_out::shrink (char *)
{
}
#endif
/* Write S of length L to the strtab buffer. L must include the ending
NUL, if that's what you want. */
unsigned
elf_out::strtab_write (const char *s, unsigned l)
{
if (strtab.pos + l > strtab.size)
data::simple_memory.grow (strtab, strtab.pos + l, false);
memcpy (strtab.buffer + strtab.pos, s, l);
unsigned res = strtab.pos;
strtab.pos += l;
return res;
}
/* Write qualified name of decl. INNER >0 if this is a definition, <0
if this is a qualifier of an outer name. */
void
elf_out::strtab_write (tree decl, int inner)
{
tree ctx = CP_DECL_CONTEXT (decl);
if (TYPE_P (ctx))
ctx = TYPE_NAME (ctx);
if (ctx != global_namespace)
strtab_write (ctx, -1);
tree name = DECL_NAME (decl);
if (!name)
name = DECL_ASSEMBLER_NAME_RAW (decl);
strtab_write (IDENTIFIER_POINTER (name), IDENTIFIER_LENGTH (name));
if (inner)
strtab_write (&"::{}"[inner+1], 2);
}
/* Map IDENTIFIER IDENT to strtab offset. Inserts into strtab if not
already there. */
unsigned
elf_out::name (tree ident)
{
unsigned res = 0;
if (ident)
{
bool existed;
int *slot = &identtab.get_or_insert (ident, &existed);
if (!existed)
*slot = strtab_write (IDENTIFIER_POINTER (ident),
IDENTIFIER_LENGTH (ident) + 1);
res = *slot;
}
return res;
}
/* Map LITERAL to strtab offset. Does not detect duplicates and
expects LITERAL to remain live until strtab is written out. */
unsigned
elf_out::name (const char *literal)
{
return strtab_write (literal, strlen (literal) + 1);
}
/* Map a DECL's qualified name to strtab offset. Does not detect
duplicates. */
unsigned
elf_out::qualified_name (tree decl, bool is_defn)
{
gcc_checking_assert (DECL_P (decl) && decl != global_namespace);
unsigned result = strtab.pos;
strtab_write (decl, is_defn);
strtab_write ("", 1);
return result;
}
/* Add section to file. Return section number. TYPE & NAME identify
the section. OFF and SIZE identify the file location of its
data. FLAGS contains additional info. */
unsigned
elf_out::add (unsigned type, unsigned name, unsigned off, unsigned size,
unsigned flags)
{
gcc_checking_assert (!(off & (SECTION_ALIGN - 1)));
if (sectab.pos + sizeof (section) > sectab.size)
data::simple_memory.grow (sectab, sectab.pos + sizeof (section), false);
section *sec = reinterpret_cast<section *> (sectab.buffer + sectab.pos);
memset (sec, 0, sizeof (section));
sec->type = type;
sec->flags = flags;
sec->name = name;
sec->offset = off;
sec->size = size;
if (flags & SHF_STRINGS)
sec->entsize = 1;
unsigned res = sectab.pos;
sectab.pos += sizeof (section);
return res / sizeof (section);
}
/* Pad to the next alignment boundary, then write BUFFER to disk.
Return the position of the start of the write, or zero on failure. */
unsigned
elf_out::write (const data &buffer)
{
#if MAPPED_WRITING
/* HDR is always mapped. */
if (&buffer != &hdr)
{
bytes_out out (this);
grow (out, buffer.pos, true);
if (out.buffer)
memcpy (out.buffer, buffer.buffer, buffer.pos);
shrink (out);
}
else
/* We should have been aligned during the first allocation. */
gcc_checking_assert (!(pos & (SECTION_ALIGN - 1)));
#else
if (::write (fd, buffer.buffer, buffer.pos) != ssize_t (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) != ssize_t (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 its 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:
/* This class-member is defined here, but the class was imported. */
bool is_member () const
{
gcc_checking_assert (get_entity_kind () == EK_DECL);
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 (module_state *);
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 < ARRAY_SIZE (names));
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;
}
class pending_key
{
public:
tree ns;
tree id;
};
template<>
struct default_hash_traits<pending_key>
{
using value_type = pending_key;
static const bool empty_zero_p = false;
static hashval_t hash (const value_type &k)
{
hashval_t h = IDENTIFIER_HASH_VALUE (k.id);
h = iterative_hash_hashval_t (DECL_UID (k.ns), h);
return h;
}
static bool equal (const value_type &k, const value_type &l)
{
return k.ns == l.ns && k.id == l.id;
}
static void mark_empty (value_type &k)
{
k.ns = k.id = NULL_TREE;
}
static void mark_deleted (value_type &k)
{
k.ns = NULL_TREE;
gcc_checking_assert (k.id);
}
static bool is_empty (const value_type &k)
{
return k.ns == NULL_TREE && k.id == NULL_TREE;
}
static bool is_deleted (const value_type &k)
{
return k.ns == NULL_TREE && k.id != NULL_TREE;
}
static void remove (value_type &)
{
}
};
typedef hash_map<pending_key, auto_vec<unsigned>> pending_map_t;
/* Not-loaded entities that are keyed to a namespace-scope
identifier. See module_state::write_pendings for details. */
pending_map_t *pending_table;
/* Decls that need some post processing once a batch of lazy loads has
completed. */
vec<tree, va_heap, vl_embed> *post_load_decls;
/* Some entities are keyed to another entitity for ODR purposes.
For example, at namespace scope, 'inline auto var = []{};', that
lambda is keyed to 'var', and follows its ODRness. */
typedef hash_map<tree, auto_vec<tree>> keyed_map_t;
static keyed_map_t *keyed_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_keyed, /* Found by key & 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_decl_spec = MK_template_mask | MK_tmpl_decl_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", "alias tmpl spec", /* 22,23 alias (template). */
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)
{
}
};
/* Hashmap of merged duplicates. Usually decls, but can contain
BINFOs. */
typedef hash_map<tree,uintptr_t,
simple_hashmap_traits<nodel_ptr_hash<tree_node>,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, bool is_typedef);
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_attached);
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