/* C++ modules. Experimental! Copyright (C) 2017-2022 Free Software Foundation, Inc. Written by Nathan Sidwell 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 . */ /* Comments in this file have a non-negligible chance of being wrong or at least inaccurate. Due to (a) my misunderstanding, (b) ambiguities that I have interpretted differently to original intent (c) changes in the specification, (d) my poor wording, (e) source changes. */ /* (Incomplete) Design Notes A hash table contains all module names. Imported modules are present in a modules array, which by construction places an import's dependencies before the import itself. The single exception is the current TU, which always occupies slot zero (even when it is not a module). Imported decls occupy an entity_ary, an array of binding_slots, indexed by importing module and index within that module. A flat index is used, as each module reserves a contiguous range of indices. Initially each slot indicates the CMI section containing the streamed decl. When the decl is imported it will point to the decl itself. Additionally each imported decl is mapped in the entity_map via its DECL_UID to the flat index in the entity_ary. Thus we can locate the index for any imported decl by using this map and then de-flattening the index via a binary seach of the module vector. Cross-module references are by (remapped) module number and module-local index. Each importable DECL contains several flags. The simple set are DECL_MODULE_EXPORT_P, DECL_MODULE_PURVIEW_P, DECL_MODULE_ATTACH_P and DECL_MODULE_IMPORT_P. The first indicates whether it is exported, the second whether it is in module or header-unit purview. The third indicates it is attached to the named module in whose purview it resides and the fourth indicates whether it was an import into this TU or not. DECL_MODULE_ATTACH_P will be false for all decls in a header-unit, and for those in a named module inside a linkage declaration. The more detailed flags are DECL_MODULE_PARTITION_P, DECL_MODULE_ENTITY_P. The first is set in a primary interface unit on decls that were read from module partitions (these will have DECL_MODULE_IMPORT_P set too). Such decls will be streamed out to the primary's CMI. DECL_MODULE_ENTITY_P is set when an entity is imported, even if it matched a non-imported entity. Such a decl will not have DECL_MODULE_IMPORT_P set, even though it has an entry in the entity map and array. Header units are module-like. For namespace-scope lookup, the decls for a particular module are held located in a sparse array hanging off the binding of the name. This is partitioned into two: a few fixed slots at the start followed by the sparse slots afterwards. By construction we only need to append new slots to the end -- there is never a need to insert in the middle. The fixed slots are MODULE_SLOT_CURRENT for the current TU (regardless of whether it is a module or not), MODULE_SLOT_GLOBAL and MODULE_SLOT_PARTITION. These latter two slots are used for merging entities across the global module and module partitions respectively. MODULE_SLOT_PARTITION is only present in a module. Neither of those two slots is searched during name lookup -- they are internal use only. This vector is created lazily once we require it, if there is only a declaration from the current TU, a regular binding is present. It is converted on demand. OPTIMIZATION: Outside of the current TU, we only need ADL to work. We could optimize regular lookup for the current TU by glomming all the visible decls on its slot. Perhaps wait until design is a little more settled though. There is only one instance of each extern-linkage namespace. It appears in every module slot that makes it visible. It also appears in MODULE_SLOT_GLOBAL. (It is an ODR violation if they collide with some other global module entity.) We also have an optimization that shares the slot for adjacent modules that declare the same such namespace. A module interface compilation produces a Compiled Module Interface (CMI). The format used is Encapsulated Lazy Records Of Numbered Declarations, which is essentially ELF's section encapsulation. (As all good nerds are aware, Elrond is half Elf.) Some sections are named, and contain information about the module as a whole (indices etc), and other sections are referenced by number. Although I don't defend against actively hostile CMIs, there is some checksumming involved to verify data integrity. When dumping out an interface, we generate a graph of all the independently-redeclarable DECLS that are needed, and the decls they reference. From that we determine the strongly connected components (SCC) within this TU. Each SCC is dumped to a separate numbered section of the CMI. We generate a binding table section, mapping each namespace&name to a defining section. This allows lazy loading. Lazy loading employs mmap to map a read-only image of the CMI. It thus only occupies address space and is paged in on demand, backed by the CMI file itself. If mmap is unavailable, regular FILEIO is used. Also, there's a bespoke ELF reader/writer here, which implements just the section table and sections (including string sections) of a 32-bit ELF in host byte-order. You can of course inspect it with readelf. I figured 32-bit is sufficient, for a single module. I detect running out of section numbers, but do not implement the ELF overflow mechanism. At least you'll get an error if that happens. We do not separate declarations and definitions. My guess is that if you refer to the declaration, you'll also need the definition (template body, inline function, class definition etc). But this does mean we can get larger SCCs than if we separated them. It is unclear whether this is a win or not. Notice that we embed section indices into the contents of other sections. Thus random manipulation of the CMI file by ELF tools may well break it. The kosher way would probably be to introduce indirection via section symbols, but that would require defining a relocation type. Notice that lazy loading of one module's decls can cause lazy loading of other decls in the same or another module. Clearly we want to avoid loops. In a correct program there can be no loops in the module dependency graph, and the above-mentioned SCC algorithm places all intra-module circular dependencies in the same SCC. It also orders the SCCs wrt each other, so dependent SCCs come first. As we load dependent modules first, we know there can be no reference to a higher-numbered module, and because we write out dependent SCCs first, likewise for SCCs within the module. This allows us to immediately detect broken references. When loading, we must ensure the rest of the compiler doesn't cause some unconnected load to occur (for instance, instantiate a template). Classes used: dumper - logger data - buffer bytes - data streamer bytes_in : bytes - scalar reader bytes_out : bytes - scalar writer elf - ELROND format elf_in : elf - ELROND reader elf_out : elf - ELROND writer trees_in : bytes_in - tree reader trees_out : bytes_out - tree writer depset - dependency set depset::hash - hash table of depsets depset::tarjan - SCC determinator uidset - set T's related to a UID uidset::hash hash table of uidset 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 (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 *note_includes; /* Modules to note CMI pathames. */ static vec *note_cmis; /* Traits to hash an arbitrary pointer. Entries are not deletable, and removal is a noop (removal needed upon destruction). */ template struct nodel_ptr_hash : pointer_hash, typed_noop_remove { /* 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, int> ptr_int_traits; typedef hash_map 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 (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 (§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; char *grow (char *, unsigned needed) final override; #if MAPPED_WRITING using allocator::shrink; void shrink (char *) final override; #endif public: unsigned get_section_limit () const { return sectab.pos / sizeof (section); } protected: unsigned add (unsigned type, unsigned name = 0, unsigned off = 0, unsigned size = 0, unsigned flags = SHF_NONE); unsigned write (const data &); #if MAPPED_WRITING unsigned write (const bytes_out &); #endif public: /* IDENTIFIER to strtab offset. */ unsigned name (tree ident); /* String literal to strtab offset. */ unsigned name (const char *n); /* Qualified name of DECL to strtab offset. */ unsigned qualified_name (tree decl, bool is_defn); private: unsigned strtab_write (const char *s, unsigned l); void strtab_write (tree decl, int); public: /* Add a section with contents or strings. */ unsigned add (const bytes_out &, bool string_p, unsigned name); public: /* Begin and end writing. */ bool begin (); bool end (); }; /* Begin reading section NAME (of type PROGBITS) from SOURCE. Data always checked for CRC. */ bool bytes_in::begin (location_t loc, elf_in *source, const char *name) { unsigned snum = source->find (name); return begin (loc, source, snum, name); } /* Begin reading section numbered SNUM with NAME (may be NULL). */ bool bytes_in::begin (location_t loc, elf_in *source, unsigned snum, const char *name) { if (!source->read (this, source->find (snum)) || !size || !check_crc ()) { source->set_error (elf::E_BAD_DATA); source->shrink (*this); if (name) error_at (loc, "section %qs is missing or corrupted", name); else error_at (loc, "section #%u is missing or corrupted", snum); return false; } pos = 4; return true; } /* Finish reading a section. */ bool bytes_in::end (elf_in *src) { if (more_p ()) set_overrun (); if (overrun) src->set_error (); src->shrink (*this); return !overrun; } /* Begin writing buffer. */ void bytes_out::begin (bool need_crc) { if (need_crc) pos = 4; memory->grow (*this, 0, false); } /* Finish writing buffer. Stream out to SINK as named section NAME. Return section number or 0 on failure. If CRC_PTR is true, crc the data. Otherwise it is a string section. */ unsigned bytes_out::end (elf_out *sink, unsigned name, unsigned *crc_ptr) { lengths[3] += pos; spans[3]++; set_crc (crc_ptr); unsigned sec_num = sink->add (*this, !crc_ptr, name); memory->shrink (*this); return sec_num; } /* Close and open the file, without destroying it. */ void elf_in::freeze () { gcc_checking_assert (!is_frozen ()); #if MAPPED_READING if (munmap (hdr.buffer, hdr.pos) < 0) set_error (errno); #endif if (close (fd) < 0) set_error (errno); fd = -1; } bool elf_in::defrost (const char *name) { gcc_checking_assert (is_frozen ()); struct stat stat; fd = open (name, O_RDONLY | O_CLOEXEC | O_BINARY); if (fd < 0 || fstat (fd, &stat) < 0) set_error (errno); else { bool ok = hdr.pos == unsigned (stat.st_size); #ifndef HOST_LACKS_INODE_NUMBERS if (device != stat.st_dev || inode != stat.st_ino) ok = false; #endif if (!ok) set_error (EMFILE); #if MAPPED_READING if (ok) { char *mapping = reinterpret_cast (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 (§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 (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 (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
(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
(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
(sectab.buffer)->link = strndx; strndx = SHN_XINDEX; } unsigned shnum = sectab.pos / sizeof (section); if (shnum >= SHN_LORESERVE) { reinterpret_cast
(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
(hdr.buffer); h->ident.magic[0] = 0x7f; h->ident.magic[1] = 'E'; /* Elrond */ h->ident.magic[2] = 'L'; /* is an */ h->ident.magic[3] = 'F'; /* elf. */ h->ident.klass = MY_CLASS; h->ident.data = MY_ENDIAN; h->ident.version = EV_CURRENT; h->ident.osabi = OSABI_NONE; h->type = ET_NONE; h->machine = EM_NONE; h->version = EV_CURRENT; h->shoff = shoff; h->ehsize = sizeof (header); h->shentsize = sizeof (section); h->shnum = shnum; h->shstrndx = strndx; pos = 0; write (hdr); } #if MAPPED_WRITING remove_mapping (); if (ftruncate (fd, length)) set_error (errno); #endif } data::simple_memory.shrink (sectab); data::simple_memory.shrink (strtab); return parent::end (); } /********************************************************************/ /* A dependency set. This is used during stream out to determine the connectivity of the graph. Every namespace-scope declaration that needs writing has a depset. The depset is filled with the (depsets of) declarations within this module that it references. For a declaration that'll generally be named types. For definitions it'll also be declarations in the body. From that we can convert the graph to a DAG, via determining the Strongly Connected Clusters. Each cluster is streamed independently, and thus we achieve lazy loading. Other decls that get a depset are namespaces themselves and unnameable declarations. */ class depset { private: tree entity; /* Entity, or containing namespace. */ uintptr_t discriminator; /* Flags or identifier. */ public: /* The kinds of entity the depset could describe. The ordering is significant, see entity_kind_name. */ enum entity_kind { EK_DECL, /* A decl. */ EK_SPECIALIZATION, /* A specialization. */ EK_PARTIAL, /* A partial specialization. */ EK_USING, /* A using declaration (at namespace scope). */ EK_NAMESPACE, /* A namespace. */ EK_REDIRECT, /* Redirect to a template_decl. */ EK_EXPLICIT_HWM, EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded. */ EK_FOR_BINDING, /* A decl being inserted for a binding. */ EK_INNER_DECL, /* A decl defined outside of its imported context. */ EK_DIRECT_HWM = EK_PARTIAL + 1, EK_BITS = 3 /* Only need to encode below EK_EXPLICIT_HWM. */ }; private: /* Placement of bit fields in discriminator. */ enum disc_bits { DB_ZERO_BIT, /* Set to disambiguate identifier from flags */ DB_SPECIAL_BIT, /* First dep slot is special. */ DB_KIND_BIT, /* Kind of the entity. */ DB_KIND_BITS = EK_BITS, DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS, DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ DB_IS_INTERNAL_BIT, /* It is an (erroneous) internal-linkage entity. */ DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage entity. */ DB_IMPORTED_BIT, /* An imported entity. */ DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ DB_HIDDEN_BIT, /* A hidden binding. */ /* The following bits are not independent, but enumerating them is awkward. */ DB_ALIAS_TMPL_INST_BIT, /* An alias template instantiation. */ DB_ALIAS_SPEC_BIT, /* Specialization of an alias template (in both spec tables). */ DB_TYPE_SPEC_BIT, /* Specialization in the type table. */ DB_FRIEND_SPEC_BIT, /* An instantiated template friend. */ }; public: /* The first slot is special for EK_SPECIALIZATIONS it is a spec_entry pointer. It is not relevant for the SCC determination. */ vec 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 (name); } private: template void set_flag_bit () { gcc_checking_assert (I < 2 || !is_binding ()); discriminator |= 1u << I; } template void clear_flag_bit () { gcc_checking_assert (I < 2 || !is_binding ()); discriminator &= ~(1u << I); } template 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 (); } 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 (); } 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 (); } public: bool is_internal () const { return get_flag_bit (); } bool refs_internal () const { return get_flag_bit (); } bool is_import () const { return get_flag_bit (); } bool is_unreached () const { return get_flag_bit (); } bool is_alias_tmpl_inst () const { return get_flag_bit (); } bool is_alias () const { return get_flag_bit (); } bool is_hidden () const { return get_flag_bit (); } bool is_type_spec () const { return get_flag_bit (); } bool is_friend_spec () const { return get_flag_bit (); } public: /* We set these bit outside of depset. */ void set_hidden_binding () { set_flag_bit (); } void clear_hidden_binding () { clear_flag_bit (); } public: bool is_special () const { return get_flag_bit (); } void set_special () { set_flag_bit (); } public: tree get_entity () const { return entity; } tree get_name () const { gcc_checking_assert (is_binding ()); return reinterpret_cast (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 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::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 (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::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 { typedef traits::compare_type key_t; typedef hash_table parent; public: vec 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 *); void add_class_entities (vec *); public: void find_dependencies (module_state *); bool finalize_dependencies (); vec connect (); }; public: struct tarjan { vec result; vec stack; unsigned index; tarjan (unsigned size) : index (0) { result.create (size); stack.create (50); } ~tarjan () { gcc_assert (!stack.length ()); stack.release (); } public: void connect (depset *); }; }; inline depset::depset (tree entity) :entity (entity), discriminator (0), cluster (0), section (0) { deps.create (0); } inline depset::~depset () { deps.release (); } const char * depset::entity_kind_name () const { /* Same order as entity_kind. */ static const char *const names[] = {"decl", "specialization", "partial", "using", "namespace", "redirect", "binding"}; entity_kind kind = get_entity_kind (); gcc_checking_assert (kind < ARRAY_SIZE (names)); return names[kind]; } /* Create a depset for a namespace binding NS::NAME. */ depset *depset::make_binding (tree ns, tree name) { depset *binding = new depset (ns); binding->discriminator = reinterpret_cast (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 { 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_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 *post_load_decls; /* Some entities are keyed to another entitity for ODR purposes. For example, at namespace scope, 'inline auto var = []{};', that lambda is keyed to 'var', and follows its ODRness. */ typedef hash_map> keyed_map_t; static keyed_map_t *keyed_table; /********************************************************************/ /* Tree streaming. The tree streaming is very specific to the tree structures themselves. A tag indicates the kind of tree being streamed. -ve tags indicate backreferences to already-streamed trees. Backreferences are auto-numbered. */ /* Tree tags. */ enum tree_tag { tt_null, /* NULL_TREE. */ tt_fixed, /* Fixed vector index. */ tt_node, /* By-value node. */ tt_decl, /* By-value mergeable decl. */ tt_tpl_parm, /* Template parm. */ /* The ordering of the following 4 is relied upon in trees_out::tree_node. */ tt_id, /* Identifier node. */ tt_conv_id, /* Conversion operator name. */ tt_anon_id, /* Anonymous name. */ tt_lambda_id, /* Lambda name. */ tt_typedef_type, /* A (possibly implicit) typedefed type. */ tt_derived_type, /* A type derived from another type. */ tt_variant_type, /* A variant of another type. */ tt_tinfo_var, /* Typeinfo object. */ tt_tinfo_typedef, /* Typeinfo typedef. */ tt_ptrmem_type, /* Pointer to member type. */ tt_nttp_var, /* NTTP_OBJECT VAR_DECL. */ tt_parm, /* Function parameter or result. */ tt_enum_value, /* An enum value. */ tt_enum_decl, /* An enum decl. */ tt_data_member, /* Data member/using-decl. */ tt_binfo, /* A BINFO. */ tt_vtable, /* A vtable. */ tt_thunk, /* A thunk. */ tt_clone_ref, tt_entity, /* A extra-cluster entity. */ tt_template, /* The TEMPLATE_RESULT of a template. */ }; enum walk_kind { WK_none, /* No walk to do (a back- or fixed-ref happened). */ WK_normal, /* Normal walk (by-name if possible). */ WK_value, /* By-value walk. */ }; enum merge_kind { MK_unique, /* Known unique. */ MK_named, /* Found by CTX, NAME + maybe_arg types etc. */ MK_field, /* Found by CTX and index on TYPE_FIELDS */ MK_vtable, /* Found by CTX and index on TYPE_VTABLES */ MK_as_base, /* Found by CTX. */ MK_partial, MK_enum, /* Found by CTX, & 1stMemberNAME. */ MK_keyed, /* Found by key & index. */ MK_friend_spec, /* Like named, but has a tmpl & args too. */ MK_local_friend, /* Found by CTX, index. */ MK_indirect_lwm = MK_enum, /* Template specialization kinds below. These are all found via primary template and specialization args. */ MK_template_mask = 0x10, /* A template specialization. */ MK_tmpl_decl_mask = 0x4, /* In decl table. */ MK_tmpl_alias_mask = 0x2, /* Also in type table */ MK_tmpl_tmpl_mask = 0x1, /* We want TEMPLATE_DECL. */ MK_type_spec = MK_template_mask, MK_decl_spec = MK_template_mask | MK_tmpl_decl_mask, MK_alias_spec = MK_decl_spec | MK_tmpl_alias_mask, MK_hwm = 0x20 }; /* This is more than a debugging array. NULLs are used to determine an invalid merge_kind number. */ static char const *const merge_kind_name[MK_hwm] = { "unique", "named", "field", "vtable", /* 0...3 */ "asbase", "partial", "enum", "attached", /* 4...7 */ "friend spec", "local friend", NULL, NULL, /* 8...11 */ NULL, NULL, NULL, NULL, "type spec", "type tmpl spec", /* 16,17 type (template). */ NULL, NULL, "decl spec", "decl tmpl spec", /* 20,21 decl (template). */ "alias spec", "alias tmpl spec", /* 22,23 alias (template). */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; /* Mergeable entity location data. */ struct merge_key { cp_ref_qualifier ref_q : 2; unsigned index; tree ret; /* Return type, if appropriate. */ tree args; /* Arg types, if appropriate. */ tree constraints; /* Constraints. */ merge_key () :ref_q (REF_QUAL_NONE), index (0), ret (NULL_TREE), args (NULL_TREE), constraints (NULL_TREE) { } }; /* Hashmap of merged duplicates. Usually decls, but can contain BINFOs. */ typedef hash_map,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 back_refs; /* Back references. */ duplicate_hash_map *duplicates; /* Map from existings to duplicate. */ vec 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 *vec_chained_decls (); vec *tree_vec (); /* vec of tree. */ vec *tree_pair_vec (); /* vec of tree_pair. */ tree tree_list (bool has_purpose); public: /* Read a tree node. */ tree tree_node (bool is_use = false); private: bool install_entity (tree decl); tree tpl_parms (unsigned &tpl_levels); bool tpl_parms_fini (tree decl, unsigned tpl_levels); bool tpl_header (tree decl, unsigned *tpl_levels); int fn_parms_init (tree); void fn_parms_fini (int tag, tree fn, tree existing, bool has_defn); unsigned add_indirect_tpl_parms (tree); public: bool add_indirects (tree); public: /* Serialize various definitions. */ bool read_definition (tree decl); private: bool is_matching_decl (tree existing, tree decl, bool is_typedef); static bool install_implicit_member (tree decl); bool read_function_def (tree decl, tree maybe_template); bool read_var_def (tree decl, tree maybe_template); bool read_class_def (tree decl, tree maybe_template); bool read_enum_def (tree decl, tree maybe_template); public: tree decl_container (); tree key_mergeable (int tag, merge_kind, tree decl, tree inner, tree type, tree container, bool is_attached); unsigned binfo_mergeable (tree *); private: uintptr_t *find_duplicate (tree existing); void register_duplicate (tree decl, tree existing); /* Mark as an already diagnosed bad duplicate. */ void 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 (*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 *); void tree_pair_vec (vec *); 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 range_t; /* A range of locations. */ typedef std::pair 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 *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; /* Information about ordinary locations we stream out. */ struct ord_loc_info { const line_map_ordinary *src; // line map we're based on unsigned offset; // offset to this line unsigned span; // number of locs we span unsigned remap; // serialization static int compare (const void *a_, const void *b_) { auto *a = static_cast (a_); auto *b = static_cast (b_); if (a->src != b->src) return a->src < b->src ? -1 : +1; // Ensure no overlap gcc_checking_assert (a->offset + a->span <= b->offset || b->offset + b->span <= a->offset); gcc_checking_assert (a->offset != b->offset); return a->offset < b->offset ? -1 : +1; } }; struct ord_loc_traits { typedef ord_loc_info value_type; typedef value_type compare_type; static const bool empty_zero_p = false; static hashval_t hash (const value_type &v) { auto h = pointer_hash::hash (v.src); return iterative_hash_hashval_t (v.offset, h); } static bool equal (const value_type &v, const compare_type p) { return v.src == p.src && v.offset == p.offset; } static void mark_empty (value_type &v) { v.src = nullptr; } static bool is_empty (value_type &v) { return !v.src; } static bool is_deleted (value_type &) { return false; } static void mark_deleted (value_type &) { gcc_unreachable (); } static void remove (value_type &) {} }; /* Table keyed by ord_loc_info, used for noting. */ static hash_table *ord_loc_table; /* Sorted vector, used for writing. */ static vec *ord_loc_remap; /* Information about macro locations we stream out. */ struct macro_loc_info { const line_map_macro *src; // original expansion unsigned remap; // serialization static int compare (const void *a_, const void *b_) { auto *a = static_cast (a_); auto *b = static_cast (b_); gcc_checking_assert (MAP_START_LOCATION (a->src) != MAP_START_LOCATION (b->src)); if (MAP_START_LOCATION (a->src) < MAP_START_LOCATION (b->src)) return -1; else return +1; } }; struct macro_loc_traits { typedef macro_loc_info value_type; typedef const line_map_macro *compare_type; static const bool empty_zero_p = false; static hashval_t hash (compare_type p) { return pointer_hash::hash (p); } static hashval_t hash (const value_type &v) { return hash (v.src); } static bool equal (const value_type &v, const compare_type p) { return v.src == p; } static void mark_empty (value_type &v) { v.src = nullptr; } static bool is_empty (value_type &v) { return !v.src; } static bool is_deleted (value_type &) { return false; } static void mark_deleted (value_type &) { gcc_unreachable (); } static void remove (value_type &) {} }; /* Table keyed by line_map_macro, used for noting. */ static hash_table *macro_loc_table; /* Sorted vector, used for writing. */ static vec *macro_loc_remap; /* Indirection to allow bsearching imports by ordinary location. */ static vec *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 * 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); } /* Flags 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; // [lwm,num) /* 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 active_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_begin (elf_out *to, cpp_reader *, module_state_config &, unsigned &crc); void write_end (elf_out *to, cpp_reader *, module_state_config &, unsigned &crc); 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); 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 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 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 depsets, depset::hash &, unsigned *crc_ptr); bool read_pendings (unsigned count); private: void write_entities (elf_out *to, vec depsets, unsigned count, unsigned *crc_ptr); bool read_entities (unsigned count, unsigned lwm, unsigned hwm); private: void write_init_maps (); range_t write_prepare_maps (module_state_config *, bool); bool read_prepare_maps (const module_state_config *); void write_ordinary_maps (elf_out *to, range_t &, bool, unsigned *crc_ptr); bool read_ordinary_maps (unsigned, unsigned); void write_macro_maps (elf_out *to, range_t &, unsigned *crc_ptr); bool read_macro_maps (unsigned); private: void write_define (bytes_out &, const cpp_macro *); cpp_macro *read_define (bytes_in &, cpp_reader *) const; vec *prepare_macros (cpp_reader *); unsigned write_macros (elf_out *to, vec *, 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 bool note_location (location_t); 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 { typedef std::pair 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; active_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::hash (reinterpret_cast (reinterpret_cast (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::hash (reinterpret_cast (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 (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 *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; /* Global trees. */ static const std::pair global_tree_arys[] = { std::pair (sizetype_tab, stk_type_kind_last), std::pair (integer_types, itk_none), std::pair (global_trees, TI_MODULE_HWM), std::pair (c_global_trees, CTI_MODULE_HWM), std::pair (cp_global_trees, CPTI_MODULE_HWM), std::pair (NULL, 0) }; static GTY(()) vec *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 *modules; /* Hash of module state, findable by {name, parent}. */ static GTY(()) hash_table *modules_hash; /* Map of imported entities. We map DECL_UID to index of entity vector. */ typedef hash_map, 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 *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 *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 *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 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 { 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_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 (CLASS_TYPE_P (frnd) && 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 ((*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 * trees_in::vec_chained_decls () { vec *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 *v) { unsigned len = vec_safe_length (v); if (streaming_p ()) u (len); for (unsigned ix = 0; ix != len; ix++) tree_node ((*v)[ix]); } vec * trees_in::tree_vec () { vec *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 *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 * trees_in::tree_pair_vec () { vec *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, (so we know which decls to write). But when we import them we do not want to mark them as in module purview. */ WB (lang->u.base.module_purview_p && !header_module_p ()); WB (lang->u.base.module_attach_p); if (VAR_OR_FUNCTION_DECL_P (t)) WB (lang->u.base.module_keyed_decls_p); switch (lang->u.base.selector) { default: gcc_unreachable (); case lds_fn: /* lang_decl_fn. */ WB (lang->u.fn.global_ctor_p); WB (lang->u.fn.global_dtor_p); WB (lang->u.fn.static_function); WB (lang->u.fn.pure_virtual); WB (lang->u.fn.defaulted_p); WB (lang->u.fn.has_in_charge_parm_p); WB (lang->u.fn.has_vtt_parm_p); /* There shouldn't be a pending inline at this point. */ gcc_assert (!lang->u.fn.pending_inline_p); WB (lang->u.fn.nonconverting); WB (lang->u.fn.thunk_p); WB (lang->u.fn.this_thunk_p); /* Do not stream lang->u.hidden_friend_p, it is a property of the TU. */ WB (lang->u.fn.omp_declare_reduction_p); WB (lang->u.fn.has_dependent_explicit_spec_p); WB (lang->u.fn.immediate_fn_p); WB (lang->u.fn.maybe_deleted); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ /* No bools. */ goto lds_min; case lds_min: /* lang_decl_min. */ lds_min: /* No bools. */ break; case lds_ns: /* lang_decl_ns. */ /* No bools. */ break; case lds_parm: /* lang_decl_parm. */ /* No bools. */ break; } #undef WB } bool trees_in::lang_decl_bools (tree t) { #define RB(X) ((X) = b ()) struct lang_decl *lang = DECL_LANG_SPECIFIC (t); lang->u.base.language = b () ? lang_cplusplus : lang_c; unsigned v; v = b () << 0; v |= b () << 1; lang->u.base.use_template = v; /* lang->u.base.not_really_extern is not streamed. */ RB (lang->u.base.initialized_in_class); RB (lang->u.base.threadprivate_or_deleted_p); /* lang->u.base.anticipated_p is not streamed. */ RB (lang->u.base.friend_or_tls); RB (lang->u.base.unknown_bound_p); /* lang->u.base.odr_used is not streamed. */ RB (lang->u.base.concept_p); RB (lang->u.base.var_declared_inline_p); RB (lang->u.base.dependent_init_p); RB (lang->u.base.module_purview_p); RB (lang->u.base.module_attach_p); if (VAR_OR_FUNCTION_DECL_P (t)) RB (lang->u.base.module_keyed_decls_p); switch (lang->u.base.selector) { default: gcc_unreachable (); case lds_fn: /* lang_decl_fn. */ RB (lang->u.fn.global_ctor_p); RB (lang->u.fn.global_dtor_p); RB (lang->u.fn.static_function); RB (lang->u.fn.pure_virtual); RB (lang->u.fn.defaulted_p); RB (lang->u.fn.has_in_charge_parm_p); RB (lang->u.fn.has_vtt_parm_p); RB (lang->u.fn.nonconverting); RB (lang->u.fn.thunk_p); RB (lang->u.fn.this_thunk_p); /* lang->u.fn.hidden_friend_p is not streamed. */ RB (lang->u.fn.omp_declare_reduction_p); RB (lang->u.fn.has_dependent_explicit_spec_p); RB (lang->u.fn.immediate_fn_p); RB (lang->u.fn.maybe_deleted); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ /* No bools. */ goto lds_min; case lds_min: /* lang_decl_min. */ lds_min: /* No bools. */ break; case lds_ns: /* lang_decl_ns. */ /* No bools. */ break; case lds_parm: /* lang_decl_parm. */ /* No bools. */ break; } #undef RB return !get_overrun (); } void trees_out::lang_type_bools (tree t) { #define WB(X) (b (X)) const struct lang_type *lang = TYPE_LANG_SPECIFIC (t); WB (lang->has_type_conversion); WB (lang->has_copy_ctor); WB (lang->has_default_ctor); WB (lang->const_needs_init); WB (lang->ref_needs_init); WB (lang->has_const_copy_assign); WB ((lang->use_template >> 0) & 1); WB ((lang->use_template >> 1) & 1); WB (lang->has_mutable); WB (lang->com_interface); WB (lang->non_pod_class); WB (lang->nearly_empty_p); WB (lang->user_align); WB (lang->has_copy_assign); WB (lang->has_new); WB (lang->has_array_new); WB ((lang->gets_delete >> 0) & 1); WB ((lang->gets_delete >> 1) & 1); // Interfaceness is recalculated upon reading. May have to revisit? // How do dllexport and dllimport interact across a module? // lang->interface_only // lang->interface_unknown WB (lang->contains_empty_class_p); WB (lang->anon_aggr); WB (lang->non_zero_init); WB (lang->empty_p); WB (lang->vec_new_uses_cookie); WB (lang->declared_class); WB (lang->diamond_shaped); WB (lang->repeated_base); gcc_assert (!lang->being_defined); // lang->debug_requested WB (lang->fields_readonly); WB (lang->ptrmemfunc_flag); WB (lang->lazy_default_ctor); WB (lang->lazy_copy_ctor); WB (lang->lazy_copy_assign); WB (lang->lazy_destructor); WB (lang->has_const_copy_ctor); WB (lang->has_complex_copy_ctor); WB (lang->has_complex_copy_assign); WB (lang->non_aggregate); WB (lang->has_complex_dflt); WB (lang->has_list_ctor); WB (lang->non_std_layout); WB (lang->is_literal); WB (lang->lazy_move_ctor); WB (lang->lazy_move_assign); WB (lang->has_complex_move_ctor); WB (lang->has_complex_move_assign); WB (lang->has_constexpr_ctor); WB (lang->unique_obj_representations); WB (lang->unique_obj_representations_set); #undef WB } bool trees_in::lang_type_bools (tree t) { #define RB(X) ((X) = b ()) struct lang_type *lang = TYPE_LANG_SPECIFIC (t); RB (lang->has_type_conversion); RB (lang->has_copy_ctor); RB (lang->has_default_ctor); RB (lang->const_needs_init); RB (lang->ref_needs_init); RB (lang->has_const_copy_assign); unsigned v; v = b () << 0; v |= b () << 1; lang->use_template = v; RB (lang->has_mutable); RB (lang->com_interface); RB (lang->non_pod_class); RB (lang->nearly_empty_p); RB (lang->user_align); RB (lang->has_copy_assign); RB (lang->has_new); RB (lang->has_array_new); v = b () << 0; v |= b () << 1; lang->gets_delete = v; // lang->interface_only // lang->interface_unknown lang->interface_unknown = true; // Redetermine interface RB (lang->contains_empty_class_p); RB (lang->anon_aggr); RB (lang->non_zero_init); RB (lang->empty_p); RB (lang->vec_new_uses_cookie); RB (lang->declared_class); RB (lang->diamond_shaped); RB (lang->repeated_base); gcc_assert (!lang->being_defined); gcc_assert (!lang->debug_requested); RB (lang->fields_readonly); RB (lang->ptrmemfunc_flag); RB (lang->lazy_default_ctor); RB (lang->lazy_copy_ctor); RB (lang->lazy_copy_assign); RB (lang->lazy_destructor); RB (lang->has_const_copy_ctor); RB (lang->has_complex_copy_ctor); RB (lang->has_complex_copy_assign); RB (lang->non_aggregate); RB (lang->has_complex_dflt); RB (lang->has_list_ctor); RB (lang->non_std_layout); RB (lang->is_literal); RB (lang->lazy_move_ctor); RB (lang->lazy_move_assign); RB (lang->has_complex_move_ctor); RB (lang->has_complex_move_assign); RB (lang->has_constexpr_ctor); RB (lang->unique_obj_representations); RB (lang->unique_obj_representations_set); #undef RB return !get_overrun (); } /* Read & write the core values and pointers. */ void trees_out::core_vals (tree t) { #define WU(X) (u (X)) #define WT(X) (tree_node (X)) tree_code code = TREE_CODE (t); /* First by shape of the tree. */ if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL)) { /* Write this early, for better log information. */ WT (t->decl_minimal.name); if (!DECL_TEMPLATE_PARM_P (t)) WT (t->decl_minimal.context); 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))) memcpy (TREE_REAL_CST_PTR (t), 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 its 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_attached = false; tree not_tmpl = STRIP_TEMPLATE (o); if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_ATTACH_P (not_tmpl)) is_attached = true; b (is_attached); } 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 (dep->deps[0]); if (streaming_p ()) u (get_mergeable_specialization_flags (entry->tmpl, decl)); tree_node (entry->tmpl); tree_node (entry->args); } else { tree ti = get_template_info (inner); tree_node (TI_TEMPLATE (ti)); tree_node (TI_ARGS (ti)); } } 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_KEYED_DECLS_P (inner) && !is_key_order ()) { /* Stream the keyed entities. */ auto *attach_vec = keyed_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_attached = 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_attached = 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_attached); 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_KEYED_DECLS_P (inner)) { /* Read and maybe install the attached entities. */ bool existed; auto &set = keyed_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); /* Add to specialization tables now that constraints etc are added. */ if (mk == MK_partial) { bool is_type = TREE_CODE (inner) == TYPE_DECL; spec.spec = is_type ? type : inner; add_mergeable_specialization (!is_type, false, &spec, decl, spec_flags); } else if (mk & MK_template_mask) { bool is_type = !(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; } if (DECL_NTTP_OBJECT_P (decl)) { /* A NTTP parm object. */ if (streaming_p ()) i (tt_nttp_var); tree_node (tparm_object_argument (decl)); tree_node (DECL_NAME (decl)); int tag = insert (decl); if (streaming_p ()) dump (dumper::TREE) && dump ("Wrote nttp object:%d %N", tag, DECL_NAME (decl)); 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 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 TRAIT_TYPE: tree_node (TRAIT_TYPE_KIND_RAW (type)); tree_node (TRAIT_TYPE_TYPE1 (type)); tree_node (TRAIT_TYPE_TYPE2 (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)); tree_node (PACK_EXPANSION_EXTRA_ARGS (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 (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 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 TRAIT_TYPE: { tree kind = tree_node (); tree type1 = tree_node (); tree type2 = tree_node (); if (!get_overrun ()) { res = cxx_make_type (TRAIT_TYPE); TRAIT_TYPE_KIND_RAW (res) = kind; TRAIT_TYPE_TYPE1 (res) = type1; TRAIT_TYPE_TYPE2 (res) = type2; SET_TYPE_STRUCTURAL_EQUALITY (res); } } break; case TYPE_ARGUMENT_PACK: if (!get_overrun ()) { tree pack = cxx_make_type (TYPE_ARGUMENT_PACK); ARGUMENT_PACK_ARGS (pack) = res; res = pack; } break; case TYPE_PACK_EXPANSION: { bool local = u (); tree param_packs = tree_node (); tree extra_args = tree_node (); if (!get_overrun ()) { tree expn = cxx_make_type (TYPE_PACK_EXPANSION); SET_TYPE_STRUCTURAL_EQUALITY (expn); PACK_EXPANSION_PATTERN (expn) = res; PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs; PACK_EXPANSION_EXTRA_ARGS (expn) = extra_args; 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 (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_nttp_var: /* An NTTP object. */ { tree init = tree_node (); tree name = tree_node (); if (!get_overrun ()) { res = get_template_parm_object (init, name); int tag = insert (res); dump (dumper::TREE) && dump ("Created nttp object:%d %N", tag, name); } } 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_KEYED_DECLS_P (scope)) { mk = MK_keyed; 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 (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 (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_keyed: { 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 = keyed_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 keyed-to decl as the 'name'. */ name = scope; key.index = ix; } break; case MK_partial: { tree ti = get_template_info (inner); key.constraints = get_constraints (inner); key.ret = TI_TEMPLATE (ti); key.args = TI_ARGS (ti); } 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 its 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_attached) { 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); tree ti = get_template_info (tmpl); if (template_args_equal (key.args, TI_ARGS (ti)) && 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_keyed) { if (DECL_LANG_SPECIFIC (name) && VAR_OR_FUNCTION_DECL_P (name) && DECL_MODULE_KEYED_DECLS_P (name)) if (auto *set = keyed_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_attached && !(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_attached, &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_attached) 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_attached && !(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)) 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 (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 (*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 its 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 *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 *vbase_vec = NULL; vec *member_vec = NULL; vec *pure_virts = NULL; vec *vcall_indices = NULL; tree key_method = NULL_TREE; tree lambda = NULL_TREE; /* Read the fields. */ vec *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 *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 (CLASS_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) || (VAR_P (decl) && DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (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 class silly; template 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 = get_template_info (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 (); *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 (); 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 (); } } 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 (); 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 (); } } } 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 (); 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 (); 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 (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 (TREE_CODE (decl) == VAR_DECL && DECL_NTTP_OBJECT_P (decl)) /* Ignore NTTP objects. */ 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::iterator end (DECL_NAMESPACE_BINDINGS (ns)->end ()); for (hash_table::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 *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 *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 (); } } /* We add the partial & explicit specializations, and the explicit instantiations. */ static void specialization_add (bool decl_p, spec_entry *entry, void *data_) { vec *data = reinterpret_cast *> (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 (a_); const spec_entry *eb = *reinterpret_cast (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 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 (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 (); } 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 (entry)); if (!decl_p) dep->set_flag_bit (); } if (needs_reaching) dep->set_flag_bit (); if (is_friend) dep->set_flag_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 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) { module->note_location (DECL_SOURCE_LOCATION (decl)); 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 (); 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 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 auto Foo (const T &arg) -> TPL; 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). 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::hash::connect () { tarjan connector (size ()); vec 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 (); 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 (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 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) { bytes_out readme (to); readme.begin (false); readme.printf ("GNU C++ %s", is_header () ? "header unit" : !is_partition () ? "primary interface" : is_interface () ? "interface partition" : "internal 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 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 loc_range_bits; unsigned active_init; public: module_state_config () :dialect_str (get_dialect ()), num_imports (0), num_partitions (0), num_entities (0), ordinary_locs (0), macro_locs (0), loc_range_bits (0), active_init (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 (); 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 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 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 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 its 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 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.first + 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.first) { pos += half + 1; len = len - (half + 1); } else if (loc >= probe->macro_locs.first + probe->macro_locs.second) len = half; else return probe; } 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; } /* Note that LOC will need writing. This allows us to prune locations that are not needed. */ bool module_state::note_location (location_t loc) { bool added = false; if (!macro_loc_table && !ord_loc_table) ; else if (loc < RESERVED_LOCATION_COUNT) ; else if (IS_ADHOC_LOC (loc)) { location_t locus = get_location_from_adhoc_loc (line_table, loc); note_location (locus); source_range range = get_range_from_loc (line_table, loc); if (range.m_start != locus) note_location (range.m_start); note_location (range.m_finish); } else if (loc >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table)) { if (spans.macro (loc)) { const line_map *map = linemap_lookup (line_table, loc); const line_map_macro *mac_map = linemap_check_macro (map); hashval_t hv = macro_loc_traits::hash (mac_map); macro_loc_info *slot = macro_loc_table->find_slot_with_hash (mac_map, hv, INSERT); if (!slot->src) { slot->src = mac_map; slot->remap = 0; // Expansion locations could themselves be from a // macro, we need to note them all. note_location (mac_map->expansion); gcc_checking_assert (mac_map->n_tokens); location_t tloc = UNKNOWN_LOCATION; for (unsigned ix = mac_map->n_tokens * 2; ix--;) if (mac_map->macro_locations[ix] != tloc) { tloc = mac_map->macro_locations[ix]; note_location (tloc); } added = true; } } } else if (IS_ORDINARY_LOC (loc)) { if (spans.ordinary (loc)) { const line_map *map = linemap_lookup (line_table, loc); const line_map_ordinary *ord_map = linemap_check_ordinary (map); ord_loc_info lkup; lkup.src = ord_map; lkup.span = 1 << ord_map->m_column_and_range_bits; lkup.offset = (loc - MAP_START_LOCATION (ord_map)) & ~(lkup.span - 1); lkup.remap = 0; ord_loc_info *slot = (ord_loc_table->find_slot_with_hash (lkup, ord_loc_traits::hash (lkup), INSERT)); if (!slot->src) { *slot = lkup; added = true; } } } else gcc_unreachable (); return added; } /* 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 ()) { note_location (loc); 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); unsigned discriminator = get_discriminator_from_adhoc_loc (line_table, loc); sec.u (discriminator); } else if (loc >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table)) { const macro_loc_info *info = nullptr; unsigned offset = 0; if (unsigned hwm = macro_loc_remap->length ()) { info = macro_loc_remap->begin (); while (hwm != 1) { unsigned mid = hwm / 2; if (MAP_START_LOCATION (info[mid].src) <= loc) { info += mid; hwm -= mid; } else hwm = mid; } offset = loc - MAP_START_LOCATION (info->src); if (offset > info->src->n_tokens) info = nullptr; } gcc_checking_assert (bool (info) == bool (spans.macro (loc))); if (info) { offset += info->remap; sec.u (LK_MACRO); sec.u (offset); dump (dumper::LOCATION) && dump ("Macro location %u output %u", loc, offset); } else if (const module_state *import = module_for_macro_loc (loc)) { unsigned off = loc - import->macro_locs.first; 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)) { const ord_loc_info *info = nullptr; unsigned offset = 0; if (unsigned hwm = ord_loc_remap->length ()) { info = ord_loc_remap->begin (); while (hwm != 1) { unsigned mid = hwm / 2; if (MAP_START_LOCATION (info[mid].src) + info[mid].offset <= loc) { info += mid; hwm -= mid; } else hwm = mid; } offset = loc - MAP_START_LOCATION (info->src) - info->offset; if (offset > info->span) info = nullptr; } gcc_checking_assert (bool (info) == bool (spans.ordinary (loc))); if (info) { offset += info->remap; sec.u (LK_ORDINARY); sec.u (offset); dump (dumper::LOCATION) && dump ("Ordinary location %u output %u", loc, offset); } 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); unsigned discriminator = sec.u (); if (locus != loc && range.m_start != loc && range.m_finish != loc) locus = get_combined_adhoc_loc (line_table, locus, range, NULL, discriminator); } break; case LK_MACRO: { unsigned off = sec.u (); if (macro_locs.second) { if (off < macro_locs.second) locus = off + macro_locs.first; 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) { if (off < ordinary_locs.second) locus = off + ordinary_locs.first; else sec.set_overrun (); } 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.second) locus = import->loc; else if (off < import->macro_locs.second) locus = off + import->macro_locs.first; else sec.set_overrun (); } else { if (!import->ordinary_locs.second) locus = import->loc; else if (off < import->ordinary_locs.second) locus = import->ordinary_locs.first + off; else sec.set_overrun (); } } } break; } return locus; } /* Allocate hash tables to record needed locations. */ void module_state::write_init_maps () { macro_loc_table = new hash_table (EXPERIMENT (1, 400)); ord_loc_table = new hash_table (EXPERIMENT (1, 400)); } /* Prepare the span adjustments. We prune unneeded locations -- at this point every needed location must have been seen by note_location. */ range_t module_state::write_prepare_maps (module_state_config *cfg, bool has_partitions) { 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); range_t info {0, 0}; // Sort the noted lines. vec_alloc (ord_loc_remap, ord_loc_table->size ()); for (auto iter = ord_loc_table->begin (), end = ord_loc_table->end (); iter != end; ++iter) ord_loc_remap->quick_push (*iter); ord_loc_remap->qsort (&ord_loc_info::compare); // Note included-from maps. bool added = false; const line_map_ordinary *current = nullptr; for (auto iter = ord_loc_remap->begin (), end = ord_loc_remap->end (); iter != end; ++iter) if (iter->src != current) { current = iter->src; for (auto probe = current; auto from = linemap_included_from (probe); probe = linemap_check_ordinary (linemap_lookup (line_table, from))) { if (has_partitions) { // Partition locations need to elide their module map // entry. probe = linemap_check_ordinary (linemap_lookup (line_table, from)); if (MAP_MODULE_P (probe)) from = linemap_included_from (probe); } if (!note_location (from)) break; added = true; } } if (added) { // Reconstruct the line array as we added items to the hash table. vec_free (ord_loc_remap); vec_alloc (ord_loc_remap, ord_loc_table->size ()); for (auto iter = ord_loc_table->begin (), end = ord_loc_table->end (); iter != end; ++iter) ord_loc_remap->quick_push (*iter); ord_loc_remap->qsort (&ord_loc_info::compare); } delete ord_loc_table; ord_loc_table = nullptr; // Merge (sufficiently) adjacent spans, and calculate remapping. constexpr unsigned adjacency = 2; // Allow 2 missing lines. auto begin = ord_loc_remap->begin (), end = ord_loc_remap->end (); auto dst = begin; unsigned offset = 0, range_bits = 0; ord_loc_info *base = nullptr; for (auto iter = begin; iter != end; ++iter) { if (base && iter->src == base->src) { if (base->offset + base->span + ((adjacency << base->src->m_column_and_range_bits) // If there are few c&r bits, allow further separation. | (adjacency << 4)) >= iter->offset) { // Merge. offset -= base->span; base->span = iter->offset + iter->span - base->offset; offset += base->span; continue; } } else if (range_bits < iter->src->m_range_bits) range_bits = iter->src->m_range_bits; offset += ((1u << iter->src->m_range_bits) - 1); offset &= ~((1u << iter->src->m_range_bits) - 1); iter->remap = offset; offset += iter->span; base = dst; *dst++ = *iter; } ord_loc_remap->truncate (dst - begin); info.first = ord_loc_remap->length (); cfg->ordinary_locs = offset; cfg->loc_range_bits = range_bits; dump () && dump ("Ordinary maps:%u locs:%u range_bits:%u", info.first, cfg->ordinary_locs, cfg->loc_range_bits); // Remap the macro locations. vec_alloc (macro_loc_remap, macro_loc_table->size ()); for (auto iter = macro_loc_table->begin (), end = macro_loc_table->end (); iter != end; ++iter) macro_loc_remap->quick_push (*iter); delete macro_loc_table; macro_loc_table = nullptr; macro_loc_remap->qsort (¯o_loc_info::compare); offset = 0; for (auto iter = macro_loc_remap->begin (), end = macro_loc_remap->end (); iter != end; ++iter) { auto mac = iter->src; iter->remap = offset; offset += mac->n_tokens; } info.second = macro_loc_remap->length (); cfg->macro_locs = offset; dump () && dump ("Macro maps:%u locs:%u", info.second, cfg->macro_locs); dump.outdent (); // If we have no ordinary locs, we must also have no macro locs. gcc_checking_assert (cfg->ordinary_locs || !cfg->macro_locs); return info; } bool module_state::read_prepare_maps (const module_state_config *cfg) { location_t ordinary = line_table->highest_location + 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 & read the location maps. Not called if there are no locations. */ void module_state::write_ordinary_maps (elf_out *to, range_t &info, bool has_partitions, unsigned *crc_p) { dump () && dump ("Writing ordinary location maps"); dump.indent (); vec filenames; filenames.create (20); /* Determine the unique filenames. */ const line_map_ordinary *current = nullptr; for (auto iter = ord_loc_remap->begin (), end = ord_loc_remap->end (); iter != end; ++iter) if (iter->src != current) { current = iter->src; const char *fname = ORDINARY_MAP_FILE_NAME (iter->src); /* We should never find a module linemap in an interval. */ gcc_checking_assert (!MAP_MODULE_P (iter->src)); /* 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 (iter->src)->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); } sec.u (info.first); /* Num maps. */ const ord_loc_info *base = nullptr; for (auto iter = ord_loc_remap->begin (), end = ord_loc_remap->end (); iter != end; ++iter) { dump (dumper::LOCATION) && dump ("Span:%u ordinary [%u+%u,+%u)->[%u,+%u)", iter - ord_loc_remap->begin (), MAP_START_LOCATION (iter->src), iter->offset, iter->span, iter->remap, iter->span); if (!base || iter->src != base->src) base = iter; sec.u (iter->offset - base->offset); if (base == iter) { sec.u (iter->src->sysp); sec.u (iter->src->m_range_bits); sec.u (iter->src->m_column_and_range_bits - iter->src->m_range_bits); const char *fname = ORDINARY_MAP_FILE_NAME (iter->src); for (unsigned ix = 0; ix != filenames.length (); ix++) if (filenames[ix] == fname) { sec.u (ix); break; } unsigned line = ORDINARY_MAP_STARTING_LINE_NUMBER (iter->src); line += iter->offset >> iter->src->m_column_and_range_bits; sec.u (line); } sec.u (iter->remap); if (base == iter) { /* 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 (iter->src); gcc_checking_assert (from < MAP_START_LOCATION (iter->src)); 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); } } filenames.release (); sec.end (to, to->name (MOD_SNAME_PFX ".olm"), crc_p); dump.outdent (); } void module_state::write_macro_maps (elf_out *to, range_t &info, unsigned *crc_p) { dump () && dump ("Writing macro location maps"); dump.indent (); bytes_out sec (to); sec.begin (); dump () && dump ("Macro maps:%u", info.second); sec.u (info.second); unsigned macro_num = 0; for (auto iter = macro_loc_remap->end (), begin = macro_loc_remap->begin (); iter-- != begin;) { auto mac = iter->src; sec.u (iter->remap); sec.u (mac->n_tokens); sec.cpp_node (mac->macro); write_location (sec, mac->expansion); const location_t *locs = mac->macro_locations; /* There are lots of identical runs. */ location_t prev = UNKNOWN_LOCATION; unsigned count = 0; unsigned runs = 0; for (unsigned jx = mac->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 ("Macro:%u %I %u/%u*2 locations [%u,%u)->%u", macro_num, identifier (mac->macro), runs, mac->n_tokens, MAP_START_LOCATION (mac), MAP_START_LOCATION (mac) + mac->n_tokens, iter->remap); macro_num++; } gcc_assert (macro_num == info.second); sec.end (to, to->name (MOD_SNAME_PFX ".mlm"), crc_p); dump.outdent (); } bool module_state::read_ordinary_maps (unsigned num_ord_locs, unsigned range_bits) { 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 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 (); dump () && dump ("Ordinary maps:%u, range_bits:%u", num_ordinary, range_bits); location_t offset = line_table->highest_location + 1; offset += ((1u << range_bits) - 1); offset &= ~((1u << range_bits) - 1); ordinary_locs.first = offset; bool propagated = spans.maybe_propagate (this, offset); line_map_ordinary *maps = static_cast (line_map_new_raw (line_table, false, num_ordinary)); const line_map_ordinary *base = nullptr; for (unsigned ix = 0; ix != num_ordinary && !sec.get_overrun (); ix++) { line_map_ordinary *map = &maps[ix]; unsigned offset = sec.u (); if (!offset) { map->reason = LC_RENAME; map->sysp = sec.u (); map->m_range_bits = sec.u (); map->m_column_and_range_bits = sec.u () + map->m_range_bits; unsigned fnum = sec.u (); map->to_file = (fnum < filenames.length () ? filenames[fnum] : ""); map->to_line = sec.u (); base = map; } else { *map = *base; map->to_line += offset >> map->m_column_and_range_bits; } unsigned remap = sec.u (); map->start_location = remap + ordinary_locs.first; if (base == map) { /* Root the outermost map at our location. */ ordinary_locs.second = remap; location_t from = read_location (sec); map->included_from = from != UNKNOWN_LOCATION ? from : loc; } } ordinary_locs.second = num_ord_locs; /* highest_location is the one handed out, not the next one to hand out. */ line_table->highest_location = ordinary_locs.first + 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 [%u,+%u)", ordinary_locs.first, 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 (unsigned num_macro_locs) { 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 (); dump () && dump ("Macro maps:%u locs:%u", num_macros, num_macro_locs); bool propagated = spans.maybe_propagate (this, line_table->highest_location + 1); location_t offset = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); macro_locs.second = num_macro_locs; macro_locs.first = offset - num_macro_locs; dump () && dump ("Macro loc delta %d", offset); dump () && dump ("Macro locations [%u,%u)", macro_locs.first, macro_locs.second); for (unsigned ix = 0; ix != num_macros && !sec.get_overrun (); ix++) { unsigned offset = sec.u (); 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; gcc_checking_assert (MAP_START_LOCATION (macro) == offset + macro_locs.first); 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); } 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) { sec.u (macro->count); sec.b (macro->fun_like); sec.b (macro->variadic); sec.b (macro->syshdr); sec.bflush (); 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]; 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 (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) const { unsigned count = sec.u (); /* We rely on knowing cpp_reader's hash table is ident_hash, and its 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 = read_location (sec); 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 = read_location (sec); 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 (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 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_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_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 *> (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; } /* Gather the macro definitions and undefinitions that we will need to write out. */ vec * module_state::prepare_macros (cpp_reader *reader) { vec *macros; vec_alloc (macros, 100); cpp_forall_identifiers (reader, maybe_add_macro, macros); dump (dumper::MACRO) && dump ("No more than %u macros", macros->length ()); macros->qsort (macro_loc_cmp); // Note the locations. for (unsigned ix = macros->length (); ix--;) { cpp_hashnode *node = (*macros)[ix]; macro_import::slot &slot = (*macro_imports)[node->deferred - 1][0]; macro_export &mac = (*macro_exports)[slot.offset]; if (IDENTIFIER_KEYWORD_P (identifier (node))) continue; if (mac.undef_loc != UNKNOWN_LOCATION) note_location (mac.undef_loc); if (mac.def) { note_location (mac.def->line); for (unsigned ix = 0; ix != mac.def->count; ix++) note_location (mac.def->exp.tokens[ix].src_loc); } } return macros; } /* 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, vec *macros, unsigned *crc_p) { dump () && dump ("Writing macros"); dump.indent (); /* 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); } if (count) // We may have ended on a tokenless macro with a very short // location, that will cause problems reading its bit flags. sec.u (0); 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); } 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 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.loc_range_bits); cfg.u (config.active_init); /* 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.loc_range_bits = cfg.u (); config.active_init = 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 (a_); auto *b = *static_cast (b_); if (a == b) return 0; else if (a->ordinary_locs.first < b->ordinary_locs.first) 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_begin (elf_out *to, cpp_reader *reader, module_state_config &config, unsigned &crc) { /* Figure out remapped module numbers, which might elide partitions. */ bitmap partitions = NULL; if (!is_header () && !is_partition ()) partitions = BITMAP_GGC_ALLOC (); write_init_maps (); 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 (imp->is_direct () && (imp->remap || imp->is_partition ())) note_location (imp->imported_from ()); } 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 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); vec *macros = nullptr; if (is_header ()) macros = prepare_macros (reader); config.num_imports = mod_hwm; config.num_partitions = modules->length () - mod_hwm; auto map_info = write_prepare_maps (&config, bool (config.num_partitions)); unsigned counts[MSC_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 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. */ if (config.ordinary_locs) write_ordinary_maps (to, map_info, bool (config.num_partitions), &crc); if (config.macro_locs) write_macro_maps (to, map_info, &crc); if (is_header ()) { counts[MSC_macros] = write_macros (to, macros, &crc); counts[MSC_inits] = write_inits (to, table, &crc); vec_free (macros); } 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)); trees_out::instrument (); write_counts (to, counts, &crc); spaces.release (); sccs.release (); vec_free (macro_loc_remap); vec_free (ord_loc_remap); vec_free (ool); // 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); } // Finish module writing after we've emitted all dynamic initializers. void module_state::write_end (elf_out *to, cpp_reader *reader, module_state_config &config, unsigned &crc) { /* And finish up. */ write_config (to, config, crc); /* Human-readable info. */ write_readme (to, reader, config.dialect_str); 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 && config.ordinary_locs)) ordinary_locs.first = line_table->highest_location + 1; else if (!read_ordinary_maps (config.ordinary_locs, config.loc_range_bits)) 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 monotonically 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 && config.macro_locs)) macro_locs.first = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); else if (!read_macro_maps (config.macro_locs)) ok = false; /* Note whether there's an active initializer. */ active_init_p = !is_header () && bool (config.active_init); 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_ATTACH_P (not_tmpl)) return -1; int mod = !DECL_MODULE_IMPORT_P (not_tmpl) ? 0 : get_importing_module (owner); gcc_checking_assert (!for_mangle || !(*modules)[mod]->is_header ()); 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) { for (;;) { tree ctx = CP_DECL_CONTEXT (decl); if (TREE_CODE (ctx) == NAMESPACE_DECL) // Found the namespace-scope decl. break; if (!CLASS_TYPE_P (ctx)) // We've met a non-class scope. Such a thing is not // reopenable, so we must be ok. return true; decl = TYPE_NAME (ctx); } tree not_tmpl = STRIP_TEMPLATE (decl); int use_tpl = 0; if (node_template_info (not_tmpl, use_tpl) && use_tpl) // Specializations of any kind can be redeclared anywhere. // FIXME: Should we be checking this in more places on the scope chain? return true; if (!DECL_LANG_SPECIFIC (not_tmpl) || !DECL_MODULE_ATTACH_P (not_tmpl)) // Decl is attached to global module. Current scope needs to be too. return !module_attach_p (); module_state *me = (*modules)[0]; module_state *them = me; 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); } // Decl is attached to named module. Current scope needs to be // attaching to the same module. if (!module_attach_p ()) return false; // Both attached to named module. if (me == them) return true; 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 (!DECL_NAMESPACE_SCOPE_P (decl)) return; gcc_checking_assert (friend_p || decl == get_originating_module_decl (decl)); if (module_attach_p ()) { retrofit_lang_decl (decl); DECL_MODULE_ATTACH_P (decl) = true; } if (!module_exporting_p ()) return; // FIXME: Check ill-formed linkage DECL_MODULE_EXPORT_P (decl) = true; } /* DECL is keyed to CTX for odr purposes. */ void maybe_key_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 (!keyed_table) keyed_table = new keyed_map_t (EXPERIMENT (1, 400)); auto &vec = keyed_table->get_or_insert (ctx); if (!vec.length ()) { retrofit_lang_decl (ctx); DECL_MODULE_KEYED_DECLS_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 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 (!keyed_table) keyed_table = new keyed_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 = module->is_header () ? MK_HEADER : MK_NAMED | MK_ATTACH; // Even in header units, we consider the decls to be purview module_kind |= MK_PURVIEW; if (module->is_partition ()) module_kind |= MK_PARTITION; if (exporting_p) { module->interface_p = true; module_kind |= MK_INTERFACE; } if (module_has_cmi_p ()) { /* 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); } } /* Return true IFF we must emit a module global initializer function (which will be called by importers' init code). */ bool module_global_init_needed () { return module_has_cmi_p () && !header_module_p (); } /* Calculate which, if any, import initializers need calling. */ bool module_determine_import_inits () { if (!modules || header_module_p ()) return false; /* Prune active_init_p. We need the same bitmap allocation scheme as for the imports member. */ function_depth++; /* Disable GC. */ bitmap covered_imports (BITMAP_GGC_ALLOC ()); bool any = false; /* 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->active_init_p) ; else if (bitmap_bit_p (covered_imports, ix)) import->active_init_p = false; else { /* Everything this imports is therefore handled by its initializer, so doesn't need initializing by us. */ bitmap_ior_into (covered_imports, import->imports); any = true; } } function_depth--; return any; } /* 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 () { if (!modules || header_module_p ()) return; 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->active_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); } } } /* 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::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); module_kind |= MK_EXPORTING; 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::create_ggc (31); vec_safe_reserve (modules, 20); /* Create module for current TU. */ module_state *current = new (ggc_alloc ()) 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); } // State propagated from finish_module_processing to fini_modules struct module_processing_cookie { elf_out out; module_state_config config; char *cmi_name; char *tmp_name; unsigned crc; bool began; module_processing_cookie (char *cmi, char *tmp, int fd, int e) : out (fd, e), cmi_name (cmi), tmp_name (tmp), crc (0), began (false) { } ~module_processing_cookie () { XDELETEVEC (tmp_name); XDELETEVEC (cmi_name); } }; /* Write the CMI, if we're a module interface. */ void * finish_module_processing (cpp_reader *reader) { module_processing_cookie *cookie = nullptr; 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 = -1; 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. */ char *cmi_name = 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; cmi_name = xstrdup (maybe_add_cmi_prefix (state->filename, &len)); tmp_name = XNEWVEC (char, len + 3); memcpy (tmp_name, cmi_name, 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", cmi_name); dump () && dump ("CMI is %s", cmi_name); } cookie = new module_processing_cookie (cmi_name, tmp_name, fd, e); if (errorcount) warning_at (state->loc, 0, "not writing module %qs due to errors", state->get_flatname ()); else if (cookie->out.begin ()) { cookie->began = true; auto loc = input_location; /* So crashes finger-point the module decl. */ input_location = state->loc; state->write_begin (&cookie->out, reader, cookie->config, cookie->crc); input_location = loc; } 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); } return cookie; } // Do the final emission of a module. At this point we know whether // the module static initializer is a NOP or not. static void late_finish_module (cpp_reader *reader, module_processing_cookie *cookie, bool init_fn_non_empty) { timevar_start (TV_MODULE_EXPORT); module_state *state = (*modules)[0]; unsigned n = dump.push (state); state->announce ("finishing"); cookie->config.active_init = init_fn_non_empty; if (cookie->began) state->write_end (&cookie->out, reader, cookie->config, cookie->crc); if (cookie->out.end () && cookie->cmi_name) { /* Some OS's do not replace NEWNAME if it already exists. This'll have a race condition in erroneous concurrent builds. */ unlink (cookie->cmi_name); if (rename (cookie->tmp_name, cookie->cmi_name)) { dump () && dump ("Rename ('%s','%s') errno=%u", cookie->tmp_name, cookie->cmi_name, errno); cookie->out.set_error (errno); } } if (cookie->out.get_error () && cookie->began) { error_at (state->loc, "failed to write compiled module: %s", cookie->out.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 (cookie->cmi_name) { /* We failed, attempt to erase all evidence we even tried. */ unlink (cookie->tmp_name); unlink (cookie->cmi_name); } delete cookie; dump.pop (n); timevar_stop (TV_MODULE_EXPORT); } void fini_modules (cpp_reader *reader, void *cookie, bool has_inits) { if (cookie) late_finish_module (reader, static_cast (cookie), has_inits); /* 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 keys -- Let it go! */ delete keyed_table; keyed_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"