mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-03 00:29:47 +00:00
20091 lines
514 KiB
C++
20091 lines
514 KiB
C++
/* 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_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. 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 *>
|
|
(§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 | 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 *> (§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) != 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 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:
|
|
/* 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 < 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;
|
|
}
|
|
|
|
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 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 hash_map<tree, auto_vec<tree>> attached_map_t;
|
|
static attached_map_t *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_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)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct duplicate_hash : nodel_ptr_hash<tree_node>
|
|
{
|
|
#if 0
|
|
/* This breaks variadic bases in the xtreme_header tests. Since ::equal is
|
|
the default pointer_hash::equal, let's use the default hash as well. */
|
|
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));
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/* 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, 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_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. +ve imports ok,
|
|
-ve imports not ok. */
|
|
#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 ()
|
|
/* Do not preallocate spans, as that causes
|
|
--enable-detailed-mem-stats problems. */
|
|
: spans (nullptr)
|
|
{
|
|
}
|
|
~loc_spans ()
|
|
{
|
|
delete spans;
|
|
}
|
|
|
|
public:
|
|
span &operator[] (unsigned ix)
|
|
{
|
|
return (*spans)[ix];
|
|
}
|
|
unsigned length () const
|
|
{
|
|
return spans->length ();
|
|
}
|
|
|
|
public:
|
|
bool init_p () const
|
|
{
|
|
return spans != nullptr;
|
|
}
|
|
/* 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;
|
|
/* Indirection to allow bsearching imports by ordinary location. */
|
|
static vec<module_state *> *ool;
|
|
|
|
/********************************************************************/
|
|
/* 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. */
|
|
bool inform_cmi_p : 1; /* Inform of a read/write. */
|
|
bool visited_p : 1; /* A walk-once flag. */
|
|
/* Record extensions emitted or permitted. */
|
|
unsigned extensions : SE_BITS;
|
|
/* 14 bits used, 2 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 a real module? */
|
|
bool has_location () 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);
|
|
|
|
void intercluster_seed (trees_out &sec, unsigned index, depset *dep);
|
|
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:
|
|
unsigned write_pendings (elf_out *to, vec<depset *> depsets,
|
|
depset::hash &, 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:
|
|
static 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;
|
|
|
|
inform_cmi_p = false;
|
|
visited_p = false;
|
|
|
|
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;
|
|
|
|
/* Deferred import queue (FIFO). */
|
|
static vec<module_state *, va_heap, vl_embed> *pending_imports;
|
|
|
|
/* 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];
|
|
|
|
/* Do not include the current TU (not an off-by-one error). */
|
|
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, 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);
|
|
tree not_tmpl = t;
|
|
if (TREE_CODE (t) == TEMPLATE_DECL)
|
|
{
|
|
fputs ("template ", stream);
|
|
not_tmpl = DECL_TEMPLATE_RESULT (t);
|
|
}
|
|
|
|
if (not_tmpl
|
|
&& DECL_P (not_tmpl)
|
|
&& DECL_LANG_SPECIFIC (not_tmpl)
|
|
&& DECL_MODULE_IMPORT_P (not_tmpl))
|
|
{
|
|
/* 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->has_location ())
|
|
str = "(detached)";
|
|
else
|
|
str = m->get_flatname ();
|
|
}
|
|
fputs (str, dumps->stream);
|
|
}
|
|
break;
|
|
|
|
case 'N': /* Name. */
|
|
{
|
|
tree t = va_arg (args, tree);
|
|
while (t && TREE_CODE (t) == OVERLOAD)
|
|
t = OVL_FUNCTION (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);
|
|
tree not_tmpl = STRIP_TEMPLATE (decl);
|
|
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 (not_tmpl)
|
|
|| !DECL_MODULE_PURVIEW_P (not_tmpl)
|
|
|| (!DECL_MODULE_IMPORT_P (not_tmpl)
|
|
&& header_module_p ())));
|
|
|
|
if (not_tmpl != decl)
|
|
gcc_assert (!note_defs->find_slot (not_tmpl, 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);
|
|
dump () && dump ("Mkdir ('%s') errno:=%u", path, failed ? errno : 0);
|
|
*base = sep;
|
|
if (failed
|
|
/* Maybe racing with another creator (of a *different*
|
|
module). */
|
|
&& errno != EEXIST)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Given a CLASSTYPE_DECL_LIST VALUE get 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 (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);
|
|
/* When building a header unit, everthing is marked as purview, but
|
|
that's the GM purview, so not what the importer will mean */
|
|
WB (lang->u.base.module_purview_p && !header_module_p ());
|
|
if (VAR_OR_FUNCTION_DECL_P (t))
|
|
WB (lang->u.base.module_attached_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_attached_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);
|
|
|
|
if (state)
|
|
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))
|
|
{
|
|
if (state)
|
|
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);
|
|
|
|
if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t))
|
|
WT (lookup_explicit_specifier (t));
|
|
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:
|
|
if (state)
|
|
{
|
|
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);
|
|
if (state)
|
|
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 : tsi_range (t))
|
|
if (stmt)
|
|
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);
|
|
if (state)
|
|
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);
|
|
if (state)
|
|
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);
|
|
if (state)
|
|
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);
|
|
|
|
if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t))
|
|
{
|
|
tree spec;
|
|
RT (spec);
|
|
store_explicit_specifier (t, spec);
|
|
}
|
|
}
|
|
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;
|
|
(*entity_ary)[ident] = decl;
|
|
|
|
/* And into the entity map, if it's not already there. */
|
|
tree not_tmpl = STRIP_TEMPLATE (decl);
|
|
if (!DECL_LANG_SPECIFIC (not_tmpl)
|
|
|| !DECL_MODULE_ENTITY_P (not_tmpl))
|
|
{
|
|
retrofit_lang_decl (not_tmpl);
|
|
DECL_MODULE_ENTITY_P (not_tmpl) = 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;
|
|
}
|
|
|
|
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;
|
|
|
|
tree not_tmpl = STRIP_TEMPLATE (o);
|
|
if (DECL_LANG_SPECIFIC (not_tmpl)
|
|
&& DECL_MODULE_PURVIEW_P (not_tmpl))
|
|
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)
|
|
{
|
|
inner = DECL_TEMPLATE_RESULT (decl);
|
|
inner_tag = insert (inner, WK_value);
|
|
|
|
if (streaming_p ())
|
|
{
|
|
int code = TREE_CODE (inner);
|
|
u (code);
|
|
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 (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 (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 (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);
|
|
}
|
|
|
|
if (type && !is_key_order ())
|
|
{
|
|
tree_node_vals (type);
|
|
if (stub_decl)
|
|
tree_node_vals (stub_decl);
|
|
}
|
|
|
|
if (!is_key_order ())
|
|
{
|
|
if (mk & MK_template_mask
|
|
|| mk == MK_partial
|
|
|| mk == MK_friend_spec)
|
|
{
|
|
if (mk != MK_partial)
|
|
{
|
|
// FIXME: We should make use of the merge-key by
|
|
// exposing it outside of key_mergeable. But this gets
|
|
// the job done.
|
|
auto *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
|
|
|
|
if (streaming_p ())
|
|
u (get_mergeable_specialization_flags (entry->tmpl, decl));
|
|
tree_node (entry->tmpl);
|
|
tree_node (entry->args);
|
|
}
|
|
else
|
|
{
|
|
tree_node (CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner)));
|
|
tree_node (CLASSTYPE_TI_ARGS (TREE_TYPE (inner)));
|
|
}
|
|
}
|
|
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 (VAR_OR_FUNCTION_DECL_P (inner)
|
|
&& DECL_LANG_SPECIFIC (inner)
|
|
&& DECL_MODULE_ATTACHMENTS_P (inner)
|
|
&& !is_key_order ())
|
|
{
|
|
/* Stream the attached entities. */
|
|
auto *attach_vec = attached_table->get (inner);
|
|
unsigned num = attach_vec->length ();
|
|
if (streaming_p ())
|
|
u (num);
|
|
for (unsigned ix = 0; ix != num; ix++)
|
|
{
|
|
tree attached = (*attach_vec)[ix];
|
|
tree_node (attached);
|
|
if (streaming_p ())
|
|
dump (dumper::MERGE)
|
|
&& dump ("Written %d[%u] attached decl %N", tag, ix, attached);
|
|
}
|
|
}
|
|
|
|
bool is_typedef = false;
|
|
if (!type && TREE_CODE (inner) == TYPE_DECL)
|
|
{
|
|
tree t = TREE_TYPE (inner);
|
|
unsigned tdef_flags = 0;
|
|
if (DECL_ORIGINAL_TYPE (inner)
|
|
&& TYPE_NAME (TREE_TYPE (inner)) == inner)
|
|
{
|
|
tdef_flags |= 1;
|
|
if (TYPE_STRUCTURAL_EQUALITY_P (t)
|
|
&& TYPE_DEPENDENT_P_VALID (t)
|
|
&& TYPE_DEPENDENT_P (t))
|
|
tdef_flags |= 2;
|
|
}
|
|
if (streaming_p ())
|
|
u (tdef_flags);
|
|
|
|
if (tdef_flags & 1)
|
|
{
|
|
/* A typedef type. */
|
|
int type_tag = insert (t);
|
|
if (streaming_p ())
|
|
dump (dumper::TREE)
|
|
&& dump ("Cloned:%d %s %C:%N", type_tag,
|
|
tdef_flags & 2 ? "depalias" : "typedef",
|
|
TREE_CODE (t), t);
|
|
|
|
is_typedef = true;
|
|
}
|
|
}
|
|
|
|
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 (NAMESPACE_SCOPE_P (inner))
|
|
gcc_checking_assert (!dep == (VAR_OR_FUNCTION_DECL_P (inner)
|
|
&& DECL_LOCAL_DECL_P (inner)));
|
|
else if ((TREE_CODE (inner) == TYPE_DECL
|
|
&& !is_typedef
|
|
&& TYPE_NAME (TREE_TYPE (inner)) == inner)
|
|
|| 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 ();
|
|
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 && 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 (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;
|
|
}
|
|
|
|
if (type && (!tree_node_vals (type)
|
|
|| (stub_decl && !tree_node_vals (stub_decl))))
|
|
goto bail;
|
|
|
|
spec_entry spec;
|
|
unsigned spec_flags = 0;
|
|
if (mk & MK_template_mask
|
|
|| mk == MK_partial
|
|
|| mk == MK_friend_spec)
|
|
{
|
|
if (mk == MK_partial)
|
|
spec_flags = 2;
|
|
else
|
|
spec_flags = u ();
|
|
|
|
spec.tmpl = tree_node ();
|
|
spec.args = tree_node ();
|
|
}
|
|
/* Hold constraints on the spec field, for a short while. */
|
|
spec.spec = tree_node ();
|
|
|
|
dump (dumper::TREE) && dump ("Read:%d %C:%N", tag, TREE_CODE (decl), decl);
|
|
|
|
existing = back_refs[~tag];
|
|
bool installed = install_entity (existing);
|
|
bool is_new = existing == decl;
|
|
|
|
if (VAR_OR_FUNCTION_DECL_P (inner)
|
|
&& DECL_LANG_SPECIFIC (inner)
|
|
&& DECL_MODULE_ATTACHMENTS_P (inner))
|
|
{
|
|
/* Read and maybe install the attached entities. */
|
|
bool existed;
|
|
auto &set = attached_table->get_or_insert (STRIP_TEMPLATE (existing),
|
|
&existed);
|
|
unsigned num = u ();
|
|
if (is_new == existed)
|
|
set_overrun ();
|
|
if (is_new)
|
|
set.reserve (num);
|
|
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.quick_push (attached);
|
|
else if (set[ix] != attached)
|
|
set_overrun ();
|
|
}
|
|
}
|
|
|
|
/* Regular typedefs will have a NULL TREE_TYPE at this point. */
|
|
unsigned tdef_flags = 0;
|
|
bool is_typedef = false;
|
|
if (!type && TREE_CODE (inner) == TYPE_DECL)
|
|
{
|
|
tdef_flags = u ();
|
|
if (tdef_flags & 1)
|
|
is_typedef = true;
|
|
}
|
|
|
|
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. */
|
|
retrofit_lang_decl (inner);
|
|
DECL_MODULE_IMPORT_P (inner) = true;
|
|
}
|
|
|
|
if (spec.spec)
|
|
set_constraints (decl, spec.spec);
|
|
|
|
if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl))
|
|
{
|
|
decl = cache_integer_cst (decl, true);
|
|
back_refs[~tag] = decl;
|
|
}
|
|
|
|
if (is_typedef)
|
|
{
|
|
/* Frob it to be ready for cloning. */
|
|
TREE_TYPE (inner) = DECL_ORIGINAL_TYPE (inner);
|
|
DECL_ORIGINAL_TYPE (inner) = NULL_TREE;
|
|
set_underlying_type (inner);
|
|
if (tdef_flags & 2)
|
|
{
|
|
/* Match instantiate_alias_template's handling. */
|
|
tree type = TREE_TYPE (inner);
|
|
TYPE_DEPENDENT_P (type) = true;
|
|
TYPE_DEPENDENT_P_VALID (type) = true;
|
|
SET_TYPE_STRUCTURAL_EQUALITY (type);
|
|
}
|
|
}
|
|
|
|
if (inner_tag)
|
|
/* Set the TEMPLATE_DECL's type. */
|
|
TREE_TYPE (decl) = TREE_TYPE (inner);
|
|
|
|
if (mk & MK_template_mask
|
|
|| mk == MK_partial)
|
|
{
|
|
/* Add to specialization tables now that constraints etc are
|
|
added. */
|
|
bool is_type = mk == MK_partial || !(mk & MK_tmpl_decl_mask);
|
|
|
|
spec.spec = is_type ? type : mk & MK_tmpl_tmpl_mask ? inner : decl;
|
|
add_mergeable_specialization (!is_type,
|
|
!is_type && mk & MK_tmpl_alias_mask,
|
|
&spec, decl, spec_flags);
|
|
}
|
|
|
|
if (NAMESPACE_SCOPE_P (decl)
|
|
&& (mk == MK_named || mk == MK_unique
|
|
|| mk == MK_enum || mk == MK_friend_spec)
|
|
&& !(VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl)))
|
|
add_module_namespace_decl (CP_DECL_CONTEXT (decl), decl);
|
|
|
|
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, is_typedef))
|
|
unmatched_duplicate (existing);
|
|
|
|
if (TREE_CODE (inner) == FUNCTION_DECL)
|
|
{
|
|
tree e_inner = STRIP_TEMPLATE (existing);
|
|
for (auto parm = DECL_ARGUMENTS (inner);
|
|
parm; parm = DECL_CHAIN (parm))
|
|
DECL_CONTEXT (parm) = e_inner;
|
|
}
|
|
|
|
/* And our result is the existing node. */
|
|
decl = existing;
|
|
}
|
|
|
|
if (mk == MK_friend_spec)
|
|
{
|
|
tree e = match_mergeable_specialization (true, &spec);
|
|
if (!e)
|
|
{
|
|
spec.spec = inner;
|
|
add_mergeable_specialization (true, false, &spec, decl, spec_flags);
|
|
}
|
|
else if (e != existing)
|
|
set_overrun ();
|
|
}
|
|
|
|
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 (!NAMESPACE_SCOPE_P (inner)
|
|
&& ((TREE_CODE (inner) == TYPE_DECL
|
|
&& !is_typedef
|
|
&& TYPE_NAME (TREE_TYPE (inner)) == inner)
|
|
|| 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)))
|
|
{
|
|
auto kind = (TREE_CODE (decl) == NAMESPACE_DECL
|
|
&& !DECL_NAMESPACE_ALIAS (decl)
|
|
? depset::EK_NAMESPACE : depset::EK_DECL);
|
|
dep = dep_hash->add_dependency (decl, kind);
|
|
}
|
|
|
|
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
|
|
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);
|
|
o = STRIP_TEMPLATE (o);
|
|
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 = TYPE_ALIGN_RAW (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:
|
|
case DEPENDENT_OPERATOR_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:
|
|
case DEPENDENT_OPERATOR_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) >> 1);
|
|
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 and NAMESPACE_DECLs cannot have DECL_TEMPLATE_INFO --
|
|
this isn't permitting them to have one. */
|
|
gcc_checking_assert (TREE_CODE (decl) == USING_DECL
|
|
|| TREE_CODE (decl) == NAMESPACE_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 ());
|
|
|
|
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)
|
|
{
|
|
spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
|
|
if (TREE_CODE (entry->spec) != TEMPLATE_DECL)
|
|
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 (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);
|
|
|
|
gcc_assert (existing);
|
|
if (mk & MK_tmpl_decl_mask)
|
|
{
|
|
if (mk & MK_tmpl_alias_mask)
|
|
/* It should be in both tables. */
|
|
gcc_checking_assert
|
|
(same_type_p (match_mergeable_specialization (false, entry),
|
|
TREE_TYPE (existing)));
|
|
if (mk & MK_tmpl_tmpl_mask)
|
|
existing = DECL_TI_TEMPLATE (existing);
|
|
}
|
|
else
|
|
{
|
|
if (mk & MK_tmpl_tmpl_mask)
|
|
existing = CLASSTYPE_TI_TEMPLATE (existing);
|
|
else
|
|
existing = TYPE_NAME (existing);
|
|
}
|
|
|
|
/* The walkabout should have found ourselves. */
|
|
gcc_checking_assert (TREE_CODE (decl) == TYPE_DECL
|
|
? same_type_p (TREE_TYPE (decl),
|
|
TREE_TYPE (existing))
|
|
: 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 (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);
|
|
}
|
|
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);
|
|
auto *root = attached_table->get (scope);
|
|
unsigned ix = root->length ();
|
|
/* If we don't find it, we'll write a really big number
|
|
that the reader will ignore. */
|
|
while (ix--)
|
|
if ((*root)[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:
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
// FIXME: We could stream the specialization hash?
|
|
spec_entry spec;
|
|
spec.tmpl = tree_node ();
|
|
spec.args = tree_node ();
|
|
|
|
if (get_overrun ())
|
|
return error_mark_node;
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
spec.spec = (mk & MK_tmpl_tmpl_mask) ? inner : decl;
|
|
}
|
|
else
|
|
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)
|
|
; /* We'll add to the table once read. */
|
|
else if (mk & MK_tmpl_decl_mask)
|
|
{
|
|
/* A declaration specialization. */
|
|
if (mk & MK_tmpl_tmpl_mask)
|
|
existing = DECL_TI_TEMPLATE (existing);
|
|
}
|
|
else
|
|
{
|
|
/* A type specialization. */
|
|
if (mk & MK_tmpl_tmpl_mask)
|
|
existing = CLASSTYPE_TI_TEMPLATE (existing);
|
|
else
|
|
existing = TYPE_NAME (existing);
|
|
}
|
|
}
|
|
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)
|
|
&& 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);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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 (auto *set = attached_table->get (name))
|
|
if (key.index < set->length ())
|
|
{
|
|
existing = (*set)[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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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, bool is_typedef)
|
|
{
|
|
// 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 d_inner = decl;
|
|
tree e_inner = existing;
|
|
if (TREE_CODE (decl) == TEMPLATE_DECL)
|
|
{
|
|
d_inner = DECL_TEMPLATE_RESULT (d_inner);
|
|
e_inner = DECL_TEMPLATE_RESULT (e_inner);
|
|
gcc_checking_assert (TREE_CODE (e_inner) == TREE_CODE (d_inner));
|
|
}
|
|
|
|
if (TREE_CODE (d_inner) == FUNCTION_DECL)
|
|
{
|
|
tree e_ret = fndecl_declared_return_type (existing);
|
|
tree d_ret = fndecl_declared_return_type (decl);
|
|
|
|
if (decl != d_inner && DECL_NAME (d_inner) == fun_identifier
|
|
&& LAMBDA_TYPE_P (DECL_CONTEXT (d_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 (e_inner);
|
|
tree d_type = TREE_TYPE (d_inner);
|
|
|
|
if (DECL_EXTERN_C_P (d_inner) != DECL_EXTERN_C_P (e_inner))
|
|
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;
|
|
}
|
|
else if (is_typedef)
|
|
{
|
|
if (!DECL_ORIGINAL_TYPE (e_inner)
|
|
|| !same_type_p (DECL_ORIGINAL_TYPE (d_inner),
|
|
DECL_ORIGINAL_TYPE (e_inner)))
|
|
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:
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (TREE_CODE (d_inner) == FUNCTION_DECL
|
|
&& DECL_DECLARED_INLINE_P (d_inner))
|
|
DECL_DECLARED_INLINE_P (e_inner) = true;
|
|
if (!DECL_EXTERNAL (d_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_LANG_SPECIFIC (decl)
|
|
&& 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 additional 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 (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;
|
|
if (maybe_dup && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (maybe_dup))
|
|
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_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 *, 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);
|
|
if (!ANON_AGGR_TYPE_P (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) == FIELD_DECL
|
|
&& ANON_AGGR_TYPE_P (TREE_TYPE (decl)))
|
|
ANON_AGGR_TYPE_FIELD
|
|
(TYPE_MAIN_VARIANT (TREE_TYPE (decl))) = 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. */
|
|
gcc_checking_assert (DECL_MODULE_EXPORT_P (decl)
|
|
== DECL_MODULE_EXPORT_P (DECL_TEMPLATE_RESULT (decl)));
|
|
|
|
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)
|
|
{
|
|
tree not_tmpl = STRIP_TEMPLATE (decl);
|
|
|
|
if (DECL_LANG_SPECIFIC (not_tmpl)
|
|
&& DECL_MODULE_IMPORT_P (not_tmpl))
|
|
{
|
|
/* 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);
|
|
|
|
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;
|
|
};
|
|
|
|
/* Return true if we are, or contain something that is exported. */
|
|
|
|
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 bool (flags & WMB_Export);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* Make sure nobody left a tree visited lying about. */
|
|
gcc_checking_assert (!TREE_VISITED (decl));
|
|
|
|
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 (flags & WMB_Using
|
|
? flags & WMB_Export : DECL_MODULE_EXPORT_P (decl));
|
|
}
|
|
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))
|
|
{
|
|
/* It contains an exported thing, so it is exported. */
|
|
gcc_checking_assert (DECL_MODULE_PURVIEW_P (decl));
|
|
DECL_MODULE_EXPORT_P (decl) = true;
|
|
}
|
|
|
|
if (DECL_MODULE_PURVIEW_P (decl))
|
|
{
|
|
data->hash->make_dependency (decl, depset::EK_NAMESPACE);
|
|
|
|
return DECL_MODULE_EXPORT_P (decl);
|
|
}
|
|
}
|
|
|
|
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 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.cc 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)
|
|
== !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 (STRIP_TEMPLATE (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
|
|
|
|
/* Make sure nobody left a tree visited lying about. */
|
|
gcc_checking_assert (!TREE_VISITED (spec));
|
|
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);
|
|
}
|
|
|
|
/* Find the innermost-namespace scope of DECL, and that
|
|
namespace-scope decl. */
|
|
|
|
tree
|
|
find_pending_key (tree decl, tree *decl_p = nullptr)
|
|
{
|
|
tree ns = decl;
|
|
do
|
|
{
|
|
decl = ns;
|
|
ns = CP_DECL_CONTEXT (ns);
|
|
if (TYPE_P (ns))
|
|
ns = TYPE_NAME (ns);
|
|
}
|
|
while (TREE_CODE (ns) != NAMESPACE_DECL);
|
|
|
|
if (decl_p)
|
|
*decl_p = decl;
|
|
|
|
return ns;
|
|
}
|
|
|
|
/* Iteratively find dependencies. During the walk we may find more
|
|
entries on the same binding that need walking. */
|
|
|
|
void
|
|
depset::hash::find_dependencies (module_state *module)
|
|
{
|
|
trees_out walker (NULL, module, *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 (item->get_entity_kind () == EK_NAMESPACE)
|
|
add_namespace_context (current, CP_DECL_CONTEXT (decl));
|
|
else
|
|
{
|
|
walker.mark_declaration (decl, current->has_defn ());
|
|
|
|
if (!walker.is_key_order ()
|
|
&& (item->get_entity_kind () == EK_SPECIALIZATION
|
|
|| item->get_entity_kind () == EK_PARTIAL
|
|
|| (item->get_entity_kind () == EK_DECL
|
|
&& item->is_member ())))
|
|
{
|
|
tree ns = find_pending_key (decl, nullptr);
|
|
add_namespace_context (item, ns);
|
|
}
|
|
|
|
// 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 (nullptr);
|
|
|
|
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;
|
|
}
|
|
|
|
/* Initialize location spans. */
|
|
|
|
void
|
|
loc_spans::init (const line_maps *lmaps, const line_map_ordinary *map)
|
|
{
|
|
gcc_checking_assert (!init_p ());
|
|
spans = new vec<span> ();
|
|
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 hwm)
|
|
{
|
|
bool opened = (module_interface_p () && !module_partition_p ()
|
|
&& import->is_partition ());
|
|
if (opened)
|
|
open (hwm);
|
|
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)
|
|
{
|
|
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);
|
|
if (spans->length ())
|
|
{
|
|
/* No overlapping! */
|
|
auto &last = spans->last ();
|
|
gcc_checking_assert (interval.ordinary.first >= last.ordinary.second);
|
|
gcc_checking_assert (interval.macro.second <= last.macro.first);
|
|
}
|
|
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 (from >= LINEMAPS_MACRO_LOWEST_LOCATION (lmaps))
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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);
|
|
else
|
|
{
|
|
if (parent)
|
|
parent->mangle (include_partition);
|
|
if (include_partition || !is_partition ())
|
|
{
|
|
// Partitions are significant for global initializer
|
|
// functions
|
|
bool partition = is_partition () && !parent->is_partition ();
|
|
subst = mangle_module_component (name, partition);
|
|
substs.safe_push (this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
mangle_module (int mod, bool include_partition)
|
|
{
|
|
module_state *imp = (*modules)[mod];
|
|
|
|
gcc_checking_assert (!imp->is_header ());
|
|
|
|
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 /* Partition should be ... */
|
|
|| !imp->is_partition () /* a partition ... */
|
|
|| imp->loadedness != ML_NONE /* that is not yet loaded ... */
|
|
|| get_primary (imp) != this) /* whose primary is this. */
|
|
{
|
|
sec.set_overrun ();
|
|
break;
|
|
}
|
|
|
|
if (!imp->has_location ())
|
|
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 num_entities;
|
|
unsigned ordinary_locs;
|
|
unsigned macro_locs;
|
|
unsigned ordinary_loc_align;
|
|
|
|
public:
|
|
module_state_config ()
|
|
:dialect_str (get_dialect ()),
|
|
num_imports (0), num_partitions (0), num_entities (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. */
|
|
};
|
|
|
|
/* DEP belongs to a different cluster, seed it to prevent
|
|
unfortunately timed duplicate import. */
|
|
// FIXME: QOI For inter-cluster references we could just only pick
|
|
// one entity from an earlier cluster. Even better track
|
|
// dependencies between earlier clusters
|
|
|
|
void
|
|
module_state::intercluster_seed (trees_out &sec, unsigned index_hwm, depset *dep)
|
|
{
|
|
if (dep->is_import ()
|
|
|| dep->cluster < index_hwm)
|
|
{
|
|
tree ent = dep->get_entity ();
|
|
if (!TREE_VISITED (ent))
|
|
{
|
|
sec.tree_node (ent);
|
|
dump (dumper::CLUSTER)
|
|
&& dump ("Seeded %s %N",
|
|
dep->is_import () ? "import" : "intercluster", ent);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write the cluster of depsets in SCC[0-SIZE).
|
|
dep->section -> section number
|
|
dep->cluster -> entity number
|
|
*/
|
|
|
|
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 ();
|
|
unsigned index_lwm = counts[MSC_entities];
|
|
|
|
/* 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 ());
|
|
depset *ns_dep = b->deps[0];
|
|
gcc_checking_assert (ns_dep->get_entity_kind ()
|
|
== depset::EK_NAMESPACE
|
|
&& ns_dep->get_entity () == b->get_entity ());
|
|
for (unsigned jx = b->deps.length (); --jx;)
|
|
{
|
|
depset *dep = b->deps[jx];
|
|
// We could be declaring something that is also a
|
|
// (merged) import
|
|
gcc_checking_assert (dep->is_import ()
|
|
|| TREE_VISITED (dep->get_entity ())
|
|
|| (dep->get_entity_kind ()
|
|
== depset::EK_USING));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case depset::EK_DECL:
|
|
case depset::EK_SPECIALIZATION:
|
|
case depset::EK_PARTIAL:
|
|
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 out-of-cluster decl is referenced before we start
|
|
streaming. We must do both imports *and* earlier clusters,
|
|
because the latter could reach into the former and cause a
|
|
duplicate loop. */
|
|
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 ())
|
|
{
|
|
for (unsigned ix = dep->deps.length (); --ix;)
|
|
{
|
|
depset *bind = dep->deps[ix];
|
|
if (bind->get_entity_kind () == depset::EK_USING)
|
|
bind = bind->deps[1];
|
|
|
|
intercluster_seed (sec, index_lwm, bind);
|
|
}
|
|
/* Also check the namespace itself. */
|
|
dep = dep->deps[0];
|
|
}
|
|
|
|
intercluster_seed (sec, index_lwm, dep);
|
|
}
|
|
}
|
|
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_dependent_aliases++;
|
|
|
|
/* 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 ();
|
|
}
|
|
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))
|
|
vec_safe_push (post_load_decls, decl);
|
|
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.cc'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_dependent_aliases--;
|
|
|
|
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);
|
|
/* P1815 may have something to say about this. */
|
|
gcc_checking_assert (TREE_PUBLIC (ns));
|
|
|
|
unsigned flags = 0;
|
|
if (TREE_PUBLIC (ns))
|
|
flags |= 1;
|
|
if (DECL_NAMESPACE_INLINE_P (ns))
|
|
flags |= 2;
|
|
if (DECL_MODULE_PURVIEW_P (ns))
|
|
flags |= 4;
|
|
if (DECL_MODULE_EXPORT_P (ns))
|
|
flags |= 8;
|
|
|
|
dump () && dump ("Writing namespace:%u %N%s%s%s%s",
|
|
b->cluster, ns,
|
|
flags & 1 ? ", public" : "",
|
|
flags & 2 ? ", inline" : "",
|
|
flags & 4 ? ", purview" : "",
|
|
flags & 8 ? ", export" : "");
|
|
sec.u (b->cluster);
|
|
sec.u (to->name (DECL_NAME (ns)));
|
|
write_namespace (sec, b->deps[0]);
|
|
|
|
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
|
|
|| (flags & 0xc) == 0x8)
|
|
sec.set_overrun ();
|
|
if (sec.get_overrun ())
|
|
break;
|
|
|
|
tree id = name ? get_identifier (from ()->name (name)) : NULL_TREE;
|
|
|
|
dump () && dump ("Read namespace:%u %P%s%s%s%s",
|
|
entity_index, parent, id,
|
|
flags & 1 ? ", public" : "",
|
|
flags & 2 ? ", inline" : "",
|
|
flags & 4 ? ", purview" : "",
|
|
flags & 8 ? ", export" : "");
|
|
bool visible_p = ((flags & 8)
|
|
|| ((flags & 1)
|
|
&& (flags & 4)
|
|
&& (is_partition () || is_module ())));
|
|
tree inner = add_imported_namespace (parent, id, src_loc, mod,
|
|
bool (flags & 2), visible_p);
|
|
if (!inner)
|
|
{
|
|
sec.set_overrun ();
|
|
break;
|
|
}
|
|
|
|
if (is_partition ())
|
|
{
|
|
if (flags & 4)
|
|
DECL_MODULE_PURVIEW_P (inner) = true;
|
|
if (flags & 8)
|
|
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 ();
|
|
|
|
for (binding_slot *slot = entity_ary->begin () + entity_lwm; count--; slot++)
|
|
{
|
|
unsigned snum = sec.u ();
|
|
if (snum && (snum - lwm) >= (hwm - lwm))
|
|
sec.set_overrun ();
|
|
if (sec.get_overrun ())
|
|
break;
|
|
|
|
if (snum)
|
|
slot->set_lazy (snum << 2);
|
|
}
|
|
|
|
dump.outdent ();
|
|
if (!sec.end (from ()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* Write the pending table to MOD_SNAME_PFX.pnd
|
|
|
|
The pending table holds information about clusters that need to be
|
|
loaded because they contain information about something that is not
|
|
found by namespace-scope lookup.
|
|
|
|
The three cases are:
|
|
|
|
(a) Template (maybe-partial) specializations that we have
|
|
instantiated or defined. When an importer needs to instantiate
|
|
that template, they /must have/ the partial, explicit & extern
|
|
specializations available. If they have the other specializations
|
|
available, they'll have less work to do. Thus, when we're about to
|
|
instantiate FOO, we have to be able to ask 'are there any
|
|
specialization of FOO in our imports?'.
|
|
|
|
(b) (Maybe-implicit) member functions definitions. A class could
|
|
be defined in one header, and an inline member defined in a
|
|
different header (this occurs in the STL). Similarly, like the
|
|
specialization case, an implicit member function could have been
|
|
'instantiated' in one module, and it'd be nice to not have to
|
|
reinstantiate it in another.
|
|
|
|
(c) A member classes completed elsewhere. A member class could be
|
|
declared in one header and defined in another. We need to know to
|
|
load the class definition before looking in it. This turns out to
|
|
be a specific case of #b, so we can treat these the same. But it
|
|
does highlight an issue -- there could be an intermediate import
|
|
between the outermost containing namespace-scope class and the
|
|
innermost being-defined member class. This is actually possible
|
|
with all of these cases, so be aware -- we're not just talking of
|
|
one level of import to get to the innermost namespace.
|
|
|
|
This gets complicated fast, it took me multiple attempts to even
|
|
get something remotely working. Partially because I focussed on
|
|
optimizing what I think turns out to be a smaller problem, given
|
|
the known need to do the more general case *anyway*. I document
|
|
the smaller problem, because it does appear to be the natural way
|
|
to do it. It's trap!
|
|
|
|
**** THE TRAP
|
|
|
|
Let's refer to the primary template or the containing class as the
|
|
KEY. And the specialization or member as the PENDING-ENTITY. (To
|
|
avoid having to say those mouthfuls all the time.)
|
|
|
|
In either case, we have an entity and we need some way of mapping
|
|
that to a set of entities that need to be loaded before we can
|
|
proceed with whatever processing of the entity we were going to do.
|
|
|
|
We need to link the key to the pending-entity in some way. Given a
|
|
key, tell me the pending-entities I need to have loaded. However
|
|
we tie the key to the pending-entity must not rely on the key being
|
|
loaded -- that'd defeat the lazy loading scheme.
|
|
|
|
As the key will be an import in we know its entity number (either
|
|
because we imported it, or we're writing it out too). Thus we can
|
|
generate a map of key-indices to pending-entities. The
|
|
pending-entity indices will be into our span of the entity table,
|
|
and thus allow them to be lazily loaded. The key index will be
|
|
into another slot of the entity table. Notice that this checking
|
|
could be expensive, we don't want to iterate over a bunch of
|
|
pending-entity indices (across multiple imports), every time we're
|
|
about do to the thing with the key. We need to quickly determine
|
|
'definitely nothing needed'.
|
|
|
|
That's almost good enough, except that key indices are not unique
|
|
in a couple of cases :( Specifically the Global Module or a module
|
|
partition can result in multiple modules assigning an entity index
|
|
for the key. The decl-merging on loading will detect that so we
|
|
only have one Key loaded, and in the entity hash it'll indicate the
|
|
entity index of first load. Which might be different to how we
|
|
know it. Notice this is restricted to GM entities or this-module
|
|
entities. Foreign imports cannot have this.
|
|
|
|
We can simply resolve this in the direction of how this module
|
|
referred to the key to how the importer knows it. Look in the
|
|
entity table slot that we nominate, maybe lazy load it, and then
|
|
lookup the resultant entity in the entity hash to learn how the
|
|
importer knows it.
|
|
|
|
But we need to go in the other direction :( Given the key, find all
|
|
the index-aliases of that key. We can partially solve that by
|
|
adding an alias hash table. Whenever we load a merged decl, add or
|
|
augment a mapping from the entity (or its entity-index) to the
|
|
newly-discovered index. Then when we look for pending entities of
|
|
a key, we also iterate over this aliases this mapping provides.
|
|
|
|
But that requires the alias to be loaded. And that's not
|
|
necessarily true.
|
|
|
|
*** THE SIMPLER WAY
|
|
|
|
The remaining fixed thing we have is the innermost namespace
|
|
containing the ultimate namespace-scope container of the key and
|
|
the name of that container (which might be the key itself). I.e. a
|
|
namespace-decl/identifier/module tuple. Let's call this the
|
|
top-key. We'll discover that the module is not important here,
|
|
because of cross-module possibilities mentioned in case #c above.
|
|
We can't markup namespace-binding slots. The best we can do is
|
|
mark the binding vector with 'there's something here', and have
|
|
another map from namespace/identifier pairs to a vector of pending
|
|
entity indices.
|
|
|
|
Maintain a pending-entity map. This is keyed by top-key, and
|
|
maps to a vector of pending-entity indices. On the binding vector
|
|
have flags saying whether the pending-name-entity map has contents.
|
|
(We might want to further extend the key to be GM-vs-Partition and
|
|
specialization-vs-member, but let's not get ahead of ourselves.)
|
|
|
|
For every key-like entity, find the outermost namespace-scope
|
|
name. Use that to lookup in the pending-entity map and then make
|
|
sure the specified entities are loaded.
|
|
|
|
An optimization might be to have a flag in each key-entity saying
|
|
that it's top key might be in the entity table. It's not clear to
|
|
me how to set that flag cheaply -- cheaper than just looking.
|
|
|
|
FIXME: It'd be nice to have a bit in decls to tell us whether to
|
|
even try this. We can have a 'already done' flag, that we set when
|
|
we've done KLASS's lazy pendings. When we import a module that
|
|
registers pendings on the same top-key as KLASS we need to clear
|
|
the flag. A recursive walk of the top-key clearing the bit will
|
|
suffice. Plus we only need to recurse on classes that have the bit
|
|
set. (That means we need to set the bit on parents of KLASS here,
|
|
don't forget.) However, first: correctness, second: efficiency. */
|
|
|
|
unsigned
|
|
module_state::write_pendings (elf_out *to, vec<depset *> depsets,
|
|
depset::hash &table, unsigned *crc_p)
|
|
{
|
|
dump () && dump ("Writing pending-entities");
|
|
dump.indent ();
|
|
|
|
trees_out sec (to, this, table);
|
|
sec.begin ();
|
|
|
|
unsigned count = 0;
|
|
tree cache_ns = NULL_TREE;
|
|
tree cache_id = NULL_TREE;
|
|
unsigned cache_section = ~0;
|
|
for (unsigned ix = 0; ix < depsets.length (); ix++)
|
|
{
|
|
depset *d = depsets[ix];
|
|
|
|
if (d->is_binding ())
|
|
continue;
|
|
|
|
if (d->is_import ())
|
|
continue;
|
|
|
|
if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION
|
|
|| d->get_entity_kind () == depset::EK_PARTIAL
|
|
|| (d->get_entity_kind () == depset::EK_DECL && d->is_member ())))
|
|
continue;
|
|
|
|
tree key_decl = nullptr;
|
|
tree key_ns = find_pending_key (d->get_entity (), &key_decl);
|
|
tree key_name = DECL_NAME (key_decl);
|
|
|
|
if (IDENTIFIER_ANON_P (key_name))
|
|
{
|
|
gcc_checking_assert (IDENTIFIER_LAMBDA_P (key_name));
|
|
if (tree attached = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (key_decl)))
|
|
key_name = DECL_NAME (attached);
|
|
else
|
|
{
|
|
/* There's nothing to attach it to. Must
|
|
always reinstantiate. */
|
|
dump ()
|
|
&& dump ("Unattached lambda %N[%u] section:%u",
|
|
d->get_entity_kind () == depset::EK_DECL
|
|
? "Member" : "Specialization", d->get_entity (),
|
|
d->cluster, d->section);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
char const *also = "";
|
|
if (d->section == cache_section
|
|
&& key_ns == cache_ns
|
|
&& key_name == cache_id)
|
|
/* Same section & key as previous, no need to repeat ourselves. */
|
|
also = "also ";
|
|
else
|
|
{
|
|
cache_ns = key_ns;
|
|
cache_id = key_name;
|
|
cache_section = d->section;
|
|
gcc_checking_assert (table.find_dependency (cache_ns));
|
|
sec.tree_node (cache_ns);
|
|
sec.tree_node (cache_id);
|
|
sec.u (d->cluster);
|
|
count++;
|
|
}
|
|
dump () && dump ("Pending %s %N entity:%u section:%u %skeyed to %P",
|
|
d->get_entity_kind () == depset::EK_DECL
|
|
? "member" : "specialization", d->get_entity (),
|
|
d->cluster, cache_section, also, cache_ns, cache_id);
|
|
}
|
|
sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p);
|
|
dump.outdent ();
|
|
|
|
return count;
|
|
}
|
|
|
|
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++)
|
|
{
|
|
pending_key key;
|
|
unsigned index;
|
|
|
|
key.ns = sec.tree_node ();
|
|
key.id = sec.tree_node ();
|
|
index = sec.u ();
|
|
|
|
if (!key.ns || !key.id
|
|
|| !(TREE_CODE (key.ns) == NAMESPACE_DECL
|
|
&& !DECL_NAMESPACE_ALIAS (key.ns))
|
|
|| !identifier_p (key.id)
|
|
|| index >= entity_num)
|
|
sec.set_overrun ();
|
|
|
|
if (sec.get_overrun ())
|
|
break;
|
|
|
|
dump () && dump ("Pending:%u keyed to %P", index, key.ns, key.id);
|
|
|
|
index += entity_lwm;
|
|
auto &vec = pending_table->get_or_insert (key);
|
|
vec.safe_push (index);
|
|
}
|
|
|
|
dump.outdent ();
|
|
if (!sec.end (from ()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* 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 = 0;
|
|
unsigned len = ool->length () - pos;
|
|
|
|
while (len)
|
|
{
|
|
unsigned half = len / 2;
|
|
module_state *probe = (*ool)[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 nullptr;
|
|
}
|
|
|
|
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 (loc >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table))
|
|
{
|
|
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];
|
|
|
|
if (span.ordinary.first != span.ordinary.second)
|
|
{
|
|
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;
|
|
}
|
|
|
|
info.num_maps.first += omap - fmap;
|
|
}
|
|
|
|
if (span.macro.first != span.macro.second)
|
|
{
|
|
/* Iterate over the span's macros, to elide the empty
|
|
expansions. */
|
|
unsigned count = 0;
|
|
for (unsigned macro
|
|
= linemap_lookup_macro_index (line_table,
|
|
span.macro.second - 1);
|
|
macro < LINEMAPS_MACRO_USED (line_table);
|
|
macro++)
|
|
{
|
|
line_map_macro const *mmap
|
|
= LINEMAPS_MACRO_MAP_AT (line_table, macro);
|
|
if (MAP_START_LOCATION (mmap) < span.macro.first)
|
|
/* Fallen out of the span. */
|
|
break;
|
|
|
|
if (mmap->n_tokens)
|
|
count++;
|
|
}
|
|
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:%u maps hwm:%u macro:%u maps lwm:%u ",
|
|
info.num_maps.first, ord_off,
|
|
info.num_maps.second, 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.
|
|
(Not true when headers are still in play :() */
|
|
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 multiple
|
|
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)
|
|
/* Empty span. */
|
|
continue;
|
|
|
|
for (unsigned macro
|
|
= linemap_lookup_macro_index (line_table, span.macro.second - 1);
|
|
macro < LINEMAPS_MACRO_USED (line_table);
|
|
macro++)
|
|
{
|
|
line_map_macro const *mmap
|
|
= LINEMAPS_MACRO_MAP_AT (line_table, macro);
|
|
location_t start_loc = MAP_START_LOCATION (mmap);
|
|
if (start_loc < span.macro.first)
|
|
/* Fallen out of the span. */
|
|
break;
|
|
|
|
if (!mmap->n_tokens)
|
|
/* Empty expansion. */
|
|
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,
|
|
line_table->highest_location + 1);
|
|
|
|
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 GTY(()) 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 GTY(()) vec<macro_export, va_gc> *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;
|
|
}
|
|
|
|
/* We have to defer some post-load processing until we've completed
|
|
reading, because they can cause more reading. */
|
|
|
|
static void
|
|
post_load_processing ()
|
|
{
|
|
/* We mustn't cause a GC, our caller should have arranged for that
|
|
not to happen. */
|
|
gcc_checking_assert (function_depth);
|
|
|
|
if (!post_load_decls)
|
|
return;
|
|
|
|
tree old_cfd = current_function_decl;
|
|
struct function *old_cfun = cfun;
|
|
while (post_load_decls->length ())
|
|
{
|
|
tree decl = post_load_decls->pop ();
|
|
|
|
dump () && dump ("Post-load processing of %N", decl);
|
|
|
|
gcc_checking_assert (DECL_ABSTRACT_P (decl));
|
|
/* Cloning can cause loading -- specifically operator delete for
|
|
the deleting dtor. */
|
|
maybe_clone_body (decl);
|
|
}
|
|
|
|
cfun = old_cfun;
|
|
current_function_decl = old_cfd;
|
|
}
|
|
|
|
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 ();
|
|
|
|
lazy_snum = ~0u;
|
|
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);
|
|
}
|
|
lazy_snum = 0;
|
|
post_load_processing ();
|
|
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.num_entities);
|
|
|
|
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.num_entities = cfg.u ();
|
|
|
|
config.ordinary_locs = cfg.u ();
|
|
config.macro_locs = cfg.u ();
|
|
config.ordinary_loc_align = cfg.u ();
|
|
|
|
done:
|
|
return cfg.end (from ());
|
|
}
|
|
|
|
/* Comparator for ordering the Ordered Ordinary Location array. */
|
|
|
|
static int
|
|
ool_cmp (const void *a_, const void *b_)
|
|
{
|
|
auto *a = *static_cast<const module_state *const *> (a_);
|
|
auto *b = *static_cast<const module_state *const *> (b_);
|
|
if (a == b)
|
|
return 0;
|
|
else if (a->ordinary_locs.first < b->ordinary_locs.second)
|
|
return -1;
|
|
else
|
|
return +1;
|
|
}
|
|
|
|
/* 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 (this);
|
|
|
|
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 ();
|
|
|
|
vec_alloc (ool, modules->length ());
|
|
for (unsigned ix = modules->length (); --ix;)
|
|
{
|
|
auto *import = (*modules)[ix];
|
|
if (import->loadedness > ML_NONE
|
|
&& !(partitions && bitmap_bit_p (partitions, import->mod)))
|
|
ool->quick_push (import);
|
|
}
|
|
ool->qsort (ool_cmp);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* depset::cluster - entity number (on entities)
|
|
depset::section - cluster number */
|
|
/* 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. */
|
|
config.num_entities = counts[MSC_entities];
|
|
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. */
|
|
counts[MSC_pendings] = write_pendings (to, sccs, table, &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 ();
|
|
|
|
vec_free (ool);
|
|
|
|
/* 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]);
|
|
|
|
{
|
|
/* Allocate space in the entities array now -- that array must be
|
|
monotionically in step with the modules array. */
|
|
entity_lwm = vec_safe_length (entity_ary);
|
|
entity_num = config.num_entities;
|
|
gcc_checking_assert (modules->length () == 1
|
|
|| modules->last ()->entity_lwm <= entity_lwm);
|
|
vec_safe_reserve (entity_ary, config.num_entities);
|
|
|
|
binding_slot slot;
|
|
slot.u.binding = NULL_TREE;
|
|
for (unsigned count = config.num_entities; count--;)
|
|
entity_ary->quick_push (slot);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* 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. */
|
|
|
|
if (ok && counts[MSC_entities] != entity_num)
|
|
ok = false;
|
|
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;
|
|
}
|
|
lazy_snum = 0;
|
|
post_load_processing ();
|
|
|
|
ggc_collect ();
|
|
|
|
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);
|
|
tree not_tmpl = STRIP_TEMPLATE (owner);
|
|
|
|
if (!DECL_LANG_SPECIFIC (not_tmpl))
|
|
return for_mangle ? -1 : 0;
|
|
|
|
if (for_mangle && !DECL_MODULE_PURVIEW_P (not_tmpl))
|
|
return -1;
|
|
|
|
int mod = !DECL_MODULE_IMPORT_P (not_tmpl) ? 0 : get_importing_module (owner);
|
|
|
|
if (for_mangle && (*modules)[mod]->is_header ())
|
|
return -1;
|
|
|
|
return mod;
|
|
}
|
|
|
|
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;
|
|
tree not_tmpl = STRIP_TEMPLATE (decl);
|
|
if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl))
|
|
{
|
|
/* 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 (use_tpl == 2)
|
|
{
|
|
/* A partial specialization. Find that specialization's
|
|
template_decl. */
|
|
for (tree list = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
|
|
list; list = TREE_CHAIN (list))
|
|
if (DECL_TEMPLATE_RESULT (TREE_VALUE (list)) == decl)
|
|
{
|
|
decl = TREE_VALUE (list);
|
|
break;
|
|
}
|
|
}
|
|
else if (DECL_TEMPLATE_RESULT (tmpl) == decl)
|
|
decl = tmpl;
|
|
}
|
|
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 (not_tmpl) && DECL_MODULE_PURVIEW_P (not_tmpl))
|
|
== 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 (not_tmpl)
|
|
&& DECL_MODULE_PURVIEW_P (not_tmpl));
|
|
|
|
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;
|
|
|
|
decl = STRIP_TEMPLATE (decl);
|
|
|
|
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 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 attached_map_t (EXPERIMENT (1, 400));
|
|
|
|
auto &vec = attached_table->get_or_insert (ctx);
|
|
if (!vec.length ())
|
|
{
|
|
retrofit_lang_decl (ctx);
|
|
DECL_MODULE_ATTACHMENTS_P (ctx) = true;
|
|
}
|
|
vec.safe_push (decl);
|
|
}
|
|
|
|
/* 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);
|
|
if (note_module_cmi_yes || inform_cmi_p)
|
|
inform (loc, "reading CMI %qs", file);
|
|
fd = open (file, O_RDONLY | O_CLOEXEC | O_BINARY);
|
|
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;
|
|
post_load_processing ();
|
|
}
|
|
|
|
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 entities keyed to the top-key of DECL. */
|
|
|
|
void
|
|
lazy_load_pendings (tree decl)
|
|
{
|
|
tree key_decl;
|
|
pending_key key;
|
|
key.ns = find_pending_key (decl, &key_decl);
|
|
key.id = DECL_NAME (key_decl);
|
|
|
|
auto *pending_vec = pending_table ? pending_table->get (key) : nullptr;
|
|
if (!pending_vec)
|
|
return;
|
|
|
|
int count = errorcount + warningcount;
|
|
|
|
timevar_start (TV_MODULE_IMPORT);
|
|
bool ok = !recursive_lazy ();
|
|
if (ok)
|
|
{
|
|
function_depth++; /* Prevent GC */
|
|
unsigned n = dump.push (NULL);
|
|
dump () && dump ("Reading %u pending entities keyed to %P",
|
|
pending_vec->length (), key.ns, key.id);
|
|
for (unsigned ix = pending_vec->length (); ix--;)
|
|
{
|
|
unsigned index = (*pending_vec)[ix];
|
|
binding_slot *slot = &(*entity_ary)[index];
|
|
|
|
if (slot->is_lazy ())
|
|
{
|
|
module_state *import = import_entity_module (index);
|
|
if (!import->lazy_load (index - import->entity_lwm, slot))
|
|
ok = false;
|
|
}
|
|
else if (dump ())
|
|
{
|
|
module_state *import = import_entity_module (index);
|
|
dump () && dump ("Entity %M[%u] already loaded",
|
|
import, index - import->entity_lwm);
|
|
}
|
|
}
|
|
|
|
pending_table->remove (key);
|
|
dump.pop (n);
|
|
lazy_snum = 0;
|
|
post_load_processing ();
|
|
function_depth--;
|
|
}
|
|
|
|
timevar_stop (TV_MODULE_IMPORT);
|
|
|
|
if (!ok)
|
|
fatal_error (input_location, "failed to load pendings for %<%E%s%E%>",
|
|
key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
|
|
|
|
if (count != errorcount + warningcount)
|
|
inform (input_location, "during load of pendings for %<%E%s%E%>",
|
|
key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
|
|
}
|
|
|
|
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->has_location ());
|
|
if (import->loadedness == ML_NONE)
|
|
if (!import->do_import (reader, true))
|
|
gcc_unreachable ();
|
|
|
|
if (import->loadedness < ML_LANGUAGE)
|
|
{
|
|
if (!attached_table)
|
|
attached_table = new attached_map_t (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->has_location ());
|
|
|
|
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_CONFIG)
|
|
{
|
|
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->has_location ());
|
|
|
|
/* 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);
|
|
releasing_vec args; // There are no args
|
|
|
|
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_probe_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--;)
|
|
if (!strcmp ((*note_includes)[ix], path))
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Process the pending_import queue, making sure we know the
|
|
filenames. */
|
|
|
|
static void
|
|
name_pending_imports (cpp_reader *reader)
|
|
{
|
|
auto *mapper = get_mapper (cpp_main_loc (reader));
|
|
|
|
if (!vec_safe_length (pending_imports))
|
|
/* Not doing anything. */
|
|
return;
|
|
|
|
timevar_start (TV_MODULE_MAPPER);
|
|
|
|
auto n = dump.push (NULL);
|
|
dump () && dump ("Resolving direct import names");
|
|
bool want_deps = (bool (mapper->get_flags () & Cody::Flags::NameOnly)
|
|
|| cpp_get_deps (reader));
|
|
bool any = false;
|
|
|
|
for (unsigned ix = 0; ix != pending_imports->length (); ix++)
|
|
{
|
|
module_state *module = (*pending_imports)[ix];
|
|
gcc_checking_assert (module->is_direct ());
|
|
if (!module->filename && !module->visited_p)
|
|
{
|
|
bool export_p = (module->module_p
|
|
&& (module->is_partition () || module->exported_p));
|
|
|
|
Cody::Flags flags = Cody::Flags::None;
|
|
if (flag_preprocess_only
|
|
&& !(module->is_header () && !export_p))
|
|
{
|
|
if (!want_deps)
|
|
continue;
|
|
flags = Cody::Flags::NameOnly;
|
|
}
|
|
|
|
if (!any)
|
|
{
|
|
any = true;
|
|
mapper->Cork ();
|
|
}
|
|
if (export_p)
|
|
mapper->ModuleExport (module->get_flatname (), flags);
|
|
else
|
|
mapper->ModuleImport (module->get_flatname (), flags);
|
|
module->visited_p = true;
|
|
}
|
|
}
|
|
|
|
if (any)
|
|
{
|
|
auto response = mapper->Uncork ();
|
|
auto r_iter = response.begin ();
|
|
for (unsigned ix = 0; ix != pending_imports->length (); ix++)
|
|
{
|
|
module_state *module = (*pending_imports)[ix];
|
|
if (module->visited_p)
|
|
{
|
|
module->visited_p = false;
|
|
gcc_checking_assert (!module->filename);
|
|
|
|
module->set_filename (*r_iter);
|
|
++r_iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
dump.pop (n);
|
|
|
|
timevar_stop (TV_MODULE_MAPPER);
|
|
}
|
|
|
|
/* 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 ();
|
|
}
|
|
}
|
|
|
|
auto desired = ML_CONFIG;
|
|
if (is_import
|
|
&& module->is_header ()
|
|
&& (!cpp_get_options (reader)->preprocessed
|
|
|| cpp_get_options (reader)->directives_only))
|
|
/* We need preprocessor state now. */
|
|
desired = ML_PREPROCESSOR;
|
|
|
|
if (!is_import || module->loadedness < desired)
|
|
{
|
|
vec_safe_push (pending_imports, module);
|
|
|
|
if (desired == ML_PREPROCESSOR)
|
|
{
|
|
unsigned n = dump.push (NULL);
|
|
|
|
dump () && dump ("Reading %M preprocessor state", module);
|
|
name_pending_imports (reader);
|
|
|
|
/* Preserve the state of the line-map. */
|
|
unsigned 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 ();
|
|
|
|
timevar_start (TV_MODULE_IMPORT);
|
|
|
|
/* Load the config of each pending import -- we must assign
|
|
module numbers monotonically. */
|
|
for (unsigned ix = 0; ix != pending_imports->length (); ix++)
|
|
{
|
|
auto *import = (*pending_imports)[ix];
|
|
if (!(import->module_p
|
|
&& (import->is_partition () || import->exported_p))
|
|
&& import->loadedness == ML_NONE
|
|
&& (import->is_header () || !flag_preprocess_only))
|
|
{
|
|
unsigned n = dump.push (import);
|
|
import->do_import (reader, true);
|
|
dump.pop (n);
|
|
}
|
|
}
|
|
vec_free (pending_imports);
|
|
|
|
/* Restore the line-map state. */
|
|
spans.open (linemap_module_restore (line_table, pre_hwm));
|
|
|
|
/* Now read the preprocessor state of this particular
|
|
import. */
|
|
if (module->loadedness == ML_CONFIG
|
|
&& module->read_preprocessor (true))
|
|
module->import_macros ();
|
|
|
|
timevar_stop (TV_MODULE_IMPORT);
|
|
|
|
dump.pop (n);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
unsigned n = dump.push (NULL);
|
|
|
|
dump () && dump ("Completed phase-4 (tokenization) processing");
|
|
|
|
name_pending_imports (reader);
|
|
vec_free (pending_imports);
|
|
|
|
spans.maybe_init ();
|
|
spans.close ();
|
|
|
|
using iterator = hash_table<module_state_hash>::iterator;
|
|
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)
|
|
{
|
|
/* Find the main module -- remember, it's not yet in the module
|
|
array. */
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
dump.pop (n);
|
|
}
|
|
|
|
/* 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)
|
|
/* Canonicalize header names. */
|
|
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] = 0;
|
|
|
|
(*note_includes)[ix] = path;
|
|
}
|
|
|
|
if (note_cmis)
|
|
/* Canonicalize & mark module names. */
|
|
for (unsigned ix = 0; ix != note_cmis->length (); ix++)
|
|
{
|
|
const char *name = (*note_cmis)[ix];
|
|
size_t len = strlen (name);
|
|
|
|
bool is_system = name[0] == '<';
|
|
bool is_user = name[0] == '"';
|
|
bool is_pathname = false;
|
|
if (!(is_system || is_user))
|
|
for (unsigned ix = len; !is_pathname && ix--;)
|
|
is_pathname = IS_DIR_SEPARATOR (name[ix]);
|
|
if (is_system || is_user || is_pathname)
|
|
{
|
|
if (len <= (is_pathname ? 0 : 2)
|
|
|| (!is_pathname && name[len-1] != (is_system ? '>' : '"')))
|
|
{
|
|
error ("invalid header name %qs", name);
|
|
continue;
|
|
}
|
|
else
|
|
name = canonicalize_header_name (is_pathname ? nullptr : reader,
|
|
0, is_pathname, name, len);
|
|
}
|
|
if (auto module = get_module (name))
|
|
module->inform_cmi_p = 1;
|
|
else
|
|
error ("invalid module name %qs", name);
|
|
}
|
|
|
|
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 pending_map_t (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 | O_BINARY,
|
|
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);
|
|
}
|
|
if (note_module_cmi_yes || state->inform_cmi_p)
|
|
inform (state->loc, "writing CMI %qs", path);
|
|
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 ())
|
|
{
|
|
/* Some OS's do not replace NEWNAME if it already
|
|
exists. This'll have a race condition in erroneous
|
|
concurrent builds. */
|
|
unlink (path);
|
|
if (rename (tmp_name, path))
|
|
{
|
|
dump () && dump ("Rename ('%s','%s') errno=%u", errno);
|
|
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. */
|
|
vec_free (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;
|
|
|
|
case OPT_flang_info_module_cmi_:
|
|
vec_safe_push (note_cmis, 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"
|