| /* C++ modules. Experimental! |
| Copyright (C) 2017-2021 Free Software Foundation, Inc. |
| Written by Nathan Sidwell <nathan@acm.org> while at FaceBook |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* Comments in this file have a non-negligible chance of being wrong |
| or at least inaccurate. Due to (a) my misunderstanding, (b) |
| ambiguities that I have interpretted differently to original intent |
| (c) changes in the specification, (d) my poor wording, (e) source |
| changes. */ |
| |
| /* (Incomplete) Design Notes |
| |
| A hash table contains all module names. Imported modules are |
| present in a modules array, which by construction places an |
| import's dependencies before the import itself. The single |
| exception is the current TU, which always occupies slot zero (even |
| when it is not a module). |
| |
| Imported decls occupy an entity_ary, an array of binding_slots, indexed |
| by importing module and index within that module. A flat index is |
| used, as each module reserves a contiguous range of indices. |
| Initially each slot indicates the CMI section containing the |
| streamed decl. When the decl is imported it will point to the decl |
| itself. |
| |
| Additionally each imported decl is mapped in the entity_map via its |
| DECL_UID to the flat index in the entity_ary. Thus we can locate |
| the index for any imported decl by using this map and then |
| de-flattening the index via a binary seach of the module vector. |
| Cross-module references are by (remapped) module number and |
| module-local index. |
| |
| Each importable DECL contains several flags. The simple set are |
| DECL_EXPORT_P, DECL_MODULE_PURVIEW_P and DECL_MODULE_IMPORT_P. The |
| first indicates whether it is exported, the second whether it is in |
| the module purview (as opposed to the global module fragment), and |
| the third indicates whether it was an import into this TU or not. |
| |
| The more detailed flags are DECL_MODULE_PARTITION_P, |
| DECL_MODULE_ENTITY_P. The first is set in a primary interface unit |
| on decls that were read from module partitions (these will have |
| DECL_MODULE_IMPORT_P set too). Such decls will be streamed out to |
| the primary's CMI. DECL_MODULE_ENTITY_P is set when an entity is |
| imported, even if it matched a non-imported entity. Such a decl |
| will not have DECL_MODULE_IMPORT_P set, even though it has an entry |
| in the entity map and array. |
| |
| Header units are module-like. |
| |
| For namespace-scope lookup, the decls for a particular module are |
| held located in a sparse array hanging off the binding of the name. |
| This is partitioned into two: a few fixed slots at the start |
| followed by the sparse slots afterwards. By construction we only |
| need to append new slots to the end -- there is never a need to |
| insert in the middle. The fixed slots are MODULE_SLOT_CURRENT for |
| the current TU (regardless of whether it is a module or not), |
| MODULE_SLOT_GLOBAL and MODULE_SLOT_PARTITION. These latter two |
| slots are used for merging entities across the global module and |
| module partitions respectively. MODULE_SLOT_PARTITION is only |
| present in a module. Neither of those two slots is searched during |
| name lookup -- they are internal use only. This vector is created |
| lazily once we require it, if there is only a declaration from the |
| current TU, a regular binding is present. It is converted on |
| demand. |
| |
| OPTIMIZATION: Outside of the current TU, we only need ADL to work. |
| We could optimize regular lookup for the current TU by glomming all |
| the visible decls on its slot. Perhaps wait until design is a |
| little more settled though. |
| |
| There is only one instance of each extern-linkage namespace. It |
| appears in every module slot that makes it visible. It also |
| appears in MODULE_SLOT_GLOBAL. (It is an ODR violation if they |
| collide with some other global module entity.) We also have an |
| optimization that shares the slot for adjacent modules that declare |
| the same such namespace. |
| |
| A module interface compilation produces a Compiled Module Interface |
| (CMI). The format used is Encapsulated Lazy Records Of Numbered |
| Declarations, which is essentially ELF's section encapsulation. (As |
| all good nerds are aware, Elrond is half Elf.) Some sections are |
| named, and contain information about the module as a whole (indices |
| etc), and other sections are referenced by number. Although I |
| don't defend against actively hostile CMIs, there is some |
| checksumming involved to verify data integrity. When dumping out |
| an interface, we generate a graph of all the |
| independently-redeclarable DECLS that are needed, and the decls |
| they reference. From that we determine the strongly connected |
| components (SCC) within this TU. Each SCC is dumped to a separate |
| numbered section of the CMI. We generate a binding table section, |
| mapping each namespace&name to a defining section. This allows |
| lazy loading. |
| |
| Lazy loading employs mmap to map a read-only image of the CMI. |
| It thus only occupies address space and is paged in on demand, |
| backed by the CMI file itself. If mmap is unavailable, regular |
| FILEIO is used. Also, there's a bespoke ELF reader/writer here, |
| which implements just the section table and sections (including |
| string sections) of a 32-bit ELF in host byte-order. You can of |
| course inspect it with readelf. I figured 32-bit is sufficient, |
| for a single module. I detect running out of section numbers, but |
| do not implement the ELF overflow mechanism. At least you'll get |
| an error if that happens. |
| |
| We do not separate declarations and definitions. My guess is that |
| if you refer to the declaration, you'll also need the definition |
| (template body, inline function, class definition etc). But this |
| does mean we can get larger SCCs than if we separated them. It is |
| unclear whether this is a win or not. |
| |
| Notice that we embed section indices into the contents of other |
| sections. Thus random manipulation of the CMI file by ELF tools |
| may well break it. The kosher way would probably be to introduce |
| indirection via section symbols, but that would require defining a |
| relocation type. |
| |
| Notice that lazy loading of one module's decls can cause lazy |
| loading of other decls in the same or another module. Clearly we |
| want to avoid loops. In a correct program there can be no loops in |
| the module dependency graph, and the above-mentioned SCC algorithm |
| places all intra-module circular dependencies in the same SCC. It |
| also orders the SCCs wrt each other, so dependent SCCs come first. |
| As we load dependent modules first, we know there can be no |
| reference to a higher-numbered module, and because we write out |
| dependent SCCs first, likewise for SCCs within the module. This |
| allows us to immediately detect broken references. When loading, |
| we must ensure the rest of the compiler doesn't cause some |
| unconnected load to occur (for instance, instantiate a template). |
| |
| Classes used: |
| |
| dumper - logger |
| |
| data - buffer |
| |
| bytes - data streamer |
| bytes_in : bytes - scalar reader |
| bytes_out : bytes - scalar writer |
| |
| elf - ELROND format |
| elf_in : elf - ELROND reader |
| elf_out : elf - ELROND writer |
| |
| trees_in : bytes_in - tree reader |
| trees_out : bytes_out - tree writer |
| |
| depset - dependency set |
| depset::hash - hash table of depsets |
| depset::tarjan - SCC determinator |
| |
| uidset<T> - set T's related to a UID |
| uidset<T>::hash hash table of uidset<T> |
| |
| loc_spans - location map data |
| |
| module_state - module object |
| |
| slurping - data needed during loading |
| |
| macro_import - imported macro data |
| macro_export - exported macro data |
| |
| The ELROND objects use mmap, for both reading and writing. If mmap |
| is unavailable, fileno IO is used to read and write blocks of data. |
| |
| The mapper object uses fileno IO to communicate with the server or |
| program. */ |
| |
| /* In expermental (trunk) sources, MODULE_VERSION is a #define passed |
| in from the Makefile. It records the modification date of the |
| source directory -- that's the only way to stay sane. In release |
| sources, we (plan to) use the compiler's major.minor versioning. |
| While the format might not change between at minor versions, it |
| seems simplest to tie the two together. There's no concept of |
| inter-version compatibility. */ |
| #define IS_EXPERIMENTAL(V) ((V) >= (1U << 20)) |
| #define MODULE_MAJOR(V) ((V) / 10000) |
| #define MODULE_MINOR(V) ((V) % 10000) |
| #define EXPERIMENT(A,B) (IS_EXPERIMENTAL (MODULE_VERSION) ? (A) : (B)) |
| #ifndef MODULE_VERSION |
| #include "bversion.h" |
| #define MODULE_VERSION (BUILDING_GCC_MAJOR * 10000U + BUILDING_GCC_MINOR) |
| #elif !IS_EXPERIMENTAL (MODULE_VERSION) |
| #error "This is not the version I was looking for." |
| #endif |
| |
| #define _DEFAULT_SOURCE 1 /* To get TZ field of struct tm, if available. */ |
| #include "config.h" |
| #define INCLUDE_STRING |
| #define INCLUDE_VECTOR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "cp-tree.h" |
| #include "timevar.h" |
| #include "stringpool.h" |
| #include "dumpfile.h" |
| #include "bitmap.h" |
| #include "cgraph.h" |
| #include "tree-iterator.h" |
| #include "cpplib.h" |
| #include "mkdeps.h" |
| #include "incpath.h" |
| #include "libiberty.h" |
| #include "stor-layout.h" |
| #include "version.h" |
| #include "tree-diagnostic.h" |
| #include "toplev.h" |
| #include "opts.h" |
| #include "attribs.h" |
| #include "intl.h" |
| #include "langhooks.h" |
| /* This TU doesn't need or want to see the networking. */ |
| #define CODY_NETWORKING 0 |
| #include "mapper-client.h" |
| |
| #if 0 // 1 for testing no mmap |
| #define MAPPED_READING 0 |
| #define MAPPED_WRITING 0 |
| #else |
| #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0 |
| /* mmap, munmap. */ |
| #define MAPPED_READING 1 |
| #if HAVE_SYSCONF && defined (_SC_PAGE_SIZE) |
| /* msync, sysconf (_SC_PAGE_SIZE), ftruncate */ |
| /* posix_fallocate used if available. */ |
| #define MAPPED_WRITING 1 |
| #else |
| #define MAPPED_WRITING 0 |
| #endif |
| #else |
| #define MAPPED_READING 0 |
| #define MAPPED_WRITING 0 |
| #endif |
| #endif |
| |
| /* Some open(2) flag differences, what a colourful world it is! */ |
| #if defined (O_CLOEXEC) |
| // OK |
| #elif defined (_O_NOINHERIT) |
| /* Windows' _O_NOINHERIT matches O_CLOEXEC flag */ |
| #define O_CLOEXEC _O_NOINHERIT |
| #else |
| #define O_CLOEXEC 0 |
| #endif |
| #if defined (O_BINARY) |
| // Ok? |
| #elif defined (_O_BINARY) |
| /* Windows' open(2) call defaults to text! */ |
| #define O_BINARY _O_BINARY |
| #else |
| #define O_BINARY 0 |
| #endif |
| |
| static inline cpp_hashnode *cpp_node (tree id) |
| { |
| return CPP_HASHNODE (GCC_IDENT_TO_HT_IDENT (id)); |
| } |
| |
| static inline tree identifier (const cpp_hashnode *node) |
| { |
| /* HT_NODE() expands to node->ident that HT_IDENT_TO_GCC_IDENT() |
| then subtracts a nonzero constant, deriving a pointer to |
| a different member than ident. That's strictly undefined |
| and detected by -Warray-bounds. Suppress it. See PR 101372. */ |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Warray-bounds" |
| return HT_IDENT_TO_GCC_IDENT (HT_NODE (const_cast<cpp_hashnode *> (node))); |
| #pragma GCC diagnostic pop |
| } |
| |
| /* Id for dumping module information. */ |
| int module_dump_id; |
| |
| /* We have a special module owner. */ |
| #define MODULE_UNKNOWN (~0U) /* Not yet known. */ |
| |
| /* Prefix for section names. */ |
| #define MOD_SNAME_PFX ".gnu.c++" |
| |
| /* Format a version for user consumption. */ |
| |
| typedef char verstr_t[32]; |
| static void |
| version2string (unsigned version, verstr_t &out) |
| { |
| unsigned major = MODULE_MAJOR (version); |
| unsigned minor = MODULE_MINOR (version); |
| |
| if (IS_EXPERIMENTAL (version)) |
| sprintf (out, "%04u/%02u/%02u-%02u:%02u%s", |
| 2000 + major / 10000, (major / 100) % 100, (major % 100), |
| minor / 100, minor % 100, |
| EXPERIMENT ("", " (experimental)")); |
| else |
| sprintf (out, "%u.%u", major, minor); |
| } |
| |
| /* Include files to note translation for. */ |
| static vec<const char *, va_heap, vl_embed> *note_includes; |
| |
| /* Modules to note CMI pathames. */ |
| static vec<const char *, va_heap, vl_embed> *note_cmis; |
| |
| /* Traits to hash an arbitrary pointer. Entries are not deletable, |
| and removal is a noop (removal needed upon destruction). */ |
| template <typename T> |
| struct nodel_ptr_hash : pointer_hash<T>, typed_noop_remove <T *> { |
| /* Nothing is deletable. Everything is insertable. */ |
| static bool is_deleted (T *) { return false; } |
| static void mark_deleted (T *) { gcc_unreachable (); } |
| }; |
| |
| /* Map from pointer to signed integer. */ |
| typedef simple_hashmap_traits<nodel_ptr_hash<void>, int> ptr_int_traits; |
| typedef hash_map<void *,signed,ptr_int_traits> ptr_int_hash_map; |
| |
| /********************************************************************/ |
| /* Basic streaming & ELF. Serialization is usually via mmap. For |
| writing we slide a buffer over the output file, syncing it |
| approproiately. For reading we simply map the whole file (as a |
| file-backed read-only map -- it's just address space, leaving the |
| OS pager to deal with getting the data to us). Some buffers need |
| to be more conventional malloc'd contents. */ |
| |
| /* Variable length buffer. */ |
| |
| class data { |
| public: |
| class allocator { |
| public: |
| /* Tools tend to moan if the dtor's not virtual. */ |
| virtual ~allocator () {} |
| |
| public: |
| void grow (data &obj, unsigned needed, bool exact); |
| void shrink (data &obj); |
| |
| public: |
| virtual char *grow (char *ptr, unsigned needed); |
| virtual void shrink (char *ptr); |
| }; |
| |
| public: |
| char *buffer; /* Buffer being transferred. */ |
| /* Although size_t would be the usual size, we know we never get |
| more than 4GB of buffer -- because that's the limit of the |
| encapsulation format. And if you need bigger imports, you're |
| doing it wrong. */ |
| unsigned size; /* Allocated size of buffer. */ |
| unsigned pos; /* Position in buffer. */ |
| |
| public: |
| data () |
| :buffer (NULL), size (0), pos (0) |
| { |
| } |
| ~data () |
| { |
| /* Make sure the derived and/or using class know what they're |
| doing. */ |
| gcc_checking_assert (!buffer); |
| } |
| |
| protected: |
| char *use (unsigned count) |
| { |
| if (size < pos + count) |
| return NULL; |
| char *res = &buffer[pos]; |
| pos += count; |
| return res; |
| } |
| |
| public: |
| void unuse (unsigned count) |
| { |
| pos -= count; |
| } |
| |
| public: |
| static allocator simple_memory; |
| }; |
| |
| /* The simple data allocator. */ |
| data::allocator data::simple_memory; |
| |
| /* Grow buffer to at least size NEEDED. */ |
| |
| void |
| data::allocator::grow (data &obj, unsigned needed, bool exact) |
| { |
| gcc_checking_assert (needed ? needed > obj.size : !obj.size); |
| if (!needed) |
| /* Pick a default size. */ |
| needed = EXPERIMENT (100, 1000); |
| |
| if (!exact) |
| needed *= 2; |
| obj.buffer = grow (obj.buffer, needed); |
| if (obj.buffer) |
| obj.size = needed; |
| else |
| obj.pos = obj.size = 0; |
| } |
| |
| /* Free a buffer. */ |
| |
| void |
| data::allocator::shrink (data &obj) |
| { |
| shrink (obj.buffer); |
| obj.buffer = NULL; |
| obj.size = 0; |
| } |
| |
| char * |
| data::allocator::grow (char *ptr, unsigned needed) |
| { |
| return XRESIZEVAR (char, ptr, needed); |
| } |
| |
| void |
| data::allocator::shrink (char *ptr) |
| { |
| XDELETEVEC (ptr); |
| } |
| |
| /* Byte streamer base. Buffer with read/write position and smarts |
| for single bits. */ |
| |
| class bytes : public data { |
| public: |
| typedef data parent; |
| |
| protected: |
| uint32_t bit_val; /* Bit buffer. */ |
| unsigned bit_pos; /* Next bit in bit buffer. */ |
| |
| public: |
| bytes () |
| :parent (), bit_val (0), bit_pos (0) |
| {} |
| ~bytes () |
| { |
| } |
| |
| protected: |
| unsigned calc_crc (unsigned) const; |
| |
| protected: |
| /* Finish bit packet. Rewind the bytes not used. */ |
| unsigned bit_flush () |
| { |
| gcc_assert (bit_pos); |
| unsigned bytes = (bit_pos + 7) / 8; |
| unuse (4 - bytes); |
| bit_pos = 0; |
| bit_val = 0; |
| return bytes; |
| } |
| }; |
| |
| /* Calculate the crc32 of the buffer. Note the CRC is stored in the |
| first 4 bytes, so don't include them. */ |
| |
| unsigned |
| bytes::calc_crc (unsigned l) const |
| { |
| unsigned crc = 0; |
| for (size_t ix = 4; ix < l; ix++) |
| crc = crc32_byte (crc, buffer[ix]); |
| return crc; |
| } |
| |
| class elf_in; |
| |
| /* Byte stream reader. */ |
| |
| class bytes_in : public bytes { |
| typedef bytes parent; |
| |
| protected: |
| bool overrun; /* Sticky read-too-much flag. */ |
| |
| public: |
| bytes_in () |
| : parent (), overrun (false) |
| { |
| } |
| ~bytes_in () |
| { |
| } |
| |
| public: |
| /* Begin reading a named section. */ |
| bool begin (location_t loc, elf_in *src, const char *name); |
| /* Begin reading a numbered section with optional name. */ |
| bool begin (location_t loc, elf_in *src, unsigned, const char * = NULL); |
| /* Complete reading a buffer. Propagate errors and return true on |
| success. */ |
| bool end (elf_in *src); |
| /* Return true if there is unread data. */ |
| bool more_p () const |
| { |
| return pos != size; |
| } |
| |
| public: |
| /* Start reading at OFFSET. */ |
| void random_access (unsigned offset) |
| { |
| if (offset > size) |
| set_overrun (); |
| pos = offset; |
| bit_pos = bit_val = 0; |
| } |
| |
| public: |
| void align (unsigned boundary) |
| { |
| if (unsigned pad = pos & (boundary - 1)) |
| read (boundary - pad); |
| } |
| |
| public: |
| const char *read (unsigned count) |
| { |
| char *ptr = use (count); |
| if (!ptr) |
| set_overrun (); |
| return ptr; |
| } |
| |
| public: |
| bool check_crc () const; |
| /* We store the CRC in the first 4 bytes, using host endianness. */ |
| unsigned get_crc () const |
| { |
| return *(const unsigned *)&buffer[0]; |
| } |
| |
| public: |
| /* Manipulate the overrun flag. */ |
| bool get_overrun () const |
| { |
| return overrun; |
| } |
| void set_overrun () |
| { |
| overrun = true; |
| } |
| |
| public: |
| unsigned u32 (); /* Read uncompressed integer. */ |
| |
| public: |
| bool b (); /* Read a bool. */ |
| void bflush (); /* Completed a block of bools. */ |
| |
| private: |
| void bfill (); /* Get the next block of bools. */ |
| |
| public: |
| int c (); /* Read a char. */ |
| int i (); /* Read a signed int. */ |
| unsigned u (); /* Read an unsigned int. */ |
| size_t z (); /* Read a size_t. */ |
| HOST_WIDE_INT wi (); /* Read a HOST_WIDE_INT. */ |
| unsigned HOST_WIDE_INT wu (); /* Read an unsigned HOST_WIDE_INT. */ |
| const char *str (size_t * = NULL); /* Read a string. */ |
| const void *buf (size_t); /* Read a fixed-length buffer. */ |
| cpp_hashnode *cpp_node (); /* Read a cpp node. */ |
| }; |
| |
| /* Verify the buffer's CRC is correct. */ |
| |
| bool |
| bytes_in::check_crc () const |
| { |
| if (size < 4) |
| return false; |
| |
| unsigned c_crc = calc_crc (size); |
| if (c_crc != get_crc ()) |
| return false; |
| |
| return true; |
| } |
| |
| class elf_out; |
| |
| /* Byte stream writer. */ |
| |
| class bytes_out : public bytes { |
| typedef bytes parent; |
| |
| public: |
| allocator *memory; /* Obtainer of memory. */ |
| |
| public: |
| bytes_out (allocator *memory) |
| : parent (), memory (memory) |
| { |
| } |
| ~bytes_out () |
| { |
| } |
| |
| public: |
| bool streaming_p () const |
| { |
| return memory != NULL; |
| } |
| |
| public: |
| void set_crc (unsigned *crc_ptr); |
| |
| public: |
| /* Begin writing, maybe reserve space for CRC. */ |
| void begin (bool need_crc = true); |
| /* Finish writing. Spill to section by number. */ |
| unsigned end (elf_out *, unsigned, unsigned *crc_ptr = NULL); |
| |
| public: |
| void align (unsigned boundary) |
| { |
| if (unsigned pad = pos & (boundary - 1)) |
| write (boundary - pad); |
| } |
| |
| public: |
| char *write (unsigned count, bool exact = false) |
| { |
| if (size < pos + count) |
| memory->grow (*this, pos + count, exact); |
| return use (count); |
| } |
| |
| public: |
| void u32 (unsigned); /* Write uncompressed integer. */ |
| |
| public: |
| void b (bool); /* Write bool. */ |
| void bflush (); /* Finish block of bools. */ |
| |
| public: |
| void c (unsigned char); /* Write unsigned char. */ |
| void i (int); /* Write signed int. */ |
| void u (unsigned); /* Write unsigned int. */ |
| void z (size_t s); /* Write size_t. */ |
| void wi (HOST_WIDE_INT); /* Write HOST_WIDE_INT. */ |
| void wu (unsigned HOST_WIDE_INT); /* Write unsigned HOST_WIDE_INT. */ |
| void str (const char *ptr) |
| { |
| str (ptr, strlen (ptr)); |
| } |
| void cpp_node (const cpp_hashnode *node) |
| { |
| str ((const char *)NODE_NAME (node), NODE_LEN (node)); |
| } |
| void str (const char *, size_t); /* Write string of known length. */ |
| void buf (const void *, size_t); /* Write fixed length buffer. */ |
| void *buf (size_t); /* Create a writable buffer */ |
| |
| public: |
| /* Format a NUL-terminated raw string. */ |
| void printf (const char *, ...) ATTRIBUTE_PRINTF_2; |
| void print_time (const char *, const tm *, const char *); |
| |
| public: |
| /* Dump instrumentation. */ |
| static void instrument (); |
| |
| protected: |
| /* Instrumentation. */ |
| static unsigned spans[4]; |
| static unsigned lengths[4]; |
| static int is_set; |
| }; |
| |
| /* Instrumentation. */ |
| unsigned bytes_out::spans[4]; |
| unsigned bytes_out::lengths[4]; |
| int bytes_out::is_set = -1; |
| |
| /* If CRC_PTR non-null, set the CRC of the buffer. Mix the CRC into |
| that pointed to by CRC_PTR. */ |
| |
| void |
| bytes_out::set_crc (unsigned *crc_ptr) |
| { |
| if (crc_ptr) |
| { |
| gcc_checking_assert (pos >= 4); |
| |
| unsigned crc = calc_crc (pos); |
| unsigned accum = *crc_ptr; |
| /* Only mix the existing *CRC_PTR if it is non-zero. */ |
| accum = accum ? crc32_unsigned (accum, crc) : crc; |
| *crc_ptr = accum; |
| |
| /* Buffer will be sufficiently aligned. */ |
| *(unsigned *)buffer = crc; |
| } |
| } |
| |
| /* Finish a set of bools. */ |
| |
| void |
| bytes_out::bflush () |
| { |
| if (bit_pos) |
| { |
| u32 (bit_val); |
| lengths[2] += bit_flush (); |
| } |
| spans[2]++; |
| is_set = -1; |
| } |
| |
| void |
| bytes_in::bflush () |
| { |
| if (bit_pos) |
| bit_flush (); |
| } |
| |
| /* When reading, we don't know how many bools we'll read in. So read |
| 4 bytes-worth, and then rewind when flushing if we didn't need them |
| all. You can't have a block of bools closer than 4 bytes to the |
| end of the buffer. */ |
| |
| void |
| bytes_in::bfill () |
| { |
| bit_val = u32 (); |
| } |
| |
| /* Bools are packed into bytes. You cannot mix bools and non-bools. |
| You must call bflush before emitting another type. So batch your |
| bools. |
| |
| It may be worth optimizing for most bools being zero. Some kind of |
| run-length encoding? */ |
| |
| void |
| bytes_out::b (bool x) |
| { |
| if (is_set != x) |
| { |
| is_set = x; |
| spans[x]++; |
| } |
| lengths[x]++; |
| bit_val |= unsigned (x) << bit_pos++; |
| if (bit_pos == 32) |
| { |
| u32 (bit_val); |
| lengths[2] += bit_flush (); |
| } |
| } |
| |
| bool |
| bytes_in::b () |
| { |
| if (!bit_pos) |
| bfill (); |
| bool v = (bit_val >> bit_pos++) & 1; |
| if (bit_pos == 32) |
| bit_flush (); |
| return v; |
| } |
| |
| /* Exactly 4 bytes. Used internally for bool packing and a few other |
| places. We can't simply use uint32_t because (a) alignment and |
| (b) we need little-endian for the bool streaming rewinding to make |
| sense. */ |
| |
| void |
| bytes_out::u32 (unsigned val) |
| { |
| if (char *ptr = write (4)) |
| { |
| ptr[0] = val; |
| ptr[1] = val >> 8; |
| ptr[2] = val >> 16; |
| ptr[3] = val >> 24; |
| } |
| } |
| |
| unsigned |
| bytes_in::u32 () |
| { |
| unsigned val = 0; |
| if (const char *ptr = read (4)) |
| { |
| val |= (unsigned char)ptr[0]; |
| val |= (unsigned char)ptr[1] << 8; |
| val |= (unsigned char)ptr[2] << 16; |
| val |= (unsigned char)ptr[3] << 24; |
| } |
| |
| return val; |
| } |
| |
| /* Chars are unsigned and written as single bytes. */ |
| |
| void |
| bytes_out::c (unsigned char v) |
| { |
| if (char *ptr = write (1)) |
| *ptr = v; |
| } |
| |
| int |
| bytes_in::c () |
| { |
| int v = 0; |
| if (const char *ptr = read (1)) |
| v = (unsigned char)ptr[0]; |
| return v; |
| } |
| |
| /* Ints 7-bit as a byte. Otherwise a 3bit count of following bytes in |
| big-endian form. 4 bits are in the first byte. */ |
| |
| void |
| bytes_out::i (int v) |
| { |
| if (char *ptr = write (1)) |
| { |
| if (v <= 0x3f && v >= -0x40) |
| *ptr = v & 0x7f; |
| else |
| { |
| unsigned bytes = 0; |
| int probe; |
| if (v >= 0) |
| for (probe = v >> 8; probe > 0x7; probe >>= 8) |
| bytes++; |
| else |
| for (probe = v >> 8; probe < -0x8; probe >>= 8) |
| bytes++; |
| *ptr = 0x80 | bytes << 4 | (probe & 0xf); |
| if ((ptr = write (++bytes))) |
| for (; bytes--; v >>= 8) |
| ptr[bytes] = v & 0xff; |
| } |
| } |
| } |
| |
| int |
| bytes_in::i () |
| { |
| int v = 0; |
| if (const char *ptr = read (1)) |
| { |
| v = *ptr & 0xff; |
| if (v & 0x80) |
| { |
| unsigned bytes = (v >> 4) & 0x7; |
| v &= 0xf; |
| if (v & 0x8) |
| v |= -1 ^ 0x7; |
| /* unsigned necessary due to left shifts of -ve values. */ |
| unsigned uv = unsigned (v); |
| if ((ptr = read (++bytes))) |
| while (bytes--) |
| uv = (uv << 8) | (*ptr++ & 0xff); |
| v = int (uv); |
| } |
| else if (v & 0x40) |
| v |= -1 ^ 0x3f; |
| } |
| |
| return v; |
| } |
| |
| void |
| bytes_out::u (unsigned v) |
| { |
| if (char *ptr = write (1)) |
| { |
| if (v <= 0x7f) |
| *ptr = v; |
| else |
| { |
| unsigned bytes = 0; |
| unsigned probe; |
| for (probe = v >> 8; probe > 0xf; probe >>= 8) |
| bytes++; |
| *ptr = 0x80 | bytes << 4 | probe; |
| if ((ptr = write (++bytes))) |
| for (; bytes--; v >>= 8) |
| ptr[bytes] = v & 0xff; |
| } |
| } |
| } |
| |
| unsigned |
| bytes_in::u () |
| { |
| unsigned v = 0; |
| |
| if (const char *ptr = read (1)) |
| { |
| v = *ptr & 0xff; |
| if (v & 0x80) |
| { |
| unsigned bytes = (v >> 4) & 0x7; |
| v &= 0xf; |
| if ((ptr = read (++bytes))) |
| while (bytes--) |
| v = (v << 8) | (*ptr++ & 0xff); |
| } |
| } |
| |
| return v; |
| } |
| |
| void |
| bytes_out::wi (HOST_WIDE_INT v) |
| { |
| if (char *ptr = write (1)) |
| { |
| if (v <= 0x3f && v >= -0x40) |
| *ptr = v & 0x7f; |
| else |
| { |
| unsigned bytes = 0; |
| HOST_WIDE_INT probe; |
| if (v >= 0) |
| for (probe = v >> 8; probe > 0x7; probe >>= 8) |
| bytes++; |
| else |
| for (probe = v >> 8; probe < -0x8; probe >>= 8) |
| bytes++; |
| *ptr = 0x80 | bytes << 4 | (probe & 0xf); |
| if ((ptr = write (++bytes))) |
| for (; bytes--; v >>= 8) |
| ptr[bytes] = v & 0xff; |
| } |
| } |
| } |
| |
| HOST_WIDE_INT |
| bytes_in::wi () |
| { |
| HOST_WIDE_INT v = 0; |
| if (const char *ptr = read (1)) |
| { |
| v = *ptr & 0xff; |
| if (v & 0x80) |
| { |
| unsigned bytes = (v >> 4) & 0x7; |
| v &= 0xf; |
| if (v & 0x8) |
| v |= -1 ^ 0x7; |
| /* unsigned necessary due to left shifts of -ve values. */ |
| unsigned HOST_WIDE_INT uv = (unsigned HOST_WIDE_INT) v; |
| if ((ptr = read (++bytes))) |
| while (bytes--) |
| uv = (uv << 8) | (*ptr++ & 0xff); |
| v = (HOST_WIDE_INT) uv; |
| } |
| else if (v & 0x40) |
| v |= -1 ^ 0x3f; |
| } |
| |
| return v; |
| } |
| |
| /* unsigned wide ints are just written as signed wide ints. */ |
| |
| inline void |
| bytes_out::wu (unsigned HOST_WIDE_INT v) |
| { |
| wi ((HOST_WIDE_INT) v); |
| } |
| |
| inline unsigned HOST_WIDE_INT |
| bytes_in::wu () |
| { |
| return (unsigned HOST_WIDE_INT) wi (); |
| } |
| |
| /* size_t written as unsigned or unsigned wide int. */ |
| |
| inline void |
| bytes_out::z (size_t s) |
| { |
| if (sizeof (s) == sizeof (unsigned)) |
| u (s); |
| else |
| wu (s); |
| } |
| |
| inline size_t |
| bytes_in::z () |
| { |
| if (sizeof (size_t) == sizeof (unsigned)) |
| return u (); |
| else |
| return wu (); |
| } |
| |
| /* Buffer simply memcpied. */ |
| void * |
| bytes_out::buf (size_t len) |
| { |
| align (sizeof (void *) * 2); |
| return write (len); |
| } |
| |
| void |
| bytes_out::buf (const void *src, size_t len) |
| { |
| if (void *ptr = buf (len)) |
| memcpy (ptr, src, len); |
| } |
| |
| const void * |
| bytes_in::buf (size_t len) |
| { |
| align (sizeof (void *) * 2); |
| const char *ptr = read (len); |
| |
| return ptr; |
| } |
| |
| /* strings as an size_t length, followed by the buffer. Make sure |
| there's a NUL terminator on read. */ |
| |
| void |
| bytes_out::str (const char *string, size_t len) |
| { |
| z (len); |
| if (len) |
| { |
| gcc_checking_assert (!string[len]); |
| buf (string, len + 1); |
| } |
| } |
| |
| const char * |
| bytes_in::str (size_t *len_p) |
| { |
| size_t len = z (); |
| |
| /* We're about to trust some user data. */ |
| if (overrun) |
| len = 0; |
| if (len_p) |
| *len_p = len; |
| const char *str = NULL; |
| if (len) |
| { |
| str = reinterpret_cast<const char *> (buf (len + 1)); |
| if (!str || str[len]) |
| { |
| set_overrun (); |
| str = NULL; |
| } |
| } |
| return str ? str : ""; |
| } |
| |
| cpp_hashnode * |
| bytes_in::cpp_node () |
| { |
| size_t len; |
| const char *s = str (&len); |
| if (!len) |
| return NULL; |
| return ::cpp_node (get_identifier_with_length (s, len)); |
| } |
| |
| /* Format a string directly to the buffer, including a terminating |
| NUL. Intended for human consumption. */ |
| |
| void |
| bytes_out::printf (const char *format, ...) |
| { |
| va_list args; |
| /* Exercise buffer expansion. */ |
| size_t len = EXPERIMENT (10, 500); |
| |
| while (char *ptr = write (len)) |
| { |
| va_start (args, format); |
| size_t actual = vsnprintf (ptr, len, format, args) + 1; |
| va_end (args); |
| if (actual <= len) |
| { |
| unuse (len - actual); |
| break; |
| } |
| unuse (len); |
| len = actual; |
| } |
| } |
| |
| void |
| bytes_out::print_time (const char *kind, const tm *time, const char *tz) |
| { |
| printf ("%stime: %4u/%02u/%02u %02u:%02u:%02u %s", |
| kind, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, |
| time->tm_hour, time->tm_min, time->tm_sec, tz); |
| } |
| |
| /* Encapsulated Lazy Records Of Named Declarations. |
| Header: Stunningly Elf32_Ehdr-like |
| Sections: Sectional data |
| [1-N) : User data sections |
| N .strtab : strings, stunningly ELF STRTAB-like |
| Index: Section table, stunningly ELF32_Shdr-like. */ |
| |
| class elf { |
| protected: |
| /* Constants used within the format. */ |
| enum private_constants { |
| /* File kind. */ |
| ET_NONE = 0, |
| EM_NONE = 0, |
| OSABI_NONE = 0, |
| |
| /* File format. */ |
| EV_CURRENT = 1, |
| CLASS32 = 1, |
| DATA2LSB = 1, |
| DATA2MSB = 2, |
| |
| /* Section numbering. */ |
| SHN_UNDEF = 0, |
| SHN_LORESERVE = 0xff00, |
| SHN_XINDEX = 0xffff, |
| |
| /* Section types. */ |
| SHT_NONE = 0, /* No contents. */ |
| SHT_PROGBITS = 1, /* Random bytes. */ |
| SHT_STRTAB = 3, /* A string table. */ |
| |
| /* Section flags. */ |
| SHF_NONE = 0x00, /* Nothing. */ |
| SHF_STRINGS = 0x20, /* NUL-Terminated strings. */ |
| |
| /* I really hope we do not get CMI files larger than 4GB. */ |
| MY_CLASS = CLASS32, |
| /* It is host endianness that is relevant. */ |
| MY_ENDIAN = DATA2LSB |
| #ifdef WORDS_BIGENDIAN |
| ^ DATA2LSB ^ DATA2MSB |
| #endif |
| }; |
| |
| public: |
| /* Constants visible to users. */ |
| enum public_constants { |
| /* Special error codes. Breaking layering a bit. */ |
| E_BAD_DATA = -1, /* Random unexpected data errors. */ |
| E_BAD_LAZY = -2, /* Badly ordered laziness. */ |
| E_BAD_IMPORT = -3 /* A nested import failed. */ |
| }; |
| |
| protected: |
| /* File identification. On-disk representation. */ |
| struct ident { |
| uint8_t magic[4]; /* 0x7f, 'E', 'L', 'F' */ |
| uint8_t klass; /* 4:CLASS32 */ |
| uint8_t data; /* 5:DATA2[LM]SB */ |
| uint8_t version; /* 6:EV_CURRENT */ |
| uint8_t osabi; /* 7:OSABI_NONE */ |
| uint8_t abiver; /* 8: 0 */ |
| uint8_t pad[7]; /* 9-15 */ |
| }; |
| /* File header. On-disk representation. */ |
| struct header { |
| struct ident ident; |
| uint16_t type; /* ET_NONE */ |
| uint16_t machine; /* EM_NONE */ |
| uint32_t version; /* EV_CURRENT */ |
| uint32_t entry; /* 0 */ |
| uint32_t phoff; /* 0 */ |
| uint32_t shoff; /* Section Header Offset in file */ |
| uint32_t flags; |
| uint16_t ehsize; /* ELROND Header SIZE -- sizeof (header) */ |
| uint16_t phentsize; /* 0 */ |
| uint16_t phnum; /* 0 */ |
| uint16_t shentsize; /* Section Header SIZE -- sizeof (section) */ |
| uint16_t shnum; /* Section Header NUM */ |
| uint16_t shstrndx; /* Section Header STRing iNDeX */ |
| }; |
| /* File section. On-disk representation. */ |
| struct section { |
| uint32_t name; /* String table offset. */ |
| uint32_t type; /* SHT_* */ |
| uint32_t flags; /* SHF_* */ |
| uint32_t addr; /* 0 */ |
| uint32_t offset; /* OFFSET in file */ |
| uint32_t size; /* SIZE of section */ |
| uint32_t link; /* 0 */ |
| uint32_t info; /* 0 */ |
| uint32_t addralign; /* 0 */ |
| uint32_t entsize; /* ENTry SIZE, usually 0 */ |
| }; |
| |
| protected: |
| data hdr; /* The header. */ |
| data sectab; /* The section table. */ |
| data strtab; /* String table. */ |
| int fd; /* File descriptor we're reading or writing. */ |
| int err; /* Sticky error code. */ |
| |
| public: |
| /* Construct from STREAM. E is errno if STREAM NULL. */ |
| elf (int fd, int e) |
| :hdr (), sectab (), strtab (), fd (fd), err (fd >= 0 ? 0 : e) |
| {} |
| ~elf () |
| { |
| gcc_checking_assert (fd < 0 && !hdr.buffer |
| && !sectab.buffer && !strtab.buffer); |
| } |
| |
| public: |
| /* Return the error, if we have an error. */ |
| int get_error () const |
| { |
| return err; |
| } |
| /* Set the error, unless it's already been set. */ |
| void set_error (int e = E_BAD_DATA) |
| { |
| if (!err) |
| err = e; |
| } |
| /* Get an error string. */ |
| const char *get_error (const char *) const; |
| |
| public: |
| /* Begin reading/writing file. Return false on error. */ |
| bool begin () const |
| { |
| return !get_error (); |
| } |
| /* Finish reading/writing file. Return false on error. */ |
| bool end (); |
| }; |
| |
| /* Return error string. */ |
| |
| const char * |
| elf::get_error (const char *name) const |
| { |
| if (!name) |
| return "Unknown CMI mapping"; |
| |
| switch (err) |
| { |
| case 0: |
| gcc_unreachable (); |
| case E_BAD_DATA: |
| return "Bad file data"; |
| case E_BAD_IMPORT: |
| return "Bad import dependency"; |
| case E_BAD_LAZY: |
| return "Bad lazy ordering"; |
| default: |
| return xstrerror (err); |
| } |
| } |
| |
| /* Finish file, return true if there's an error. */ |
| |
| bool |
| elf::end () |
| { |
| /* Close the stream and free the section table. */ |
| if (fd >= 0 && close (fd)) |
| set_error (errno); |
| fd = -1; |
| |
| return !get_error (); |
| } |
| |
| /* ELROND reader. */ |
| |
| class elf_in : public elf { |
| typedef elf parent; |
| |
| private: |
| /* For freezing & defrosting. */ |
| #if !defined (HOST_LACKS_INODE_NUMBERS) |
| dev_t device; |
| ino_t inode; |
| #endif |
| |
| public: |
| elf_in (int fd, int e) |
| :parent (fd, e) |
| { |
| } |
| ~elf_in () |
| { |
| } |
| |
| public: |
| bool is_frozen () const |
| { |
| return fd < 0 && hdr.pos; |
| } |
| bool is_freezable () const |
| { |
| return fd >= 0 && hdr.pos; |
| } |
| void freeze (); |
| bool defrost (const char *); |
| |
| /* If BYTES is in the mmapped area, allocate a new buffer for it. */ |
| void preserve (bytes_in &bytes ATTRIBUTE_UNUSED) |
| { |
| #if MAPPED_READING |
| if (hdr.buffer && bytes.buffer >= hdr.buffer |
| && bytes.buffer < hdr.buffer + hdr.pos) |
| { |
| char *buf = bytes.buffer; |
| bytes.buffer = data::simple_memory.grow (NULL, bytes.size); |
| memcpy (bytes.buffer, buf, bytes.size); |
| } |
| #endif |
| } |
| /* If BYTES is not in SELF's mmapped area, free it. SELF might be |
| NULL. */ |
| static void release (elf_in *self ATTRIBUTE_UNUSED, bytes_in &bytes) |
| { |
| #if MAPPED_READING |
| if (!(self && self->hdr.buffer && bytes.buffer >= self->hdr.buffer |
| && bytes.buffer < self->hdr.buffer + self->hdr.pos)) |
| #endif |
| data::simple_memory.shrink (bytes.buffer); |
| bytes.buffer = NULL; |
| bytes.size = 0; |
| } |
| |
| public: |
| static void grow (data &data, unsigned needed) |
| { |
| gcc_checking_assert (!data.buffer); |
| #if !MAPPED_READING |
| data.buffer = XNEWVEC (char, needed); |
| #endif |
| data.size = needed; |
| } |
| static void shrink (data &data) |
| { |
| #if !MAPPED_READING |
| XDELETEVEC (data.buffer); |
| #endif |
| data.buffer = NULL; |
| data.size = 0; |
| } |
| |
| public: |
| const section *get_section (unsigned s) const |
| { |
| if (s * sizeof (section) < sectab.size) |
| return reinterpret_cast<const section *> |
| (§ab.buffer[s * sizeof (section)]); |
| else |
| return NULL; |
| } |
| unsigned get_section_limit () const |
| { |
| return sectab.size / sizeof (section); |
| } |
| |
| protected: |
| const char *read (data *, unsigned, unsigned); |
| |
| public: |
| /* Read section by number. */ |
| bool read (data *d, const section *s) |
| { |
| return s && read (d, s->offset, s->size); |
| } |
| |
| /* Find section by name. */ |
| unsigned find (const char *name); |
| /* Find section by index. */ |
| const section *find (unsigned snum, unsigned type = SHT_PROGBITS); |
| |
| public: |
| /* Release the string table, when we're done with it. */ |
| void release () |
| { |
| shrink (strtab); |
| } |
| |
| public: |
| bool begin (location_t); |
| bool end () |
| { |
| release (); |
| #if MAPPED_READING |
| if (hdr.buffer) |
| munmap (hdr.buffer, hdr.pos); |
| hdr.buffer = NULL; |
| #endif |
| shrink (sectab); |
| |
| return parent::end (); |
| } |
| |
| public: |
| /* Return string name at OFFSET. Checks OFFSET range. Always |
| returns non-NULL. We know offset 0 is an empty string. */ |
| const char *name (unsigned offset) |
| { |
| return &strtab.buffer[offset < strtab.size ? offset : 0]; |
| } |
| }; |
| |
| /* ELROND writer. */ |
| |
| class elf_out : public elf, public data::allocator { |
| typedef elf parent; |
| /* Desired section alignment on disk. */ |
| static const int SECTION_ALIGN = 16; |
| |
| private: |
| ptr_int_hash_map identtab; /* Map of IDENTIFIERS to strtab offsets. */ |
| unsigned pos; /* Write position in file. */ |
| #if MAPPED_WRITING |
| unsigned offset; /* Offset of the mapping. */ |
| unsigned extent; /* Length of mapping. */ |
| unsigned page_size; /* System page size. */ |
| #endif |
| |
| public: |
| elf_out (int fd, int e) |
| :parent (fd, e), identtab (500), pos (0) |
| { |
| #if MAPPED_WRITING |
| offset = extent = 0; |
| page_size = sysconf (_SC_PAGE_SIZE); |
| if (page_size < SECTION_ALIGN) |
| /* Something really strange. */ |
| set_error (EINVAL); |
| #endif |
| } |
| ~elf_out () |
| { |
| data::simple_memory.shrink (hdr); |
| data::simple_memory.shrink (sectab); |
| data::simple_memory.shrink (strtab); |
| } |
| |
| #if MAPPED_WRITING |
| private: |
| void create_mapping (unsigned ext, bool extending = true); |
| void remove_mapping (); |
| #endif |
| |
| protected: |
| using allocator::grow; |
| virtual char *grow (char *, unsigned needed); |
| #if MAPPED_WRITING |
| using allocator::shrink; |
| virtual void shrink (char *); |
| #endif |
| |
| public: |
| unsigned get_section_limit () const |
| { |
| return sectab.pos / sizeof (section); |
| } |
| |
| protected: |
| unsigned add (unsigned type, unsigned name = 0, |
| unsigned off = 0, unsigned size = 0, unsigned flags = SHF_NONE); |
| unsigned write (const data &); |
| #if MAPPED_WRITING |
| unsigned write (const bytes_out &); |
| #endif |
| |
| public: |
| /* IDENTIFIER to strtab offset. */ |
| unsigned name (tree ident); |
| /* String literal to strtab offset. */ |
| unsigned name (const char *n); |
| /* Qualified name of DECL to strtab offset. */ |
| unsigned qualified_name (tree decl, bool is_defn); |
| |
| private: |
| unsigned strtab_write (const char *s, unsigned l); |
| void strtab_write (tree decl, int); |
| |
| public: |
| /* Add a section with contents or strings. */ |
| unsigned add (const bytes_out &, bool string_p, unsigned name); |
| |
| public: |
| /* Begin and end writing. */ |
| bool begin (); |
| bool end (); |
| }; |
| |
| /* Begin reading section NAME (of type PROGBITS) from SOURCE. |
| Data always checked for CRC. */ |
| |
| bool |
| bytes_in::begin (location_t loc, elf_in *source, const char *name) |
| { |
| unsigned snum = source->find (name); |
| |
| return begin (loc, source, snum, name); |
| } |
| |
| /* Begin reading section numbered SNUM with NAME (may be NULL). */ |
| |
| bool |
| bytes_in::begin (location_t loc, elf_in *source, unsigned snum, const char *name) |
| { |
| if (!source->read (this, source->find (snum)) |
| || !size || !check_crc ()) |
| { |
| source->set_error (elf::E_BAD_DATA); |
| source->shrink (*this); |
| if (name) |
| error_at (loc, "section %qs is missing or corrupted", name); |
| else |
| error_at (loc, "section #%u is missing or corrupted", snum); |
| return false; |
| } |
| pos = 4; |
| return true; |
| } |
| |
| /* Finish reading a section. */ |
| |
| bool |
| bytes_in::end (elf_in *src) |
| { |
| if (more_p ()) |
| set_overrun (); |
| if (overrun) |
| src->set_error (); |
| |
| src->shrink (*this); |
| |
| return !overrun; |
| } |
| |
| /* Begin writing buffer. */ |
| |
| void |
| bytes_out::begin (bool need_crc) |
| { |
| if (need_crc) |
| pos = 4; |
| memory->grow (*this, 0, false); |
| } |
| |
| /* Finish writing buffer. Stream out to SINK as named section NAME. |
| Return section number or 0 on failure. If CRC_PTR is true, crc |
| the data. Otherwise it is a string section. */ |
| |
| unsigned |
| bytes_out::end (elf_out *sink, unsigned name, unsigned *crc_ptr) |
| { |
| lengths[3] += pos; |
| spans[3]++; |
| |
| set_crc (crc_ptr); |
| unsigned sec_num = sink->add (*this, !crc_ptr, name); |
| memory->shrink (*this); |
| |
| return sec_num; |
| } |
| |
| /* Close and open the file, without destroying it. */ |
| |
| void |
| elf_in::freeze () |
| { |
| gcc_checking_assert (!is_frozen ()); |
| #if MAPPED_READING |
| if (munmap (hdr.buffer, hdr.pos) < 0) |
| set_error (errno); |
| #endif |
| if (close (fd) < 0) |
| set_error (errno); |
| fd = -1; |
| } |
| |
| bool |
| elf_in::defrost (const char *name) |
| { |
| gcc_checking_assert (is_frozen ()); |
| struct stat stat; |
| |
| fd = open (name, O_RDONLY | O_CLOEXEC | O_BINARY); |
| if (fd < 0 || fstat (fd, &stat) < 0) |
| set_error (errno); |
| else |
| { |
| bool ok = hdr.pos == unsigned (stat.st_size); |
| #ifndef HOST_LACKS_INODE_NUMBERS |
| if (device != stat.st_dev |
| || inode != stat.st_ino) |
| ok = false; |
| #endif |
| if (!ok) |
| set_error (EMFILE); |
| #if MAPPED_READING |
| if (ok) |
| { |
| char *mapping = reinterpret_cast<char *> |
| (mmap (NULL, hdr.pos, PROT_READ, MAP_SHARED, fd, 0)); |
| if (mapping == MAP_FAILED) |
| fail: |
| set_error (errno); |
| else |
| { |
| if (madvise (mapping, hdr.pos, MADV_RANDOM)) |
| goto fail; |
| |
| /* These buffers are never NULL in this case. */ |
| strtab.buffer = mapping + strtab.pos; |
| sectab.buffer = mapping + sectab.pos; |
| hdr.buffer = mapping; |
| } |
| } |
| #endif |
| } |
| |
| return !get_error (); |
| } |
| |
| /* Read at current position into BUFFER. Return true on success. */ |
| |
| const char * |
| elf_in::read (data *data, unsigned pos, unsigned length) |
| { |
| #if MAPPED_READING |
| if (pos + length > hdr.pos) |
| { |
| set_error (EINVAL); |
| return NULL; |
| } |
| #else |
| if (pos != ~0u && lseek (fd, pos, SEEK_SET) < 0) |
| { |
| set_error (errno); |
| return NULL; |
| } |
| #endif |
| grow (*data, length); |
| #if MAPPED_READING |
| data->buffer = hdr.buffer + pos; |
| #else |
| if (::read (fd, data->buffer, data->size) != ssize_t (length)) |
| { |
| set_error (errno); |
| shrink (*data); |
| return NULL; |
| } |
| #endif |
| |
| return data->buffer; |
| } |
| |
| /* Read section SNUM of TYPE. Return section pointer or NULL on error. */ |
| |
| const elf::section * |
| elf_in::find (unsigned snum, unsigned type) |
| { |
| const section *sec = get_section (snum); |
| if (!snum || !sec || sec->type != type) |
| return NULL; |
| return sec; |
| } |
| |
| /* Find a section NAME and TYPE. Return section number, or zero on |
| failure. */ |
| |
| unsigned |
| elf_in::find (const char *sname) |
| { |
| for (unsigned pos = sectab.size; pos -= sizeof (section); ) |
| { |
| const section *sec |
| = reinterpret_cast<const section *> (§ab.buffer[pos]); |
| |
| if (0 == strcmp (sname, name (sec->name))) |
| return pos / sizeof (section); |
| } |
| |
| return 0; |
| } |
| |
| /* Begin reading file. Verify header. Pull in section and string |
| tables. Return true on success. */ |
| |
| bool |
| elf_in::begin (location_t loc) |
| { |
| if (!parent::begin ()) |
| return false; |
| |
| struct stat stat; |
| unsigned size = 0; |
| if (!fstat (fd, &stat)) |
| { |
| #if !defined (HOST_LACKS_INODE_NUMBERS) |
| device = stat.st_dev; |
| inode = stat.st_ino; |
| #endif |
| /* Never generate files > 4GB, check we've not been given one. */ |
| if (stat.st_size == unsigned (stat.st_size)) |
| size = unsigned (stat.st_size); |
| } |
| |
| #if MAPPED_READING |
| /* MAP_SHARED so that the file is backing store. If someone else |
| concurrently writes it, they're wrong. */ |
| void *mapping = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); |
| if (mapping == MAP_FAILED) |
| { |
| fail: |
| set_error (errno); |
| return false; |
| } |
| /* We'll be hopping over this randomly. Some systems declare the |
| first parm as char *, and other declare it as void *. */ |
| if (madvise (reinterpret_cast <char *> (mapping), size, MADV_RANDOM)) |
| goto fail; |
| |
| hdr.buffer = (char *)mapping; |
| #else |
| read (&hdr, 0, sizeof (header)); |
| #endif |
| hdr.pos = size; /* Record size of the file. */ |
| |
| const header *h = reinterpret_cast<const header *> (hdr.buffer); |
| if (!h) |
| return false; |
| |
| if (h->ident.magic[0] != 0x7f |
| || h->ident.magic[1] != 'E' |
| || h->ident.magic[2] != 'L' |
| || h->ident.magic[3] != 'F') |
| { |
| error_at (loc, "not Encapsulated Lazy Records of Named Declarations"); |
| failed: |
| shrink (hdr); |
| return false; |
| } |
| |
| /* We expect a particular format -- the ELF is not intended to be |
| distributable. */ |
| if (h->ident.klass != MY_CLASS |
| || h->ident.data != MY_ENDIAN |
| || h->ident.version != EV_CURRENT |
| || h->type != ET_NONE |
| || h->machine != EM_NONE |
| || h->ident.osabi != OSABI_NONE) |
| { |
| error_at (loc, "unexpected encapsulation format or type"); |
| goto failed; |
| } |
| |
| int e = -1; |
| if (!h->shoff || h->shentsize != sizeof (section)) |
| { |
| malformed: |
| set_error (e); |
| error_at (loc, "encapsulation is malformed"); |
| goto failed; |
| } |
| |
| unsigned strndx = h->shstrndx; |
| unsigned shnum = h->shnum; |
| if (shnum == SHN_XINDEX) |
| { |
| if (!read (§ab, h->shoff, sizeof (section))) |
| { |
| section_table_fail: |
| e = errno; |
| goto malformed; |
| } |
| shnum = get_section (0)->size; |
| /* Freeing does mean we'll re-read it in the case we're not |
| mapping, but this is going to be rare. */ |
| shrink (sectab); |
| } |
| |
| if (!shnum) |
| goto malformed; |
| |
| if (!read (§ab, h->shoff, shnum * sizeof (section))) |
| goto section_table_fail; |
| |
| if (strndx == SHN_XINDEX) |
| strndx = get_section (0)->link; |
| |
| if (!read (&strtab, find (strndx, SHT_STRTAB))) |
| goto malformed; |
| |
| /* The string table should be at least one byte, with NUL chars |
| at either end. */ |
| if (!(strtab.size && !strtab.buffer[0] |
| && !strtab.buffer[strtab.size - 1])) |
| goto malformed; |
| |
| #if MAPPED_READING |
| /* Record the offsets of the section and string tables. */ |
| sectab.pos = h->shoff; |
| strtab.pos = shnum * sizeof (section); |
| #else |
| shrink (hdr); |
| #endif |
| |
| return true; |
| } |
| |
| /* Create a new mapping. */ |
| |
| #if MAPPED_WRITING |
| void |
| elf_out::create_mapping (unsigned ext, bool extending) |
| { |
| #ifndef HAVE_POSIX_FALLOCATE |
| #define posix_fallocate(fd,off,len) ftruncate (fd, off + len) |
| #endif |
| void *mapping = MAP_FAILED; |
| if (extending && ext < 1024 * 1024) |
| { |
| if (!posix_fallocate (fd, offset, ext * 2)) |
| mapping = mmap (NULL, ext * 2, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, offset); |
| if (mapping != MAP_FAILED) |
| ext *= 2; |
| } |
| if (mapping == MAP_FAILED) |
| { |
| if (!extending || !posix_fallocate (fd, offset, ext)) |
| mapping = mmap (NULL, ext, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, offset); |
| if (mapping == MAP_FAILED) |
| { |
| set_error (errno); |
| mapping = NULL; |
| ext = 0; |
| } |
| } |
| #undef posix_fallocate |
| hdr.buffer = (char *)mapping; |
| extent = ext; |
| } |
| #endif |
| |
| /* Flush out the current mapping. */ |
| |
| #if MAPPED_WRITING |
| void |
| elf_out::remove_mapping () |
| { |
| if (hdr.buffer) |
| { |
| /* MS_ASYNC dtrt with the removed mapping, including a |
| subsequent overlapping remap. */ |
| if (msync (hdr.buffer, extent, MS_ASYNC) |
| || munmap (hdr.buffer, extent)) |
| /* We're somewhat screwed at this point. */ |
| set_error (errno); |
| } |
| |
| hdr.buffer = NULL; |
| } |
| #endif |
| |
| /* Grow a mapping of PTR to be NEEDED bytes long. This gets |
| interesting if the new size grows the EXTENT. */ |
| |
| char * |
| elf_out::grow (char *data, unsigned needed) |
| { |
| if (!data) |
| { |
| /* First allocation, check we're aligned. */ |
| gcc_checking_assert (!(pos & (SECTION_ALIGN - 1))); |
| #if MAPPED_WRITING |
| data = hdr.buffer + (pos - offset); |
| #endif |
| } |
| |
| #if MAPPED_WRITING |
| unsigned off = data - hdr.buffer; |
| if (off + needed > extent) |
| { |
| /* We need to grow the mapping. */ |
| unsigned lwm = off & ~(page_size - 1); |
| unsigned hwm = (off + needed + page_size - 1) & ~(page_size - 1); |
| |
| gcc_checking_assert (hwm > extent); |
| |
| remove_mapping (); |
| |
| offset += lwm; |
| create_mapping (extent < hwm - lwm ? hwm - lwm : extent); |
| |
| data = hdr.buffer + (off - lwm); |
| } |
| #else |
| data = allocator::grow (data, needed); |
| #endif |
| |
| return data; |
| } |
| |
| #if MAPPED_WRITING |
| /* Shrinking is a NOP. */ |
| void |
| elf_out::shrink (char *) |
| { |
| } |
| #endif |
| |
| /* Write S of length L to the strtab buffer. L must include the ending |
| NUL, if that's what you want. */ |
| |
| unsigned |
| elf_out::strtab_write (const char *s, unsigned l) |
| { |
| if (strtab.pos + l > strtab.size) |
| data::simple_memory.grow (strtab, strtab.pos + l, false); |
| memcpy (strtab.buffer + strtab.pos, s, l); |
| unsigned res = strtab.pos; |
| strtab.pos += l; |
| return res; |
| } |
| |
| /* Write qualified name of decl. INNER >0 if this is a definition, <0 |
| if this is a qualifier of an outer name. */ |
| |
| void |
| elf_out::strtab_write (tree decl, int inner) |
| { |
| tree ctx = CP_DECL_CONTEXT (decl); |
| if (TYPE_P (ctx)) |
| ctx = TYPE_NAME (ctx); |
| if (ctx != global_namespace) |
| strtab_write (ctx, -1); |
| |
| tree name = DECL_NAME (decl); |
| if (!name) |
| name = DECL_ASSEMBLER_NAME_RAW (decl); |
| strtab_write (IDENTIFIER_POINTER (name), IDENTIFIER_LENGTH (name)); |
| |
| if (inner) |
| strtab_write (&"::{}"[inner+1], 2); |
| } |
| |
| /* Map IDENTIFIER IDENT to strtab offset. Inserts into strtab if not |
| already there. */ |
| |
| unsigned |
| elf_out::name (tree ident) |
| { |
| unsigned res = 0; |
| if (ident) |
| { |
| bool existed; |
| int *slot = &identtab.get_or_insert (ident, &existed); |
| if (!existed) |
| *slot = strtab_write (IDENTIFIER_POINTER (ident), |
| IDENTIFIER_LENGTH (ident) + 1); |
| res = *slot; |
| } |
| return res; |
| } |
| |
| /* Map LITERAL to strtab offset. Does not detect duplicates and |
| expects LITERAL to remain live until strtab is written out. */ |
| |
| unsigned |
| elf_out::name (const char *literal) |
| { |
| return strtab_write (literal, strlen (literal) + 1); |
| } |
| |
| /* Map a DECL's qualified name to strtab offset. Does not detect |
| duplicates. */ |
| |
| unsigned |
| elf_out::qualified_name (tree decl, bool is_defn) |
| { |
| gcc_checking_assert (DECL_P (decl) && decl != global_namespace); |
| unsigned result = strtab.pos; |
| |
| strtab_write (decl, is_defn); |
| strtab_write ("", 1); |
| |
| return result; |
| } |
| |
| /* Add section to file. Return section number. TYPE & NAME identify |
| the section. OFF and SIZE identify the file location of its |
| data. FLAGS contains additional info. */ |
| |
| unsigned |
| elf_out::add (unsigned type, unsigned name, unsigned off, unsigned size, |
| unsigned flags) |
| { |
| gcc_checking_assert (!(off & (SECTION_ALIGN - 1))); |
| if (sectab.pos + sizeof (section) > sectab.size) |
| data::simple_memory.grow (sectab, sectab.pos + sizeof (section), false); |
| section *sec = reinterpret_cast<section *> (sectab.buffer + sectab.pos); |
| memset (sec, 0, sizeof (section)); |
| sec->type = type; |
| sec->flags = flags; |
| sec->name = name; |
| sec->offset = off; |
| sec->size = size; |
| if (flags & SHF_STRINGS) |
| sec->entsize = 1; |
| |
| unsigned res = sectab.pos; |
| sectab.pos += sizeof (section); |
| return res / sizeof (section); |
| } |
| |
| /* Pad to the next alignment boundary, then write BUFFER to disk. |
| Return the position of the start of the write, or zero on failure. */ |
| |
| unsigned |
| elf_out::write (const data &buffer) |
| { |
| #if MAPPED_WRITING |
| /* HDR is always mapped. */ |
| if (&buffer != &hdr) |
| { |
| bytes_out out (this); |
| grow (out, buffer.pos, true); |
| if (out.buffer) |
| memcpy (out.buffer, buffer.buffer, buffer.pos); |
| shrink (out); |
| } |
| else |
| /* We should have been aligned during the first allocation. */ |
| gcc_checking_assert (!(pos & (SECTION_ALIGN - 1))); |
| #else |
| if (::write (fd, buffer.buffer, buffer.pos) != ssize_t (buffer.pos)) |
| { |
| set_error (errno); |
| return 0; |
| } |
| #endif |
| unsigned res = pos; |
| pos += buffer.pos; |
| |
| if (unsigned padding = -pos & (SECTION_ALIGN - 1)) |
| { |
| #if !MAPPED_WRITING |
| /* Align the section on disk, should help the necessary copies. |
| fseeking to extend is non-portable. */ |
| static char zero[SECTION_ALIGN]; |
| if (::write (fd, &zero, padding) != ssize_t (padding)) |
| set_error (errno); |
| #endif |
| pos += padding; |
| } |
| return res; |
| } |
| |
| /* Write a streaming buffer. It must be using us as an allocator. */ |
| |
| #if MAPPED_WRITING |
| unsigned |
| elf_out::write (const bytes_out &buf) |
| { |
| gcc_checking_assert (buf.memory == this); |
| /* A directly mapped buffer. */ |
| gcc_checking_assert (buf.buffer - hdr.buffer >= 0 |
| && buf.buffer - hdr.buffer + buf.size <= extent); |
| unsigned res = pos; |
| pos += buf.pos; |
| |
| /* Align up. We're not going to advance into the next page. */ |
| pos += -pos & (SECTION_ALIGN - 1); |
| |
| return res; |
| } |
| #endif |
| |
| /* Write data and add section. STRING_P is true for a string |
| section, false for PROGBITS. NAME identifies the section (0 is the |
| empty name). DATA is the contents. Return section number or 0 on |
| failure (0 is the undef section). */ |
| |
| unsigned |
| elf_out::add (const bytes_out &data, bool string_p, unsigned name) |
| { |
| unsigned off = write (data); |
| |
| return add (string_p ? SHT_STRTAB : SHT_PROGBITS, name, |
| off, data.pos, string_p ? SHF_STRINGS : SHF_NONE); |
| } |
| |
| /* Begin writing the file. Initialize the section table and write an |
| empty header. Return false on failure. */ |
| |
| bool |
| elf_out::begin () |
| { |
| if (!parent::begin ()) |
| return false; |
| |
| /* Let the allocators pick a default. */ |
| data::simple_memory.grow (strtab, 0, false); |
| data::simple_memory.grow (sectab, 0, false); |
| |
| /* The string table starts with an empty string. */ |
| name (""); |
| |
| /* Create the UNDEF section. */ |
| add (SHT_NONE); |
| |
| #if MAPPED_WRITING |
| /* Start a mapping. */ |
| create_mapping (EXPERIMENT (page_size, |
| (32767 + page_size) & ~(page_size - 1))); |
| if (!hdr.buffer) |
| return false; |
| #endif |
| |
| /* Write an empty header. */ |
| grow (hdr, sizeof (header), true); |
| header *h = reinterpret_cast<header *> (hdr.buffer); |
| memset (h, 0, sizeof (header)); |
| hdr.pos = hdr.size; |
| write (hdr); |
| return !get_error (); |
| } |
| |
| /* Finish writing the file. Write out the string & section tables. |
| Fill in the header. Return true on error. */ |
| |
| bool |
| elf_out::end () |
| { |
| if (fd >= 0) |
| { |
| /* Write the string table. */ |
| unsigned strnam = name (".strtab"); |
| unsigned stroff = write (strtab); |
| unsigned strndx = add (SHT_STRTAB, strnam, stroff, strtab.pos, |
| SHF_STRINGS); |
| |
| /* Store escape values in section[0]. */ |
| if (strndx >= SHN_LORESERVE) |
| { |
| reinterpret_cast<section *> (sectab.buffer)->link = strndx; |
| strndx = SHN_XINDEX; |
| } |
| unsigned shnum = sectab.pos / sizeof (section); |
| if (shnum >= SHN_LORESERVE) |
| { |
| reinterpret_cast<section *> (sectab.buffer)->size = shnum; |
| shnum = SHN_XINDEX; |
| } |
| |
| unsigned shoff = write (sectab); |
| |
| #if MAPPED_WRITING |
| if (offset) |
| { |
| remove_mapping (); |
| offset = 0; |
| create_mapping ((sizeof (header) + page_size - 1) & ~(page_size - 1), |
| false); |
| } |
| unsigned length = pos; |
| #else |
| if (lseek (fd, 0, SEEK_SET) < 0) |
| set_error (errno); |
| #endif |
| /* Write header. */ |
| if (!get_error ()) |
| { |
| /* Write the correct header now. */ |
| header *h = reinterpret_cast<header *> (hdr.buffer); |
| h->ident.magic[0] = 0x7f; |
| h->ident.magic[1] = 'E'; /* Elrond */ |
| h->ident.magic[2] = 'L'; /* is an */ |
| h->ident.magic[3] = 'F'; /* elf. */ |
| h->ident.klass = MY_CLASS; |
| h->ident.data = MY_ENDIAN; |
| h->ident.version = EV_CURRENT; |
| h->ident.osabi = OSABI_NONE; |
| h->type = ET_NONE; |
| h->machine = EM_NONE; |
| h->version = EV_CURRENT; |
| h->shoff = shoff; |
| h->ehsize = sizeof (header); |
| h->shentsize = sizeof (section); |
| h->shnum = shnum; |
| h->shstrndx = strndx; |
| |
| pos = 0; |
| write (hdr); |
| } |
| |
| #if MAPPED_WRITING |
| remove_mapping (); |
| if (ftruncate (fd, length)) |
| set_error (errno); |
| #endif |
| } |
| |
| data::simple_memory.shrink (sectab); |
| data::simple_memory.shrink (strtab); |
| |
| return parent::end (); |
| } |
| |
| /********************************************************************/ |
| |
| /* A dependency set. This is used during stream out to determine the |
| connectivity of the graph. Every namespace-scope declaration that |
| needs writing has a depset. The depset is filled with the (depsets |
| of) declarations within this module that it references. For a |
| declaration that'll generally be named types. For definitions |
| it'll also be declarations in the body. |
| |
| From that we can convert the graph to a DAG, via determining the |
| Strongly Connected Clusters. Each cluster is streamed |
| independently, and thus we achieve lazy loading. |
| |
| Other decls that get a depset are namespaces themselves and |
| unnameable declarations. */ |
| |
| class depset { |
| private: |
| tree entity; /* Entity, or containing namespace. */ |
| uintptr_t discriminator; /* Flags or identifier. */ |
| |
| public: |
| /* The kinds of entity the depset could describe. The ordering is |
| significant, see entity_kind_name. */ |
| enum entity_kind |
| { |
| EK_DECL, /* A decl. */ |
| EK_SPECIALIZATION, /* A specialization. */ |
| EK_PARTIAL, /* A partial specialization. */ |
| EK_USING, /* A using declaration (at namespace scope). */ |
| EK_NAMESPACE, /* A namespace. */ |
| EK_REDIRECT, /* Redirect to a template_decl. */ |
| EK_EXPLICIT_HWM, |
| EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded. */ |
| EK_FOR_BINDING, /* A decl being inserted for a binding. */ |
| EK_INNER_DECL, /* A decl defined outside of it's imported |
| context. */ |
| EK_DIRECT_HWM = EK_PARTIAL + 1, |
| |
| EK_BITS = 3 /* Only need to encode below EK_EXPLICIT_HWM. */ |
| }; |
| |
| private: |
| /* Placement of bit fields in discriminator. */ |
| enum disc_bits |
| { |
| DB_ZERO_BIT, /* Set to disambiguate identifier from flags */ |
| DB_SPECIAL_BIT, /* First dep slot is special. */ |
| DB_KIND_BIT, /* Kind of the entity. */ |
| DB_KIND_BITS = EK_BITS, |
| DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS, |
| DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ |
| DB_IS_INTERNAL_BIT, /* It is an (erroneous) |
| internal-linkage entity. */ |
| DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage |
| entity. */ |
| DB_IMPORTED_BIT, /* An imported entity. */ |
| DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ |
| DB_HIDDEN_BIT, /* A hidden binding. */ |
| /* The following bits are not independent, but enumerating them is |
| awkward. */ |
| DB_ALIAS_TMPL_INST_BIT, /* An alias template instantiation. */ |
| DB_ALIAS_SPEC_BIT, /* Specialization of an alias template |
| (in both spec tables). */ |
| DB_TYPE_SPEC_BIT, /* Specialization in the type table. |
| */ |
| DB_FRIEND_SPEC_BIT, /* An instantiated template friend. */ |
| }; |
| |
| public: |
| /* The first slot is special for EK_SPECIALIZATIONS it is a |
| spec_entry pointer. It is not relevant for the SCC |
| determination. */ |
| vec<depset *> deps; /* Depsets we reference. */ |
| |
| public: |
| unsigned cluster; /* Strongly connected cluster, later entity number */ |
| unsigned section; /* Section written to. */ |
| /* During SCC construction, section is lowlink, until the depset is |
| removed from the stack. See Tarjan algorithm for details. */ |
| |
| private: |
| /* Construction via factories. Destruction via hash traits. */ |
| depset (tree entity); |
| ~depset (); |
| |
| public: |
| static depset *make_binding (tree, tree); |
| static depset *make_entity (tree, entity_kind, bool = false); |
| /* Late setting a binding name -- /then/ insert into hash! */ |
| inline void set_binding_name (tree name) |
| { |
| gcc_checking_assert (!get_name ()); |
| discriminator = reinterpret_cast<uintptr_t> (name); |
| } |
| |
| private: |
| template<unsigned I> void set_flag_bit () |
| { |
| gcc_checking_assert (I < 2 || !is_binding ()); |
| discriminator |= 1u << I; |
| } |
| template<unsigned I> void clear_flag_bit () |
| { |
| gcc_checking_assert (I < 2 || !is_binding ()); |
| discriminator &= ~(1u << I); |
| } |
| template<unsigned I> bool get_flag_bit () const |
| { |
| gcc_checking_assert (I < 2 || !is_binding ()); |
| return bool ((discriminator >> I) & 1); |
| } |
| |
| public: |
| bool is_binding () const |
| { |
| return !get_flag_bit<DB_ZERO_BIT> (); |
| } |
| entity_kind get_entity_kind () const |
| { |
| if (is_binding ()) |
| return EK_BINDING; |
| return entity_kind ((discriminator >> DB_KIND_BIT) & ((1u << EK_BITS) - 1)); |
| } |
| const char *entity_kind_name () const; |
| |
| public: |
| bool has_defn () const |
| { |
| return get_flag_bit<DB_DEFN_BIT> (); |
| } |
| |
| public: |
| /* This class-member is defined here, but the class was imported. */ |
| bool is_member () const |
| { |
| gcc_checking_assert (get_entity_kind () == EK_DECL); |
| return get_flag_bit<DB_IS_MEMBER_BIT> (); |
| } |
| public: |
| bool is_internal () const |
| { |
| return get_flag_bit<DB_IS_INTERNAL_BIT> (); |
| } |
| bool refs_internal () const |
| { |
| return get_flag_bit<DB_REFS_INTERNAL_BIT> (); |
| } |
| bool is_import () const |
| { |
| return get_flag_bit<DB_IMPORTED_BIT> (); |
| } |
| bool is_unreached () const |
| { |
| return get_flag_bit<DB_UNREACHED_BIT> (); |
| } |
| bool is_alias_tmpl_inst () const |
| { |
| return get_flag_bit<DB_ALIAS_TMPL_INST_BIT> (); |
| } |
| bool is_alias () const |
| { |
| return get_flag_bit<DB_ALIAS_SPEC_BIT> (); |
| } |
| bool is_hidden () const |
| { |
| return get_flag_bit<DB_HIDDEN_BIT> (); |
| } |
| bool is_type_spec () const |
| { |
| return get_flag_bit<DB_TYPE_SPEC_BIT> (); |
| } |
| bool is_friend_spec () const |
| { |
| return get_flag_bit<DB_FRIEND_SPEC_BIT> (); |
| } |
| |
| public: |
| /* We set these bit outside of depset. */ |
| void set_hidden_binding () |
| { |
| set_flag_bit<DB_HIDDEN_BIT> (); |
| } |
| void clear_hidden_binding () |
| { |
| clear_flag_bit<DB_HIDDEN_BIT> (); |
| } |
| |
| public: |
| bool is_special () const |
| { |
| return get_flag_bit<DB_SPECIAL_BIT> (); |
| } |
| void set_special () |
| { |
| set_flag_bit<DB_SPECIAL_BIT> (); |
| } |
| |
| public: |
| tree get_entity () const |
| { |
| return entity; |
| } |
| tree get_name () const |
| { |
| gcc_checking_assert (is_binding ()); |
| return reinterpret_cast <tree> (discriminator); |
| } |
| |
| public: |
| /* Traits for a hash table of pointers to bindings. */ |
| struct traits { |
| /* Each entry is a pointer to a depset. */ |
| typedef depset *value_type; |
| /* We lookup by container:maybe-identifier pair. */ |
| typedef std::pair<tree,tree> compare_type; |
| |
| static const bool empty_zero_p = true; |
| |
| /* hash and equality for compare_type. */ |
| inline static hashval_t hash (const compare_type &p) |
| { |
| hashval_t h = pointer_hash<tree_node>::hash (p.first); |
| if (p.second) |
| { |
| hashval_t nh = IDENTIFIER_HASH_VALUE (p.second); |
| h = iterative_hash_hashval_t (h, nh); |
| } |
| return h; |
| } |
| inline static bool equal (const value_type b, const compare_type &p) |
| { |
| if (b->entity != p.first) |
| return false; |
| |
| if (p.second) |
| return b->discriminator == reinterpret_cast<uintptr_t> (p.second); |
| else |
| return !b->is_binding (); |
| } |
| |
| /* (re)hasher for a binding itself. */ |
| inline static hashval_t hash (const value_type b) |
| { |
| hashval_t h = pointer_hash<tree_node>::hash (b->entity); |
| if (b->is_binding ()) |
| { |
| hashval_t nh = IDENTIFIER_HASH_VALUE (b->get_name ()); |
| h = iterative_hash_hashval_t (h, nh); |
| } |
| return h; |
| } |
| |
| /* Empty via NULL. */ |
| static inline void mark_empty (value_type &p) {p = NULL;} |
| static inline bool is_empty (value_type p) {return !p;} |
| |
| /* Nothing is deletable. Everything is insertable. */ |
| static bool is_deleted (value_type) { return false; } |
| static void mark_deleted (value_type) { gcc_unreachable (); } |
| |
| /* We own the entities in the hash table. */ |
| static void remove (value_type p) |
| { |
| delete (p); |
| } |
| }; |
| |
| public: |
| class hash : public hash_table<traits> { |
| typedef traits::compare_type key_t; |
| typedef hash_table<traits> parent; |
| |
| public: |
| vec<depset *> worklist; /* Worklist of decls to walk. */ |
| hash *chain; /* Original table. */ |
| depset *current; /* Current depset being depended. */ |
| unsigned section; /* When writing out, the section. */ |
| bool sneakoscope; /* Detecting dark magic (of a voldemort). */ |
| bool reached_unreached; /* We reached an unreached entity. */ |
| |
| public: |
| hash (size_t size, hash *c = NULL) |
| : parent (size), chain (c), current (NULL), section (0), |
| sneakoscope (false), reached_unreached (false) |
| { |
| worklist.create (size); |
| } |
| ~hash () |
| { |
| worklist.release (); |
| } |
| |
| public: |
| bool is_key_order () const |
| { |
| return chain != NULL; |
| } |
| |
| private: |
| depset **entity_slot (tree entity, bool = true); |
| depset **binding_slot (tree ctx, tree name, bool = true); |
| depset *maybe_add_declaration (tree decl); |
| |
| public: |
| depset *find_dependency (tree entity); |
| depset *find_binding (tree ctx, tree name); |
| depset *make_dependency (tree decl, entity_kind); |
| void add_dependency (depset *); |
| |
| public: |
| void add_mergeable (depset *); |
| depset *add_dependency (tree decl, entity_kind); |
| void add_namespace_context (depset *, tree ns); |
| |
| private: |
| static bool add_binding_entity (tree, WMB_Flags, void *); |
| |
| public: |
| bool add_namespace_entities (tree ns, bitmap partitions); |
| void add_specializations (bool decl_p); |
| void add_partial_entities (vec<tree, va_gc> *); |
| void add_class_entities (vec<tree, va_gc> *); |
| |
| public: |
| void find_dependencies (module_state *); |
| bool finalize_dependencies (); |
| vec<depset *> connect (); |
| }; |
| |
| public: |
| struct tarjan { |
| vec<depset *> result; |
| vec<depset *> stack; |
| unsigned index; |
| |
| tarjan (unsigned size) |
| : index (0) |
| { |
| result.create (size); |
| stack.create (50); |
| } |
| ~tarjan () |
| { |
| gcc_assert (!stack.length ()); |
| stack.release (); |
| } |
| |
| public: |
| void connect (depset *); |
| }; |
| }; |
| |
| inline |
| depset::depset (tree entity) |
| :entity (entity), discriminator (0), cluster (0), section (0) |
| { |
| deps.create (0); |
| } |
| |
| inline |
| depset::~depset () |
| { |
| deps.release (); |
| } |
| |
| const char * |
| depset::entity_kind_name () const |
| { |
| /* Same order as entity_kind. */ |
| static const char *const names[] = |
| {"decl", "specialization", "partial", "using", |
| "namespace", "redirect", "binding"}; |
| entity_kind kind = get_entity_kind (); |
| gcc_checking_assert (kind < sizeof (names) / sizeof(names[0])); |
| return names[kind]; |
| } |
| |
| /* Create a depset for a namespace binding NS::NAME. */ |
| |
| depset *depset::make_binding (tree ns, tree name) |
| { |
| depset *binding = new depset (ns); |
| |
| binding->discriminator = reinterpret_cast <uintptr_t> (name); |
| |
| return binding; |
| } |
| |
| depset *depset::make_entity (tree entity, entity_kind ek, bool is_defn) |
| { |
| depset *r = new depset (entity); |
| |
| r->discriminator = ((1 << DB_ZERO_BIT) |
| | (ek << DB_KIND_BIT) |
| | is_defn << DB_DEFN_BIT); |
| |
| return r; |
| } |
| |
| class pending_key |
| { |
| public: |
| tree ns; |
| tree id; |
| }; |
| |
| template<> |
| struct default_hash_traits<pending_key> |
| { |
| using value_type = pending_key; |
| |
| static const bool empty_zero_p = false; |
| static hashval_t hash (const value_type &k) |
| { |
| hashval_t h = IDENTIFIER_HASH_VALUE (k.id); |
| h = iterative_hash_hashval_t (DECL_UID (k.ns), h); |
| |
| return h; |
| } |
| static bool equal (const value_type &k, const value_type &l) |
| { |
| return k.ns == l.ns && k.id == l.id; |
| } |
| static void mark_empty (value_type &k) |
| { |
| k.ns = k.id = NULL_TREE; |
| } |
| static void mark_deleted (value_type &k) |
| { |
| k.ns = NULL_TREE; |
| gcc_checking_assert (k.id); |
| } |
| static bool is_empty (const value_type &k) |
| { |
| return k.ns == NULL_TREE && k.id == NULL_TREE; |
| } |
| static bool is_deleted (const value_type &k) |
| { |
| return k.ns == NULL_TREE && k.id != NULL_TREE; |
| } |
| static void remove (value_type &) |
| { |
| } |
| }; |
| |
| typedef hash_map<pending_key, auto_vec<unsigned>> pending_map_t; |
| |
| /* Not-loaded entities that are keyed to a namespace-scope |
| identifier. See module_state::write_pendings for details. */ |
| pending_map_t *pending_table; |
| |
| /* Decls that need some post processing once a batch of lazy loads has |
| completed. */ |
| vec<tree, va_heap, vl_embed> *post_load_decls; |
| |
| /* Some entities are attached to another entitity for ODR purposes. |
| For example, at namespace scope, 'inline auto var = []{};', that |
| lambda is attached to 'var', and follows its ODRness. */ |
| typedef hash_map<tree, auto_vec<tree>> attached_map_t; |
| static attached_map_t *attached_table; |
| |
| /********************************************************************/ |
| /* Tree streaming. The tree streaming is very specific to the tree |
| structures themselves. A tag indicates the kind of tree being |
| streamed. -ve tags indicate backreferences to already-streamed |
| trees. Backreferences are auto-numbered. */ |
| |
| /* Tree tags. */ |
| enum tree_tag { |
| tt_null, /* NULL_TREE. */ |
| tt_fixed, /* Fixed vector index. */ |
| |
| tt_node, /* By-value node. */ |
| tt_decl, /* By-value mergeable decl. */ |
| tt_tpl_parm, /* Template parm. */ |
| |
| /* The ordering of the following 4 is relied upon in |
| trees_out::tree_node. */ |
| tt_id, /* Identifier node. */ |
| tt_conv_id, /* Conversion operator name. */ |
| tt_anon_id, /* Anonymous name. */ |
| tt_lambda_id, /* Lambda name. */ |
| |
| tt_typedef_type, /* A (possibly implicit) typedefed type. */ |
| tt_derived_type, /* A type derived from another type. */ |
| tt_variant_type, /* A variant of another type. */ |
| |
| tt_tinfo_var, /* Typeinfo object. */ |
| tt_tinfo_typedef, /* Typeinfo typedef. */ |
| tt_ptrmem_type, /* Pointer to member type. */ |
| |
| tt_parm, /* Function parameter or result. */ |
| tt_enum_value, /* An enum value. */ |
| tt_enum_decl, /* An enum decl. */ |
| tt_data_member, /* Data member/using-decl. */ |
| |
| tt_binfo, /* A BINFO. */ |
| tt_vtable, /* A vtable. */ |
| tt_thunk, /* A thunk. */ |
| tt_clone_ref, |
| |
| tt_entity, /* A extra-cluster entity. */ |
| |
| tt_template, /* The TEMPLATE_RESULT of a template. */ |
| }; |
| |
| enum walk_kind { |
| WK_none, /* No walk to do (a back- or fixed-ref happened). */ |
| WK_normal, /* Normal walk (by-name if possible). */ |
| |
| WK_value, /* By-value walk. */ |
| }; |
| |
| enum merge_kind |
| { |
| MK_unique, /* Known unique. */ |
| MK_named, /* Found by CTX, NAME + maybe_arg types etc. */ |
| MK_field, /* Found by CTX and index on TYPE_FIELDS */ |
| MK_vtable, /* Found by CTX and index on TYPE_VTABLES */ |
| MK_as_base, /* Found by CTX. */ |
| |
| MK_partial, |
| |
| MK_enum, /* Found by CTX, & 1stMemberNAME. */ |
| MK_attached, /* Found by attachee & index. */ |
| |
| MK_friend_spec, /* Like named, but has a tmpl & args too. */ |
| MK_local_friend, /* Found by CTX, index. */ |
| |
| MK_indirect_lwm = MK_enum, |
| |
| /* Template specialization kinds below. These are all found via |
| primary template and specialization args. */ |
| MK_template_mask = 0x10, /* A template specialization. */ |
| |
| MK_tmpl_decl_mask = 0x4, /* In decl table. */ |
| MK_tmpl_alias_mask = 0x2, /* Also in type table */ |
| |
| MK_tmpl_tmpl_mask = 0x1, /* We want TEMPLATE_DECL. */ |
| |
| MK_type_spec = MK_template_mask, |
| MK_decl_spec = MK_template_mask | MK_tmpl_decl_mask, |
| MK_alias_spec = MK_decl_spec | MK_tmpl_alias_mask, |
| |
| MK_hwm = 0x20 |
| }; |
| /* This is more than a debugging array. NULLs are used to determine |
| an invalid merge_kind number. */ |
| static char const *const merge_kind_name[MK_hwm] = |
| { |
| "unique", "named", "field", "vtable", /* 0...3 */ |
| "asbase", "partial", "enum", "attached", /* 4...7 */ |
| |
| "friend spec", "local friend", NULL, NULL, /* 8...11 */ |
| NULL, NULL, NULL, NULL, |
| |
| "type spec", "type tmpl spec", /* 16,17 type (template). */ |
| NULL, NULL, |
| |
| "decl spec", "decl tmpl spec", /* 20,21 decl (template). */ |
| "alias spec", "alias tmpl spec", /* 22,23 alias (template). */ |
| NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, |
| }; |
| |
| /* Mergeable entity location data. */ |
| struct merge_key { |
| cp_ref_qualifier ref_q : 2; |
| unsigned index; |
| |
| tree ret; /* Return type, if appropriate. */ |
| tree args; /* Arg types, if appropriate. */ |
| |
| tree constraints; /* Constraints. */ |
| |
| merge_key () |
| :ref_q (REF_QUAL_NONE), index (0), |
| ret (NULL_TREE), args (NULL_TREE), |
| constraints (NULL_TREE) |
| { |
| } |
| }; |
| |
| struct duplicate_hash : nodel_ptr_hash<tree_node> |
| { |
| #if 0 |
| /* This breaks variadic bases in the xtreme_header tests. Since ::equal is |
| the default pointer_hash::equal, let's use the default hash as well. */ |
| inline static hashval_t hash (value_type decl) |
| { |
| if (TREE_CODE (decl) == TREE_BINFO) |
| decl = TYPE_NAME (BINFO_TYPE (decl)); |
| return hashval_t (DECL_UID (decl)); |
| } |
| #endif |
| }; |
| |
| /* Hashmap of merged duplicates. Usually decls, but can contain |
| BINFOs. */ |
| typedef hash_map<tree,uintptr_t, |
| simple_hashmap_traits<duplicate_hash,uintptr_t> > |
| duplicate_hash_map; |
| |
| /* Tree stream reader. Note that reading a stream doesn't mark the |
| read trees with TREE_VISITED. Thus it's quite safe to have |
| multiple concurrent readers. Which is good, because lazy |
| loading. */ |
| class trees_in : public bytes_in { |
| typedef bytes_in parent; |
| |
| private: |
| module_state *state; /* Module being imported. */ |
| vec<tree> back_refs; /* Back references. */ |
| duplicate_hash_map *duplicates; /* Map from existings to duplicate. */ |
| vec<tree> post_decls; /* Decls to post process. */ |
| unsigned unused; /* Inhibit any interior TREE_USED |
| marking. */ |
| |
| public: |
| trees_in (module_state *); |
| ~trees_in (); |
| |
| public: |
| int insert (tree); |
| tree back_ref (int); |
| |
| private: |
| tree start (unsigned = 0); |
| |
| public: |
| /* Needed for binfo writing */ |
| bool core_bools (tree); |
| |
| private: |
| /* Stream tree_core, lang_decl_specific and lang_type_specific |
| bits. */ |
| bool core_vals (tree); |
| bool lang_type_bools (tree); |
| bool lang_type_vals (tree); |
| bool lang_decl_bools (tree); |
| bool lang_decl_vals (tree); |
| bool lang_vals (tree); |
| bool tree_node_bools (tree); |
| bool tree_node_vals (tree); |
| tree tree_value (); |
| tree decl_value (); |
| tree tpl_parm_value (); |
| |
| private: |
| tree chained_decls (); /* Follow DECL_CHAIN. */ |
| vec<tree, va_heap> *vec_chained_decls (); |
| vec<tree, va_gc> *tree_vec (); /* vec of tree. */ |
| vec<tree_pair_s, va_gc> *tree_pair_vec (); /* vec of tree_pair. */ |
| tree tree_list (bool has_purpose); |
| |
| public: |
| /* Read a tree node. */ |
| tree tree_node (bool is_use = false); |
| |
| private: |
| bool install_entity (tree decl); |
| tree tpl_parms (unsigned &tpl_levels); |
| bool tpl_parms_fini (tree decl, unsigned tpl_levels); |
| bool tpl_header (tree decl, unsigned *tpl_levels); |
| int fn_parms_init (tree); |
| void fn_parms_fini (int tag, tree fn, tree existing, bool has_defn); |
| unsigned add_indirect_tpl_parms (tree); |
| public: |
| bool add_indirects (tree); |
| |
| public: |
| /* Serialize various definitions. */ |
| bool read_definition (tree decl); |
| |
| private: |
| bool is_matching_decl (tree existing, tree decl, bool is_typedef); |
| static bool install_implicit_member (tree decl); |
| bool read_function_def (tree decl, tree maybe_template); |
| bool read_var_def (tree decl, tree maybe_template); |
| bool read_class_def (tree decl, tree maybe_template); |
| bool read_enum_def (tree decl, tree maybe_template); |
| |
| public: |
| tree decl_container (); |
| tree key_mergeable (int tag, merge_kind, tree decl, tree inner, tree type, |
| tree container, bool is_mod); |
| unsigned binfo_mergeable (tree *); |
| |
| private: |
| uintptr_t *find_duplicate (tree existing); |
| void register_duplicate (tree decl, tree existing); |
| /* Mark as an already diagnosed bad duplicate. */ |
| void unmatched_duplicate (tree existing) |
| { |
| *find_duplicate (existing) |= 1; |
| } |
| |
| public: |
| bool is_duplicate (tree decl) |
| { |
| return find_duplicate (decl) != NULL; |
| } |
| tree maybe_duplicate (tree decl) |
| { |
| if (uintptr_t *dup = find_duplicate (decl)) |
| return reinterpret_cast<tree> (*dup & ~uintptr_t (1)); |
| return decl; |
| } |
| tree odr_duplicate (tree decl, bool has_defn); |
| |
| public: |
| /* Return the next decl to postprocess, or NULL. */ |
| tree post_process () |
| { |
| return post_decls.length () ? post_decls.pop () : NULL_TREE; |
| } |
| private: |
| /* Register DECL for postprocessing. */ |
| void post_process (tree decl) |
| { |
| post_decls.safe_push (decl); |
| } |
| |
| private: |
| void assert_definition (tree, bool installing); |
| }; |
| |
| trees_in::trees_in (module_state *state) |
| :parent (), state (state), unused (0) |
| { |
| duplicates = NULL; |
| back_refs.create (500); |
| post_decls.create (0); |
| } |
| |
| trees_in::~trees_in () |
| { |
| delete (duplicates); |
| back_refs.release (); |
| post_decls.release (); |
| } |
| |
| /* Tree stream writer. */ |
| class trees_out : public bytes_out { |
| typedef bytes_out parent; |
| |
| private: |
| module_state *state; /* The module we are writing. */ |
| ptr_int_hash_map tree_map; /* Trees to references */ |
| depset::hash *dep_hash; /* Dependency table. */ |
| int ref_num; /* Back reference number. */ |
| unsigned section; |
| #if CHECKING_P |
| int importedness; /* Checker that imports not occurring |
| inappropriately. +ve imports ok, |
| -ve imports not ok. */ |
| #endif |
| |
| public: |
| trees_out (allocator *, module_state *, depset::hash &deps, unsigned sec = 0); |
| ~trees_out (); |
| |
| private: |
| void mark_trees (); |
| void unmark_trees (); |
| |
| public: |
| /* Hey, let's ignore the well known STL iterator idiom. */ |
| void begin (); |
| unsigned end (elf_out *sink, unsigned name, unsigned *crc_ptr); |
| void end (); |
| |
| public: |
| enum tags |
| { |
| tag_backref = -1, /* Upper bound on the backrefs. */ |
| tag_value = 0, /* Write by value. */ |
| tag_fixed /* Lower bound on the fixed trees. */ |
| }; |
| |
| public: |
| bool is_key_order () const |
| { |
| return dep_hash->is_key_order (); |
| } |
| |
| public: |
| int insert (tree, walk_kind = WK_normal); |
| |
| private: |
| void start (tree, bool = false); |
| |
| private: |
| walk_kind ref_node (tree); |
| public: |
| int get_tag (tree); |
| void set_importing (int i ATTRIBUTE_UNUSED) |
| { |
| #if CHECKING_P |
| importedness = i; |
| #endif |
| } |
| |
| private: |
| void core_bools (tree); |
| void core_vals (tree); |
| void lang_type_bools (tree); |
| void lang_type_vals (tree); |
| void lang_decl_bools (tree); |
| void lang_decl_vals (tree); |
| void lang_vals (tree); |
| void tree_node_bools (tree); |
| void tree_node_vals (tree); |
| |
| private: |
| void chained_decls (tree); |
| void vec_chained_decls (tree); |
| void tree_vec (vec<tree, va_gc> *); |
| void tree_pair_vec (vec<tree_pair_s, va_gc> *); |
| void tree_list (tree, bool has_purpose); |
| |
| public: |
| /* Mark a node for by-value walking. */ |
| void mark_by_value (tree); |
| |
| public: |
| void tree_node (tree); |
| |
| private: |
| void install_entity (tree decl, depset *); |
| void tpl_parms (tree parms, unsigned &tpl_levels); |
| void tpl_parms_fini (tree decl, unsigned tpl_levels); |
| void fn_parms_fini (tree) {} |
| unsigned add_indirect_tpl_parms (tree); |
| public: |
| void add_indirects (tree); |
| void fn_parms_init (tree); |
| void tpl_header (tree decl, unsigned *tpl_levels); |
| |
| public: |
| merge_kind get_merge_kind (tree decl, depset *maybe_dep); |
| tree decl_container (tree decl); |
| void key_mergeable (int tag, merge_kind, tree decl, tree inner, |
| tree container, depset *maybe_dep); |
| void binfo_mergeable (tree binfo); |
| |
| private: |
| bool decl_node (tree, walk_kind ref); |
| void type_node (tree); |
| void tree_value (tree); |
| void tpl_parm_value (tree); |
| |
| public: |
| void decl_value (tree, depset *); |
| |
| public: |
| /* Serialize various definitions. */ |
| void write_definition (tree decl); |
| void mark_declaration (tree decl, bool do_defn); |
| |
| private: |
| void mark_function_def (tree decl); |
| void mark_var_def (tree decl); |
| void mark_class_def (tree decl); |
| void mark_enum_def (tree decl); |
| void mark_class_member (tree decl, bool do_defn = true); |
| void mark_binfos (tree type); |
| |
| private: |
| void write_var_def (tree decl); |
| void write_function_def (tree decl); |
| void write_class_def (tree decl); |
| void write_enum_def (tree decl); |
| |
| private: |
| static void assert_definition (tree); |
| |
| public: |
| static void instrument (); |
| |
| private: |
| /* Tree instrumentation. */ |
| static unsigned tree_val_count; |
| static unsigned decl_val_count; |
| static unsigned back_ref_count; |
| static unsigned null_count; |
| }; |
| |
| /* Instrumentation counters. */ |
| unsigned trees_out::tree_val_count; |
| unsigned trees_out::decl_val_count; |
| unsigned trees_out::back_ref_count; |
| unsigned trees_out::null_count; |
| |
| trees_out::trees_out (allocator *mem, module_state *state, depset::hash &deps, |
| unsigned section) |
| :parent (mem), state (state), tree_map (500), |
| dep_hash (&deps), ref_num (0), section (section) |
| { |
| #if CHECKING_P |
| importedness = 0; |
| #endif |
| } |
| |
| trees_out::~trees_out () |
| { |
| } |
| |
| /********************************************************************/ |
| /* Location. We're aware of the line-map concept and reproduce it |
| here. Each imported module allocates a contiguous span of ordinary |
| maps, and of macro maps. adhoc maps are serialized by contents, |
| not pre-allocated. The scattered linemaps of a module are |
| coalesced when writing. */ |
| |
| |
| /* I use half-open [first,second) ranges. */ |
| typedef std::pair<unsigned,unsigned> range_t; |
| |
| /* A range of locations. */ |
| typedef std::pair<location_t,location_t> loc_range_t; |
| |
| /* Spans of the line maps that are occupied by this TU. I.e. not |
| within imports. Only extended when in an interface unit. |
| Interval zero corresponds to the forced header linemap(s). This |
| is a singleton object. */ |
| |
| class loc_spans { |
| public: |
| /* An interval of line maps. The line maps here represent a contiguous |
| non-imported range. */ |
| struct span { |
| loc_range_t ordinary; /* Ordinary map location range. */ |
| loc_range_t macro; /* Macro map location range. */ |
| int ordinary_delta; /* Add to ordinary loc to get serialized loc. */ |
| int macro_delta; /* Likewise for macro loc. */ |
| }; |
| |
| private: |
| vec<span> *spans; |
| |
| public: |
| loc_spans () |
| /* Do not preallocate spans, as that causes |
| --enable-detailed-mem-stats problems. */ |
| : spans (nullptr) |
| { |
| } |
| ~loc_spans () |
| { |
| delete spans; |
| } |
| |
| public: |
| span &operator[] (unsigned ix) |
| { |
| return (*spans)[ix]; |
| } |
| unsigned length () const |
| { |
| return spans->length (); |
| } |
| |
| public: |
| bool init_p () const |
| { |
| return spans != nullptr; |
| } |
| /* Initializer. */ |
| void init (const line_maps *lmaps, const line_map_ordinary *map); |
| |
| /* Slightly skewed preprocessed files can cause us to miss an |
| initialization in some places. Fallback initializer. */ |
| void maybe_init () |
| { |
| if (!init_p ()) |
| init (line_table, nullptr); |
| } |
| |
| public: |
| enum { |
| SPAN_RESERVED = 0, /* Reserved (fixed) locations. */ |
| SPAN_FIRST = 1, /* LWM of locations to stream */ |
| SPAN_MAIN = 2 /* Main file and onwards. */ |
| }; |
| |
| public: |
| location_t main_start () const |
| { |
| return (*spans)[SPAN_MAIN].ordinary.first; |
| } |
| |
| public: |
| void open (location_t); |
| void close (); |
| |
| public: |
| /* Propagate imported linemaps to us, if needed. */ |
| bool maybe_propagate (module_state *import, location_t loc); |
| |
| public: |
| const span *ordinary (location_t); |
| const span *macro (location_t); |
| }; |
| |
| static loc_spans spans; |
| /* Indirection to allow bsearching imports by ordinary location. */ |
| static vec<module_state *> *ool; |
| |
| /********************************************************************/ |
| /* Data needed by a module during the process of loading. */ |
| struct GTY(()) slurping { |
| |
| /* Remap import's module numbering to our numbering. Values are |
| shifted by 1. Bit0 encodes if the import is direct. */ |
| vec<unsigned, va_heap, vl_embed> * |
| GTY((skip)) remap; /* Module owner remapping. */ |
| |
| elf_in *GTY((skip)) from; /* The elf loader. */ |
| |
| /* This map is only for header imports themselves -- the global |
| headers bitmap hold it for the current TU. */ |
| bitmap headers; /* Transitive set of direct imports, including |
| self. Used for macro visibility and |
| priority. */ |
| |
| /* These objects point into the mmapped area, unless we're not doing |
| that, or we got frozen or closed. In those cases they point to |
| buffers we own. */ |
| bytes_in macro_defs; /* Macro definitions. */ |
| bytes_in macro_tbl; /* Macro table. */ |
| |
| /* Location remapping. first->ordinary, second->macro. */ |
| range_t GTY((skip)) loc_deltas; |
| |
| unsigned current; /* Section currently being loaded. */ |
| unsigned remaining; /* Number of lazy sections yet to read. */ |
| unsigned lru; /* An LRU counter. */ |
| |
| public: |
| slurping (elf_in *); |
| ~slurping (); |
| |
| public: |
| /* Close the ELF file, if it's open. */ |
| void close () |
| { |
| if (from) |
| { |
| from->end (); |
| delete from; |
| from = NULL; |
| } |
| } |
| |
| public: |
| void release_macros (); |
| |
| public: |
| void alloc_remap (unsigned size) |
| { |
| gcc_assert (!remap); |
| vec_safe_reserve (remap, size); |
| for (unsigned ix = size; ix--;) |
| remap->quick_push (0); |
| } |
| unsigned remap_module (unsigned owner) |
| { |
| if (owner < remap->length ()) |
| return (*remap)[owner] >> 1; |
| return 0; |
| } |
| |
| public: |
| /* GC allocation. But we must explicitly delete it. */ |
| static void *operator new (size_t x) |
| { |
| return ggc_alloc_atomic (x); |
| } |
| static void operator delete (void *p) |
| { |
| ggc_free (p); |
| } |
| }; |
| |
| slurping::slurping (elf_in *from) |
| : remap (NULL), from (from), |
| headers (BITMAP_GGC_ALLOC ()), macro_defs (), macro_tbl (), |
| loc_deltas (0, 0), |
| current (~0u), remaining (0), lru (0) |
| { |
| } |
| |
| slurping::~slurping () |
| { |
| vec_free (remap); |
| remap = NULL; |
| release_macros (); |
| close (); |
| } |
| |
| void slurping::release_macros () |
| { |
| if (macro_defs.size) |
| elf_in::release (from, macro_defs); |
| if (macro_tbl.size) |
| elf_in::release (from, macro_tbl); |
| } |
| |
| /* Information about location maps used during writing. */ |
| |
| struct location_map_info { |
| range_t num_maps; |
| |
| unsigned max_range; |
| }; |
| |
| /* Flage for extensions that end up being streamed. */ |
| |
| enum streamed_extensions { |
| SE_OPENMP = 1 << 0, |
| SE_BITS = 1 |
| }; |
| |
| /********************************************************************/ |
| struct module_state_config; |
| |
| /* Increasing levels of loadedness. */ |
| enum module_loadedness { |
| ML_NONE, /* Not loaded. */ |
| ML_CONFIG, /* Config loaed. */ |
| ML_PREPROCESSOR, /* Preprocessor loaded. */ |
| ML_LANGUAGE, /* Language loaded. */ |
| }; |
| |
| /* Increasing levels of directness (toplevel) of import. */ |
| enum module_directness { |
| MD_NONE, /* Not direct. */ |
| MD_PARTITION_DIRECT, /* Direct import of a partition. */ |
| MD_DIRECT, /* Direct import. */ |
| MD_PURVIEW_DIRECT, /* direct import in purview. */ |
| }; |
| |
| /* State of a particular module. */ |
| |
| class GTY((chain_next ("%h.parent"), for_user)) module_state { |
| public: |
| /* We always import & export ourselves. */ |
| bitmap imports; /* Transitive modules we're importing. */ |
| bitmap exports; /* Subset of that, that we're exporting. */ |
| |
| module_state *parent; |
| tree name; /* Name of the module. */ |
| |
| slurping *slurp; /* Data for loading. */ |
| |
| const char *flatname; /* Flatname of module. */ |
| char *filename; /* CMI Filename */ |
| |
| /* Indices into the entity_ary. */ |
| unsigned entity_lwm; |
| unsigned entity_num; |
| |
| /* Location ranges for this module. adhoc-locs are decomposed, so |
| don't have a range. */ |
| loc_range_t GTY((skip)) ordinary_locs; |
| loc_range_t GTY((skip)) macro_locs; |
| |
| /* LOC is first set too the importing location. When initially |
| loaded it refers to a module loc whose parent is the importing |
| location. */ |
| location_t loc; /* Location referring to module itself. */ |
| unsigned crc; /* CRC we saw reading it in. */ |
| |
| unsigned mod; /* Module owner number. */ |
| unsigned remap; /* Remapping during writing. */ |
| |
| unsigned short subst; /* Mangle subst if !0. */ |
| |
| /* How loaded this module is. */ |
| enum module_loadedness loadedness : 2; |
| |
| bool module_p : 1; /* /The/ module of this TU. */ |
| bool header_p : 1; /* Is a header unit. */ |
| bool interface_p : 1; /* An interface. */ |
| bool partition_p : 1; /* A partition. */ |
| |
| /* How directly this module is imported. */ |
| enum module_directness directness : 2; |
| |
| bool exported_p : 1; /* directness != MD_NONE && exported. */ |
| bool cmi_noted_p : 1; /* We've told the user about the CMI, don't |
| do it again */ |
| bool call_init_p : 1; /* This module's global initializer needs |
| calling. */ |
| bool inform_cmi_p : 1; /* Inform of a read/write. */ |
| bool visited_p : 1; /* A walk-once flag. */ |
| /* Record extensions emitted or permitted. */ |
| unsigned extensions : SE_BITS; |
| /* 14 bits used, 2 bits remain */ |
| |
| public: |
| module_state (tree name, module_state *, bool); |
| ~module_state (); |
| |
| public: |
| void release () |
| { |
| imports = exports = NULL; |
| slurped (); |
| } |
| void slurped () |
| { |
| delete slurp; |
| slurp = NULL; |
| } |
| elf_in *from () const |
| { |
| return slurp->from; |
| } |
| |
| public: |
| /* Kind of this module. */ |
| bool is_module () const |
| { |
| return module_p; |
| } |
| bool is_header () const |
| { |
| return header_p; |
| } |
| bool is_interface () const |
| { |
| return interface_p; |
| } |
| bool is_partition () const |
| { |
| return partition_p; |
| } |
| |
| /* How this module is used in the current TU. */ |
| bool is_exported () const |
| { |
| return exported_p; |
| } |
| bool is_direct () const |
| { |
| return directness >= MD_DIRECT; |
| } |
| bool is_purview_direct () const |
| { |
| return directness == MD_PURVIEW_DIRECT; |
| } |
| bool is_partition_direct () const |
| { |
| return directness == MD_PARTITION_DIRECT; |
| } |
| |
| public: |
| /* Is this a real module? */ |
| bool has_location () const |
| { |
| return loc != UNKNOWN_LOCATION; |
| } |
| |
| public: |
| bool check_not_purview (location_t loc); |
| |
| public: |
| void mangle (bool include_partition); |
| |
| public: |
| void set_import (module_state const *, bool is_export); |
| void announce (const char *) const; |
| |
| public: |
| /* Read and write module. */ |
| void write (elf_out *to, cpp_reader *); |
| bool read_initial (cpp_reader *); |
| bool read_preprocessor (bool); |
| bool read_language (bool); |
| |
| public: |
| /* Read a section. */ |
| bool load_section (unsigned snum, binding_slot *mslot); |
| /* Lazily read a section. */ |
| bool lazy_load (unsigned index, binding_slot *mslot); |
| |
| public: |
| /* Juggle a limited number of file numbers. */ |
| static void freeze_an_elf (); |
| bool maybe_defrost (); |
| |
| public: |
| void maybe_completed_reading (); |
| bool check_read (bool outermost, bool ok); |
| |
| private: |
| /* The README, for human consumption. */ |
| void write_readme (elf_out *to, cpp_reader *, |
| const char *dialect, unsigned extensions); |
| void write_env (elf_out *to); |
| |
| private: |
| /* Import tables. */ |
| void write_imports (bytes_out &cfg, bool direct); |
| unsigned read_imports (bytes_in &cfg, cpp_reader *, line_maps *maps); |
| |
| private: |
| void write_imports (elf_out *to, unsigned *crc_ptr); |
| bool read_imports (cpp_reader *, line_maps *); |
| |
| private: |
| void write_partitions (elf_out *to, unsigned, unsigned *crc_ptr); |
| bool read_partitions (unsigned); |
| |
| private: |
| void write_config (elf_out *to, struct module_state_config &, unsigned crc); |
| bool read_config (struct module_state_config &); |
| static void write_counts (elf_out *to, unsigned [], unsigned *crc_ptr); |
| bool read_counts (unsigned []); |
| |
| public: |
| void note_cmi_name (); |
| |
| private: |
| static unsigned write_bindings (elf_out *to, vec<depset *> depsets, |
| unsigned *crc_ptr); |
| bool read_bindings (unsigned count, unsigned lwm, unsigned hwm); |
| |
| static void write_namespace (bytes_out &sec, depset *ns_dep); |
| tree read_namespace (bytes_in &sec); |
| |
| void write_namespaces (elf_out *to, vec<depset *> spaces, |
| unsigned, unsigned *crc_ptr); |
| bool read_namespaces (unsigned); |
| |
| void intercluster_seed (trees_out &sec, unsigned index, depset *dep); |
| unsigned write_cluster (elf_out *to, depset *depsets[], unsigned size, |
| depset::hash &, unsigned *counts, unsigned *crc_ptr); |
| bool read_cluster (unsigned snum); |
| |
| private: |
| unsigned write_inits (elf_out *to, depset::hash &, unsigned *crc_ptr); |
| bool read_inits (unsigned count); |
| |
| private: |
| unsigned write_pendings (elf_out *to, vec<depset *> depsets, |
| depset::hash &, unsigned *crc_ptr); |
| bool read_pendings (unsigned count); |
| |
| private: |
| void write_entities (elf_out *to, vec<depset *> depsets, |
| unsigned count, unsigned *crc_ptr); |
| bool read_entities (unsigned count, unsigned lwm, unsigned hwm); |
| |
| private: |
| location_map_info write_prepare_maps (module_state_config *); |
| bool read_prepare_maps (const module_state_config *); |
| |
| void write_ordinary_maps (elf_out *to, location_map_info &, |
| module_state_config *, bool, unsigned *crc_ptr); |
| bool read_ordinary_maps (); |
| void write_macro_maps (elf_out *to, location_map_info &, |
| module_state_config *, unsigned *crc_ptr); |
| bool read_macro_maps (); |
| |
| private: |
| void write_define (bytes_out &, const cpp_macro *, bool located = true); |
| cpp_macro *read_define (bytes_in &, cpp_reader *, bool located = true) const; |
| unsigned write_macros (elf_out *to, cpp_reader *, unsigned *crc_ptr); |
| bool read_macros (); |
| void install_macros (); |
| |
| public: |
| void import_macros (); |
| |
| public: |
| static void undef_macro (cpp_reader *, location_t, cpp_hashnode *); |
| static cpp_macro *deferred_macro (cpp_reader *, location_t, cpp_hashnode *); |
| |
| public: |
| static void write_location (bytes_out &, location_t); |
| location_t read_location (bytes_in &) const; |
| |
| public: |
| void set_flatname (); |
| const char *get_flatname () const |
| { |
| return flatname; |
| } |
| location_t imported_from () const; |
| |
| public: |
| void set_filename (const Cody::Packet &); |
| bool do_import (cpp_reader *, bool outermost); |
| }; |
| |
| /* Hash module state by name. This cannot be a member of |
| module_state, because of GTY restrictions. We never delete from |
| the hash table, but ggc_ptr_hash doesn't support that |
| simplification. */ |
| |
| struct module_state_hash : ggc_ptr_hash<module_state> { |
| typedef std::pair<tree,uintptr_t> compare_type; /* {name,parent} */ |
| |
| static inline hashval_t hash (const value_type m); |
| static inline hashval_t hash (const compare_type &n); |
| static inline bool equal (const value_type existing, |
| const compare_type &candidate); |
| }; |
| |
| module_state::module_state (tree name, module_state *parent, bool partition) |
| : imports (BITMAP_GGC_ALLOC ()), exports (BITMAP_GGC_ALLOC ()), |
| parent (parent), name (name), slurp (NULL), |
| flatname (NULL), filename (NULL), |
| entity_lwm (~0u >> 1), entity_num (0), |
| ordinary_locs (0, 0), macro_locs (0, 0), |
| loc (UNKNOWN_LOCATION), |
| crc (0), mod (MODULE_UNKNOWN), remap (0), subst (0) |
| { |
| loadedness = ML_NONE; |
| |
| module_p = header_p = interface_p = partition_p = false; |
| |
| directness = MD_NONE; |
| exported_p = false; |
| |
| cmi_noted_p = false; |
| call_init_p = false; |
| |
| partition_p = partition; |
| |
| inform_cmi_p = false; |
| visited_p = false; |
| |
| extensions = 0; |
| if (name && TREE_CODE (name) == STRING_CST) |
| { |
| header_p = true; |
| |
| const char *string = TREE_STRING_POINTER (name); |
| gcc_checking_assert (string[0] == '.' |
| ? IS_DIR_SEPARATOR (string[1]) |
| : IS_ABSOLUTE_PATH (string)); |
| } |
| |
| gcc_checking_assert (!(parent && header_p)); |
| } |
| |
| module_state::~module_state () |
| { |
| release (); |
| } |
| |
| /* Hash module state. */ |
| static hashval_t |
| module_name_hash (const_tree name) |
| { |
| if (TREE_CODE (name) == STRING_CST) |
| return htab_hash_string (TREE_STRING_POINTER (name)); |
| else |
| return IDENTIFIER_HASH_VALUE (name); |
| } |
| |
| hashval_t |
| module_state_hash::hash (const value_type m) |
| { |
| hashval_t ph = pointer_hash<void>::hash |
| (reinterpret_cast<void *> (reinterpret_cast<uintptr_t> (m->parent) |
| | m->is_partition ())); |
| hashval_t nh = module_name_hash (m->name); |
| return iterative_hash_hashval_t (ph, nh); |
| } |
| |
| /* Hash a name. */ |
| hashval_t |
| module_state_hash::hash (const compare_type &c) |
| { |
| hashval_t ph = pointer_hash<void>::hash (reinterpret_cast<void *> (c.second)); |
| hashval_t nh = module_name_hash (c.first); |
| |
| return iterative_hash_hashval_t (ph, nh); |
| } |
| |
| bool |
| module_state_hash::equal (const value_type existing, |
| const compare_type &candidate) |
| { |
| uintptr_t ep = (reinterpret_cast<uintptr_t> (existing->parent) |
| | existing->is_partition ()); |
| if (ep != candidate.second) |
| return false; |
| |
| /* Identifier comparison is by pointer. If the string_csts happen |
| to be the same object, then they're equal too. */ |
| if (existing->name == candidate.first) |
| return true; |
| |
| /* If neither are string csts, they can't be equal. */ |
| if (TREE_CODE (candidate.first) != STRING_CST |
| || TREE_CODE (existing->name) != STRING_CST) |
| return false; |
| |
| /* String equality. */ |
| if (TREE_STRING_LENGTH (existing->name) |
| == TREE_STRING_LENGTH (candidate.first) |
| && !memcmp (TREE_STRING_POINTER (existing->name), |
| TREE_STRING_POINTER (candidate.first), |
| TREE_STRING_LENGTH (existing->name))) |
| return true; |
| |
| return false; |
| } |
| |
| /********************************************************************/ |
| /* Global state */ |
| |
| /* Mapper name. */ |
| static const char *module_mapper_name; |
| |
| /* Deferred import queue (FIFO). */ |
| static vec<module_state *, va_heap, vl_embed> *pending_imports; |
| |
| /* CMI repository path and workspace. */ |
| static char *cmi_repo; |
| static size_t cmi_repo_length; |
| static char *cmi_path; |
| static size_t cmi_path_alloc; |
| |
| /* Count of available and loaded clusters. */ |
| static unsigned available_clusters; |
| static unsigned loaded_clusters; |
| |
| /* What the current TU is. */ |
| unsigned module_kind; |
| |
| /* Number of global init calls needed. */ |
| unsigned num_init_calls_needed = 0; |
| |
| /* Global trees. */ |
| static const std::pair<tree *, unsigned> global_tree_arys[] = |
| { |
| std::pair<tree *, unsigned> (sizetype_tab, stk_type_kind_last), |
| std::pair<tree *, unsigned> (integer_types, itk_none), |
| std::pair<tree *, unsigned> (global_trees, TI_MODULE_HWM), |
| std::pair<tree *, unsigned> (c_global_trees, CTI_MODULE_HWM), |
| std::pair<tree *, unsigned> (cp_global_trees, CPTI_MODULE_HWM), |
| std::pair<tree *, unsigned> (NULL, 0) |
| }; |
| static GTY(()) vec<tree, va_gc> *fixed_trees; |
| static unsigned global_crc; |
| |
| /* Lazy loading can open many files concurrently, there are |
| per-process limits on that. We pay attention to the process limit, |
| and attempt to increase it when we run out. Otherwise we use an |
| LRU scheme to figure out who to flush. Note that if the import |
| graph /depth/ exceeds lazy_limit, we'll exceed the limit. */ |
| static unsigned lazy_lru; /* LRU counter. */ |
| static unsigned lazy_open; /* Number of open modules */ |
| static unsigned lazy_limit; /* Current limit of open modules. */ |
| static unsigned lazy_hard_limit; /* Hard limit on open modules. */ |
| /* Account for source, assembler and dump files & directory searches. |
| We don't keep the source file's open, so we don't have to account |
| for #include depth. I think dump files are opened and closed per |
| pass, but ICBW. */ |
| #define LAZY_HEADROOM 15 /* File descriptor headroom. */ |
| |
| /* Vector of module state. Indexed by OWNER. Has at least 2 slots. */ |
| static GTY(()) vec<module_state *, va_gc> *modules; |
| |
| /* Hash of module state, findable by {name, parent}. */ |
| static GTY(()) hash_table<module_state_hash> *modules_hash; |
| |
| /* Map of imported entities. We map DECL_UID to index of entity |
| vector. */ |
| typedef hash_map<unsigned/*UID*/, unsigned/*index*/, |
| simple_hashmap_traits<int_hash<unsigned,0>, unsigned> |
| > entity_map_t; |
| static entity_map_t *entity_map; |
| /* Doesn't need GTYing, because any tree referenced here is also |
| findable by, symbol table, specialization table, return type of |
| reachable function. */ |
| static vec<binding_slot, va_heap, vl_embed> *entity_ary; |
| |
| /* Members entities of imported classes that are defined in this TU. |
| These are where the entity's context is not from the current TU. |
| We need to emit the definition (but not the enclosing class). |
| |
| We could find these by walking ALL the imported classes that we |
| could provide a member definition. But that's expensive, |
| especially when you consider lazy implicit member declarations, |
| which could be ANY imported class. */ |
| static GTY(()) vec<tree, va_gc> *class_members; |
| |
| /* The same problem exists for class template partial |
| specializations. Now that we have constraints, the invariant of |
| expecting them in the instantiation table no longer holds. One of |
| the constrained partial specializations will be there, but the |
| others not so much. It's not even an unconstrained partial |
| spacialization in the table :( so any partial template declaration |
| is added to this list too. */ |
| static GTY(()) vec<tree, va_gc> *partial_specializations; |
| |
| /********************************************************************/ |
| |
| /* Our module mapper (created lazily). */ |
| module_client *mapper; |
| |
| static module_client *make_mapper (location_t loc); |
| inline module_client *get_mapper (location_t loc) |
| { |
| auto *res = mapper; |
| if (!res) |
| res = make_mapper (loc); |
| return res; |
| } |
| |
| /********************************************************************/ |
| static tree |
| get_clone_target (tree decl) |
| { |
| tree target; |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| tree res_orig = DECL_CLONED_FUNCTION (DECL_TEMPLATE_RESULT (decl)); |
| |
| target = DECL_TI_TEMPLATE (res_orig); |
| } |
| else |
| target = DECL_CLONED_FUNCTION (decl); |
| |
| gcc_checking_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (target)); |
| |
| return target; |
| } |
| |
| /* Like FOR_EACH_CLONE, but will walk cloned templates. */ |
| #define FOR_EVERY_CLONE(CLONE, FN) \ |
| if (!DECL_MAYBE_IN_CHARGE_CDTOR_P (FN)); \ |
| else \ |
| for (CLONE = DECL_CHAIN (FN); \ |
| CLONE && DECL_CLONED_FUNCTION_P (CLONE); \ |
| CLONE = DECL_CHAIN (CLONE)) |
| |
| /* It'd be nice if USE_TEMPLATE was a field of template_info |
| (a) it'd solve the enum case dealt with below, |
| (b) both class templates and decl templates would store this in the |
| same place |
| (c) this function wouldn't need the by-ref arg, which is annoying. */ |
| |
| static tree |
| node_template_info (tree decl, int &use) |
| { |
| tree ti = NULL_TREE; |
| int use_tpl = -1; |
| if (DECL_IMPLICIT_TYPEDEF_P (decl)) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| ti = TYPE_TEMPLATE_INFO (type); |
| if (ti) |
| { |
| if (TYPE_LANG_SPECIFIC (type)) |
| use_tpl = CLASSTYPE_USE_TEMPLATE (type); |
| else |
| { |
| /* An enum, where we don't explicitly encode use_tpl. |
| If the containing context (a type or a function), is |
| an ({im,ex}plicit) instantiation, then this is too. |
| If it's a partial or explicit specialization, then |
| this is not!. */ |
| tree ctx = CP_DECL_CONTEXT (decl); |
| if (TYPE_P (ctx)) |
| ctx = TYPE_NAME (ctx); |
| node_template_info (ctx, use); |
| use_tpl = use != 2 ? use : 0; |
| } |
| } |
| } |
| else if (DECL_LANG_SPECIFIC (decl) |
| && (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == FUNCTION_DECL |
| || TREE_CODE (decl) == FIELD_DECL |
| || TREE_CODE (decl) == TEMPLATE_DECL)) |
| { |
| use_tpl = DECL_USE_TEMPLATE (decl); |
| ti = DECL_TEMPLATE_INFO (decl); |
| } |
| |
| use = use_tpl; |
| return ti; |
| } |
| |
| /* Find the index in entity_ary for an imported DECL. It should |
| always be there, but bugs can cause it to be missing, and that can |
| crash the crash reporting -- let's not do that! When streaming |
| out we place entities from this module there too -- with negated |
| indices. */ |
| |
| static unsigned |
| import_entity_index (tree decl, bool null_ok = false) |
| { |
| if (unsigned *slot = entity_map->get (DECL_UID (decl))) |
| return *slot; |
| |
| gcc_checking_assert (null_ok); |
| return ~(~0u >> 1); |
| } |
| |
| /* Find the module for an imported entity at INDEX in the entity ary. |
| There must be one. */ |
| |
| static module_state * |
| import_entity_module (unsigned index) |
| { |
| if (index > ~(~0u >> 1)) |
| /* This is an index for an exported entity. */ |
| return (*modules)[0]; |
| |
| /* Do not include the current TU (not an off-by-one error). */ |
| unsigned pos = 1; |
| unsigned len = modules->length () - pos; |
| while (len) |
| { |
| unsigned half = len / 2; |
| module_state *probe = (*modules)[pos + half]; |
| if (index < probe->entity_lwm) |
| len = half; |
| else if (index < probe->entity_lwm + probe->entity_num) |
| return probe; |
| else |
| { |
| pos += half + 1; |
| len = len - (half + 1); |
| } |
| } |
| gcc_unreachable (); |
| } |
| |
| |
| /********************************************************************/ |
| /* A dumping machinery. */ |
| |
| class dumper { |
| public: |
| enum { |
| LOCATION = TDF_LINENO, /* -lineno:Source location streaming. */ |
| DEPEND = TDF_GRAPH, /* -graph:Dependency graph construction. */ |
| CLUSTER = TDF_BLOCKS, /* -blocks:Clusters. */ |
| TREE = TDF_UID, /* -uid:Tree streaming. */ |
| MERGE = TDF_ALIAS, /* -alias:Mergeable Entities. */ |
| ELF = TDF_ASMNAME, /* -asmname:Elf data. */ |
| MACRO = TDF_VOPS /* -vops:Macros. */ |
| }; |
| |
| private: |
| struct impl { |
| typedef vec<module_state *, va_heap, vl_embed> stack_t; |
| |
| FILE *stream; /* Dump stream. */ |
| unsigned indent; /* Local indentation. */ |
| bool bol; /* Beginning of line. */ |
| stack_t stack; /* Trailing array of module_state. */ |
| |
| bool nested_name (tree); /* Dump a name following DECL_CONTEXT. */ |
| }; |
| |
| public: |
| /* The dumper. */ |
| impl *dumps; |
| dump_flags_t flags; |
| |
| public: |
| /* Push/pop module state dumping. */ |
| unsigned push (module_state *); |
| void pop (unsigned); |
| |
| public: |
| /* Change local indentation. */ |
| void indent () |
| { |
| if (dumps) |
| dumps->indent++; |
| } |
| void outdent () |
| { |
| if (dumps) |
| { |
| gcc_checking_assert (dumps->indent); |
| dumps->indent--; |
| } |
| } |
| |
| public: |
| /* Is dump enabled?. */ |
| bool operator () (int mask = 0) |
| { |
| if (!dumps || !dumps->stream) |
| return false; |
| if (mask && !(mask & flags)) |
| return false; |
| return true; |
| } |
| /* Dump some information. */ |
| bool operator () (const char *, ...); |
| }; |
| |
| /* The dumper. */ |
| static dumper dump = {0, dump_flags_t (0)}; |
| |
| /* Push to dumping M. Return previous indentation level. */ |
| |
| unsigned |
| dumper::push (module_state *m) |
| { |
| FILE *stream = NULL; |
| if (!dumps || !dumps->stack.length ()) |
| { |
| stream = dump_begin (module_dump_id, &flags); |
| if (!stream) |
| return 0; |
| } |
| |
| if (!dumps || !dumps->stack.space (1)) |
| { |
| /* Create or extend the dump implementor. */ |
| unsigned current = dumps ? dumps->stack.length () : 0; |
| unsigned count = current ? current * 2 : EXPERIMENT (1, 20); |
| size_t alloc = (offsetof (impl, stack) |
| + impl::stack_t::embedded_size (count)); |
| dumps = XRESIZEVAR (impl, dumps, alloc); |
| dumps->stack.embedded_init (count, current); |
| } |
| if (stream) |
| dumps->stream = stream; |
| |
| unsigned n = dumps->indent; |
| dumps->indent = 0; |
| dumps->bol = true; |
| dumps->stack.quick_push (m); |
| if (m) |
| { |
| module_state *from = NULL; |
| |
| if (dumps->stack.length () > 1) |
| from = dumps->stack[dumps->stack.length () - 2]; |
| else |
| dump (""); |
| dump (from ? "Starting module %M (from %M)" |
| : "Starting module %M", m, from); |
| } |
| |
| return n; |
| } |
| |
| /* Pop from dumping. Restore indentation to N. */ |
| |
| void dumper::pop (unsigned n) |
| { |
| if (!dumps) |
| return; |
| |
| gcc_checking_assert (dump () && !dumps->indent); |
| if (module_state *m = dumps->stack[dumps->stack.length () - 1]) |
| { |
| module_state *from = (dumps->stack.length () > 1 |
| ? dumps->stack[dumps->stack.length () - 2] : NULL); |
| dump (from ? "Finishing module %M (returning to %M)" |
| : "Finishing module %M", m, from); |
| } |
| dumps->stack.pop (); |
| dumps->indent = n; |
| if (!dumps->stack.length ()) |
| { |
| dump_end (module_dump_id, dumps->stream); |
| dumps->stream = NULL; |
| } |
| } |
| |
| /* Dump a nested name for arbitrary tree T. Sometimes it won't have a |
| name. */ |
| |
| bool |
| dumper::impl::nested_name (tree t) |
| { |
| tree ti = NULL_TREE; |
| int origin = -1; |
| tree name = NULL_TREE; |
| |
| if (t && TREE_CODE (t) == TREE_BINFO) |
| t = BINFO_TYPE (t); |
| |
| if (t && TYPE_P (t)) |
| t = TYPE_NAME (t); |
| |
| if (t && DECL_P (t)) |
| { |
| if (t == global_namespace || DECL_TEMPLATE_PARM_P (t)) |
| ; |
| else if (tree ctx = DECL_CONTEXT (t)) |
| if (TREE_CODE (ctx) == TRANSLATION_UNIT_DECL |
| || nested_name (ctx)) |
| fputs ("::", stream); |
| |
| int use_tpl; |
| ti = node_template_info (t, use_tpl); |
| if (ti && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL |
| && (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == t)) |
| t = TI_TEMPLATE (ti); |
| tree not_tmpl = t; |
| if (TREE_CODE (t) == TEMPLATE_DECL) |
| { |
| fputs ("template ", stream); |
| not_tmpl = DECL_TEMPLATE_RESULT (t); |
| } |
| |
| if (not_tmpl |
| && DECL_P (not_tmpl) |
| && DECL_LANG_SPECIFIC (not_tmpl) |
| && DECL_MODULE_IMPORT_P (not_tmpl)) |
| { |
| /* We need to be careful here, so as to not explode on |
| inconsistent data -- we're probably debugging, because |
| Something Is Wrong. */ |
| unsigned index = import_entity_index (t, true); |
| if (!(index & ~(~0u >> 1))) |
| origin = import_entity_module (index)->mod; |
| else if (index > ~(~0u >> 1)) |
| /* An imported partition member that we're emitting. */ |
| origin = 0; |
| else |
| origin = -2; |
| } |
| |
| name = DECL_NAME (t) ? DECL_NAME (t) |
| : HAS_DECL_ASSEMBLER_NAME_P (t) ? DECL_ASSEMBLER_NAME_RAW (t) |
| : NULL_TREE; |
| } |
| else |
| name = t; |
| |
| if (name) |
| switch (TREE_CODE (name)) |
| { |
| default: |
| fputs ("#unnamed#", stream); |
| break; |
| |
| case IDENTIFIER_NODE: |
| fwrite (IDENTIFIER_POINTER (name), 1, IDENTIFIER_LENGTH (name), stream); |
| break; |
| |
| case INTEGER_CST: |
| print_hex (wi::to_wide (name), stream); |
| break; |
| |
| case STRING_CST: |
| /* If TREE_TYPE is NULL, this is a raw string. */ |
| fwrite (TREE_STRING_POINTER (name), 1, |
| TREE_STRING_LENGTH (name) - (TREE_TYPE (name) != NULL_TREE), |
| stream); |
| break; |
| } |
| else |
| fputs ("#null#", stream); |
| |
| if (origin >= 0) |
| { |
| const module_state *module = (*modules)[origin]; |
| fprintf (stream, "@%s:%d", !module ? "" : !module->name ? "(unnamed)" |
| : module->get_flatname (), origin); |
| } |
| else if (origin == -2) |
| fprintf (stream, "@???"); |
| |
| if (ti) |
| { |
| tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)); |
| fputs ("<", stream); |
| if (args) |
| for (int ix = 0; ix != TREE_VEC_LENGTH (args); ix++) |
| { |
| if (ix) |
| fputs (",", stream); |
| nested_name (TREE_VEC_ELT (args, ix)); |
| } |
| fputs (">", stream); |
| } |
| |
| return true; |
| } |
| |
| /* Formatted dumping. FORMAT begins with '+' do not emit a trailing |
| new line. (Normally it is appended.) |
| Escapes: |
| %C - tree_code |
| %I - identifier |
| %M - module_state |
| %N - name -- DECL_NAME |
| %P - context:name pair |
| %R - unsigned:unsigned ratio |
| %S - symbol -- DECL_ASSEMBLER_NAME |
| %U - long unsigned |
| %V - version |
| --- the following are printf-like, but without its flexibility |
| %d - decimal int |
| %p - pointer |
| %s - string |
| %u - unsigned int |
| %x - hex int |
| |
| We do not implement the printf modifiers. */ |
| |
| bool |
| dumper::operator () (const char *format, ...) |
| { |
| if (!(*this) ()) |
| return false; |
| |
| bool no_nl = format[0] == '+'; |
| format += no_nl; |
| |
| if (dumps->bol) |
| { |
| /* Module import indent. */ |
| if (unsigned depth = dumps->stack.length () - 1) |
| { |
| const char *prefix = ">>>>"; |
| fprintf (dumps->stream, (depth <= strlen (prefix) |
| ? &prefix[strlen (prefix) - depth] |
| : ">.%d.>"), depth); |
| } |
| |
| /* Local indent. */ |
| if (unsigned indent = dumps->indent) |
| { |
| const char *prefix = " "; |
| fprintf (dumps->stream, (indent <= strlen (prefix) |
| ? &prefix[strlen (prefix) - indent] |
| : " .%d. "), indent); |
| } |
| dumps->bol = false; |
| } |
| |
| va_list args; |
| va_start (args, format); |
| while (const char *esc = strchr (format, '%')) |
| { |
| fwrite (format, 1, (size_t)(esc - format), dumps->stream); |
| format = ++esc; |
| switch (*format++) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case '%': |
| fputc ('%', dumps->stream); |
| break; |
| |
| case 'C': /* Code */ |
| { |
| tree_code code = (tree_code)va_arg (args, unsigned); |
| fputs (get_tree_code_name (code), dumps->stream); |
| } |
| break; |
| |
| case 'I': /* Identifier. */ |
| { |
| tree t = va_arg (args, tree); |
| dumps->nested_name (t); |
| } |
| break; |
| |
| case 'M': /* Module. */ |
| { |
| const char *str = "(none)"; |
| if (module_state *m = va_arg (args, module_state *)) |
| { |
| if (!m->has_location ()) |
| str = "(detached)"; |
| else |
| str = m->get_flatname (); |
| } |
| fputs (str, dumps->stream); |
| } |
| break; |
| |
| case 'N': /* Name. */ |
| { |
| tree t = va_arg (args, tree); |
| while (t && TREE_CODE (t) == OVERLOAD) |
| t = OVL_FUNCTION (t); |
| fputc ('\'', dumps->stream); |
| dumps->nested_name (t); |
| fputc ('\'', dumps->stream); |
| } |
| break; |
| |
| case 'P': /* Pair. */ |
| { |
| tree ctx = va_arg (args, tree); |
| tree name = va_arg (args, tree); |
| fputc ('\'', dumps->stream); |
| dumps->nested_name (ctx); |
| if (ctx && ctx != global_namespace) |
| fputs ("::", dumps->stream); |
| dumps->nested_name (name); |
| fputc ('\'', dumps->stream); |
| } |
| break; |
| |
| case 'R': /* Ratio */ |
| { |
| unsigned a = va_arg (args, unsigned); |
| unsigned b = va_arg (args, unsigned); |
| fprintf (dumps->stream, "%.1f", (float) a / (b + !b)); |
| } |
| break; |
| |
| case 'S': /* Symbol name */ |
| { |
| tree t = va_arg (args, tree); |
| if (t && TYPE_P (t)) |
| t = TYPE_NAME (t); |
| if (t && HAS_DECL_ASSEMBLER_NAME_P (t) |
| && DECL_ASSEMBLER_NAME_SET_P (t)) |
| { |
| fputc ('(', dumps->stream); |
| fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (t)), |
| dumps->stream); |
| fputc (')', dumps->stream); |
| } |
| } |
| break; |
| |
| case 'U': /* long unsigned. */ |
| { |
| unsigned long u = va_arg (args, unsigned long); |
| fprintf (dumps->stream, "%lu", u); |
| } |
| break; |
| |
| case 'V': /* Verson. */ |
| { |
| unsigned v = va_arg (args, unsigned); |
| verstr_t string; |
| |
| version2string (v, string); |
| fputs (string, dumps->stream); |
| } |
| break; |
| |
| case 'c': /* Character. */ |
| { |
| int c = va_arg (args, int); |
| fputc (c, dumps->stream); |
| } |
| break; |
| |
| case 'd': /* Decimal Int. */ |
| { |
| int d = va_arg (args, int); |
| fprintf (dumps->stream, "%d", d); |
| } |
| break; |
| |
| case 'p': /* Pointer. */ |
| { |
| void *p = va_arg (args, void *); |
| fprintf (dumps->stream, "%p", p); |
| } |
| break; |
| |
| case 's': /* String. */ |
| { |
| const char *s = va_arg (args, char *); |
| gcc_checking_assert (s); |
| fputs (s, dumps->stream); |
| } |
| break; |
| |
| case 'u': /* Unsigned. */ |
| { |
| unsigned u = va_arg (args, unsigned); |
| fprintf (dumps->stream, "%u", u); |
| } |
| break; |
| |
| case 'x': /* Hex. */ |
| { |
| unsigned x = va_arg (args, unsigned); |
| fprintf (dumps->stream, "%x", x); |
| } |
| break; |
| } |
| } |
| fputs (format, dumps->stream); |
| va_end (args); |
| if (!no_nl) |
| { |
| dumps->bol = true; |
| fputc ('\n', dumps->stream); |
| } |
| return true; |
| } |
| |
| struct note_def_cache_hasher : ggc_cache_ptr_hash<tree_node> |
| { |
| static int keep_cache_entry (tree t) |
| { |
| if (!CHECKING_P) |
| /* GTY is unfortunately not clever enough to conditionalize |
| this. */ |
| gcc_unreachable (); |
| |
| if (ggc_marked_p (t)) |
| return -1; |
| |
| unsigned n = dump.push (NULL); |
| /* This might or might not be an error. We should note its |
| dropping whichever. */ |
| dump () && dump ("Dropping %N from note_defs table", t); |
| dump.pop (n); |
| |
| return 0; |
| } |
| }; |
| |
| /* We should stream each definition at most once. |
| This needs to be a cache because there are cases where a definition |
| ends up being not retained, and we need to drop those so we don't |
| get confused if memory is reallocated. */ |
| typedef hash_table<note_def_cache_hasher> note_defs_table_t; |
| static GTY((cache)) note_defs_table_t *note_defs; |
| |
| void |
| trees_in::assert_definition (tree decl ATTRIBUTE_UNUSED, |
| bool installing ATTRIBUTE_UNUSED) |
| { |
| #if CHECKING_P |
| tree *slot = note_defs->find_slot (decl, installing ? INSERT : NO_INSERT); |
| tree not_tmpl = STRIP_TEMPLATE (decl); |
| if (installing) |
| { |
| /* We must be inserting for the first time. */ |
| gcc_assert (!*slot); |
| *slot = decl; |
| } |
| else |
| /* If this is not the mergeable entity, it should not be in the |
| table. If it is a non-global-module mergeable entity, it |
| should be in the table. Global module entities could have been |
| defined textually in the current TU and so might or might not |
| be present. */ |
| gcc_assert (!is_duplicate (decl) |
| ? !slot |
| : (slot |
| || !DECL_LANG_SPECIFIC (not_tmpl) |
| || !DECL_MODULE_PURVIEW_P (not_tmpl) |
| || (!DECL_MODULE_IMPORT_P (not_tmpl) |
| && header_module_p ()))); |
| |
| if (not_tmpl != decl) |
| gcc_assert (!note_defs->find_slot (not_tmpl, NO_INSERT)); |
| #endif |
| } |
| |
| void |
| trees_out::assert_definition (tree decl ATTRIBUTE_UNUSED) |
| { |
| #if CHECKING_P |
| tree *slot = note_defs->find_slot (decl, INSERT); |
| gcc_assert (!*slot); |
| *slot = decl; |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| gcc_assert (!note_defs->find_slot (DECL_TEMPLATE_RESULT (decl), NO_INSERT)); |
| #endif |
| } |
| |
| /********************************************************************/ |
| static bool |
| noisy_p () |
| { |
| if (quiet_flag) |
| return false; |
| |
| pp_needs_newline (global_dc->printer) = true; |
| diagnostic_set_last_function (global_dc, (diagnostic_info *) NULL); |
| |
| return true; |
| } |
| |
| /* Set the cmi repo. Strip trailing '/', '.' becomes NULL. */ |
| |
| static void |
| set_cmi_repo (const char *r) |
| { |
| XDELETEVEC (cmi_repo); |
| XDELETEVEC (cmi_path); |
| cmi_path_alloc = 0; |
| |
| cmi_repo = NULL; |
| cmi_repo_length = 0; |
| |
| if (!r || !r[0]) |
| return; |
| |
| size_t len = strlen (r); |
| cmi_repo = XNEWVEC (char, len + 1); |
| memcpy (cmi_repo, r, len + 1); |
| |
| if (len > 1 && IS_DIR_SEPARATOR (cmi_repo[len-1])) |
| len--; |
| if (len == 1 && cmi_repo[0] == '.') |
| len--; |
| cmi_repo[len] = 0; |
| cmi_repo_length = len; |
| } |
| |
| /* TO is a repo-relative name. Provide one that we may use from where |
| we are. */ |
| |
| static const char * |
| maybe_add_cmi_prefix (const char *to, size_t *len_p = NULL) |
| { |
| size_t len = len_p || cmi_repo_length ? strlen (to) : 0; |
| |
| if (cmi_repo_length && !IS_ABSOLUTE_PATH (to)) |
| { |
| if (cmi_path_alloc < cmi_repo_length + len + 2) |
| { |
| XDELETEVEC (cmi_path); |
| cmi_path_alloc = cmi_repo_length + len * 2 + 2; |
| cmi_path = XNEWVEC (char, cmi_path_alloc); |
| |
| memcpy (cmi_path, cmi_repo, cmi_repo_length); |
| cmi_path[cmi_repo_length] = DIR_SEPARATOR; |
| } |
| |
| memcpy (&cmi_path[cmi_repo_length + 1], to, len + 1); |
| len += cmi_repo_length + 1; |
| to = cmi_path; |
| } |
| |
| if (len_p) |
| *len_p = len; |
| |
| return to; |
| } |
| |
| /* Try and create the directories of PATH. */ |
| |
| static void |
| create_dirs (char *path) |
| { |
| /* Try and create the missing directories. */ |
| for (char *base = path; *base; base++) |
| if (IS_DIR_SEPARATOR (*base)) |
| { |
| char sep = *base; |
| *base = 0; |
| int failed = mkdir (path, S_IRWXU | S_IRWXG | S_IRWXO); |
| dump () && dump ("Mkdir ('%s') errno:=%u", path, failed ? errno : 0); |
| *base = sep; |
| if (failed |
| /* Maybe racing with another creator (of a *different* |
| module). */ |
| && errno != EEXIST) |
| break; |
| } |
| } |
| |
| /* Given a CLASSTYPE_DECL_LIST VALUE get the the template friend decl, |
| if that's what this is. */ |
| |
| static tree |
| friend_from_decl_list (tree frnd) |
| { |
| tree res = frnd; |
| |
| if (TREE_CODE (frnd) != TEMPLATE_DECL) |
| { |
| tree tmpl = NULL_TREE; |
| if (TYPE_P (frnd)) |
| { |
| res = TYPE_NAME (frnd); |
| if (CLASSTYPE_TEMPLATE_INFO (frnd)) |
| tmpl = CLASSTYPE_TI_TEMPLATE (frnd); |
| } |
| else if (DECL_TEMPLATE_INFO (frnd)) |
| { |
| tmpl = DECL_TI_TEMPLATE (frnd); |
| if (TREE_CODE (tmpl) != TEMPLATE_DECL) |
| tmpl = NULL_TREE; |
| } |
| |
| if (tmpl && DECL_TEMPLATE_RESULT (tmpl) == res) |
| res = tmpl; |
| } |
| |
| return res; |
| } |
| |
| static tree |
| find_enum_member (tree ctx, tree name) |
| { |
| for (tree values = TYPE_VALUES (ctx); |
| values; values = TREE_CHAIN (values)) |
| if (DECL_NAME (TREE_VALUE (values)) == name) |
| return TREE_VALUE (values); |
| |
| return NULL_TREE; |
| } |
| |
| /********************************************************************/ |
| /* Instrumentation gathered writing bytes. */ |
| |
| void |
| bytes_out::instrument () |
| { |
| dump ("Wrote %u bytes in %u blocks", lengths[3], spans[3]); |
| dump ("Wrote %u bits in %u bytes", lengths[0] + lengths[1], lengths[2]); |
| for (unsigned ix = 0; ix < 2; ix++) |
| dump (" %u %s spans of %R bits", spans[ix], |
| ix ? "one" : "zero", lengths[ix], spans[ix]); |
| dump (" %u blocks with %R bits padding", spans[2], |
| lengths[2] * 8 - (lengths[0] + lengths[1]), spans[2]); |
| } |
| |
| /* Instrumentation gathered writing trees. */ |
| void |
| trees_out::instrument () |
| { |
| if (dump ("")) |
| { |
| bytes_out::instrument (); |
| dump ("Wrote:"); |
| dump (" %u decl trees", decl_val_count); |
| dump (" %u other trees", tree_val_count); |
| dump (" %u back references", back_ref_count); |
| dump (" %u null trees", null_count); |
| } |
| } |
| |
| /* Setup and teardown for a tree walk. */ |
| |
| void |
| trees_out::begin () |
| { |
| gcc_assert (!streaming_p () || !tree_map.elements ()); |
| |
| mark_trees (); |
| if (streaming_p ()) |
| parent::begin (); |
| } |
| |
| unsigned |
| trees_out::end (elf_out *sink, unsigned name, unsigned *crc_ptr) |
| { |
| gcc_checking_assert (streaming_p ()); |
| |
| unmark_trees (); |
| return parent::end (sink, name, crc_ptr); |
| } |
| |
| void |
| trees_out::end () |
| { |
| gcc_assert (!streaming_p ()); |
| |
| unmark_trees (); |
| /* Do not parent::end -- we weren't streaming. */ |
| } |
| |
| void |
| trees_out::mark_trees () |
| { |
| if (size_t size = tree_map.elements ()) |
| { |
| /* This isn't our first rodeo, destroy and recreate the |
| tree_map. I'm a bad bad man. Use the previous size as a |
| guess for the next one (so not all bad). */ |
| tree_map.~ptr_int_hash_map (); |
| new (&tree_map) ptr_int_hash_map (size); |
| } |
| |
| /* Install the fixed trees, with +ve references. */ |
| unsigned limit = fixed_trees->length (); |
| for (unsigned ix = 0; ix != limit; ix++) |
| { |
| tree val = (*fixed_trees)[ix]; |
| bool existed = tree_map.put (val, ix + tag_fixed); |
| gcc_checking_assert (!TREE_VISITED (val) && !existed); |
| TREE_VISITED (val) = true; |
| } |
| |
| ref_num = 0; |
| } |
| |
| /* Unmark the trees we encountered */ |
| |
| void |
| trees_out::unmark_trees () |
| { |
| ptr_int_hash_map::iterator end (tree_map.end ()); |
| for (ptr_int_hash_map::iterator iter (tree_map.begin ()); iter != end; ++iter) |
| { |
| tree node = reinterpret_cast<tree> ((*iter).first); |
| int ref = (*iter).second; |
| /* We should have visited the node, and converted its mergeable |
| reference to a regular reference. */ |
| gcc_checking_assert (TREE_VISITED (node) |
| && (ref <= tag_backref || ref >= tag_fixed)); |
| TREE_VISITED (node) = false; |
| } |
| } |
| |
| /* Mark DECL for by-value walking. We do this by inserting it into |
| the tree map with a reference of zero. May be called multiple |
| times on the same node. */ |
| |
| void |
| trees_out::mark_by_value (tree decl) |
| { |
| gcc_checking_assert (DECL_P (decl) |
| /* Enum consts are INTEGER_CSTS. */ |
| || TREE_CODE (decl) == INTEGER_CST |
| || TREE_CODE (decl) == TREE_BINFO); |
| |
| if (TREE_VISITED (decl)) |
| /* Must already be forced or fixed. */ |
| gcc_checking_assert (*tree_map.get (decl) >= tag_value); |
| else |
| { |
| bool existed = tree_map.put (decl, tag_value); |
| gcc_checking_assert (!existed); |
| TREE_VISITED (decl) = true; |
| } |
| } |
| |
| int |
| trees_out::get_tag (tree t) |
| { |
| gcc_checking_assert (TREE_VISITED (t)); |
| return *tree_map.get (t); |
| } |
| |
| /* Insert T into the map, return its tag number. */ |
| |
| int |
| trees_out::insert (tree t, walk_kind walk) |
| { |
| gcc_checking_assert (walk != WK_normal || !TREE_VISITED (t)); |
| int tag = --ref_num; |
| bool existed; |
| int &slot = tree_map.get_or_insert (t, &existed); |
| gcc_checking_assert (TREE_VISITED (t) == existed |
| && (!existed |
| || (walk == WK_value && slot == tag_value))); |
| TREE_VISITED (t) = true; |
| slot = tag; |
| |
| return tag; |
| } |
| |
| /* Insert T into the backreference array. Return its back reference |
| number. */ |
| |
| int |
| trees_in::insert (tree t) |
| { |
| gcc_checking_assert (t || get_overrun ()); |
| back_refs.safe_push (t); |
| return -(int)back_refs.length (); |
| } |
| |
| /* A chained set of decls. */ |
| |
| void |
| trees_out::chained_decls (tree decls) |
| { |
| for (; decls; decls = DECL_CHAIN (decls)) |
| { |
| if (VAR_OR_FUNCTION_DECL_P (decls) |
| && DECL_LOCAL_DECL_P (decls)) |
| { |
| /* Make sure this is the first encounter, and mark for |
| walk-by-value. */ |
| gcc_checking_assert (!TREE_VISITED (decls) |
| && !DECL_TEMPLATE_INFO (decls)); |
| mark_by_value (decls); |
| } |
| tree_node (decls); |
| } |
| tree_node (NULL_TREE); |
| } |
| |
| tree |
| trees_in::chained_decls () |
| { |
| tree decls = NULL_TREE; |
| for (tree *chain = &decls;;) |
| if (tree decl = tree_node ()) |
| { |
| if (!DECL_P (decl) || DECL_CHAIN (decl)) |
| { |
| set_overrun (); |
| break; |
| } |
| *chain = decl; |
| chain = &DECL_CHAIN (decl); |
| } |
| else |
| break; |
| |
| return decls; |
| } |
| |
| /* A vector of decls following DECL_CHAIN. */ |
| |
| void |
| trees_out::vec_chained_decls (tree decls) |
| { |
| if (streaming_p ()) |
| { |
| unsigned len = 0; |
| |
| for (tree decl = decls; decl; decl = DECL_CHAIN (decl)) |
| len++; |
| u (len); |
| } |
| |
| for (tree decl = decls; decl; decl = DECL_CHAIN (decl)) |
| { |
| if (DECL_IMPLICIT_TYPEDEF_P (decl) |
| && TYPE_NAME (TREE_TYPE (decl)) != decl) |
| /* An anonynmous struct with a typedef name. An odd thing to |
| write. */ |
| tree_node (NULL_TREE); |
| else |
| tree_node (decl); |
| } |
| } |
| |
| vec<tree, va_heap> * |
| trees_in::vec_chained_decls () |
| { |
| vec<tree, va_heap> *v = NULL; |
| |
| if (unsigned len = u ()) |
| { |
| vec_alloc (v, len); |
| |
| for (unsigned ix = 0; ix < len; ix++) |
| { |
| tree decl = tree_node (); |
| if (decl && !DECL_P (decl)) |
| { |
| set_overrun (); |
| break; |
| } |
| v->quick_push (decl); |
| } |
| |
| if (get_overrun ()) |
| { |
| vec_free (v); |
| v = NULL; |
| } |
| } |
| |
| return v; |
| } |
| |
| /* A vector of trees. */ |
| |
| void |
| trees_out::tree_vec (vec<tree, va_gc> *v) |
| { |
| unsigned len = vec_safe_length (v); |
| if (streaming_p ()) |
| u (len); |
| for (unsigned ix = 0; ix != len; ix++) |
| tree_node ((*v)[ix]); |
| } |
| |
| vec<tree, va_gc> * |
| trees_in::tree_vec () |
| { |
| vec<tree, va_gc> *v = NULL; |
| if (unsigned len = u ()) |
| { |
| vec_alloc (v, len); |
| for (unsigned ix = 0; ix != len; ix++) |
| v->quick_push (tree_node ()); |
| } |
| return v; |
| } |
| |
| /* A vector of tree pairs. */ |
| |
| void |
| trees_out::tree_pair_vec (vec<tree_pair_s, va_gc> *v) |
| { |
| unsigned len = vec_safe_length (v); |
| if (streaming_p ()) |
| u (len); |
| if (len) |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree_pair_s const &s = (*v)[ix]; |
| tree_node (s.purpose); |
| tree_node (s.value); |
| } |
| } |
| |
| vec<tree_pair_s, va_gc> * |
| trees_in::tree_pair_vec () |
| { |
| vec<tree_pair_s, va_gc> *v = NULL; |
| if (unsigned len = u ()) |
| { |
| vec_alloc (v, len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree_pair_s s; |
| s.purpose = tree_node (); |
| s.value = tree_node (); |
| v->quick_push (s); |
| } |
| } |
| return v; |
| } |
| |
| void |
| trees_out::tree_list (tree list, bool has_purpose) |
| { |
| for (; list; list = TREE_CHAIN (list)) |
| { |
| gcc_checking_assert (TREE_VALUE (list)); |
| tree_node (TREE_VALUE (list)); |
| if (has_purpose) |
| tree_node (TREE_PURPOSE (list)); |
| } |
| tree_node (NULL_TREE); |
| } |
| |
| tree |
| trees_in::tree_list (bool has_purpose) |
| { |
| tree res = NULL_TREE; |
| |
| for (tree *chain = &res; tree value = tree_node (); |
| chain = &TREE_CHAIN (*chain)) |
| { |
| tree purpose = has_purpose ? tree_node () : NULL_TREE; |
| *chain = build_tree_list (purpose, value); |
| } |
| |
| return res; |
| } |
| /* Start tree write. Write information to allocate the receiving |
| node. */ |
| |
| void |
| trees_out::start (tree t, bool code_streamed) |
| { |
| if (TYPE_P (t)) |
| { |
| enum tree_code code = TREE_CODE (t); |
| gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t); |
| /* All these types are TYPE_NON_COMMON. */ |
| gcc_checking_assert (code == RECORD_TYPE |
| || code == UNION_TYPE |
| || code == ENUMERAL_TYPE |
| || code == TEMPLATE_TYPE_PARM |
| || code == TEMPLATE_TEMPLATE_PARM |
| || code == BOUND_TEMPLATE_TEMPLATE_PARM); |
| } |
| |
| if (!code_streamed) |
| u (TREE_CODE (t)); |
| |
| switch (TREE_CODE (t)) |
| { |
| default: |
| if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_vl_exp) |
| u (VL_EXP_OPERAND_LENGTH (t)); |
| break; |
| |
| case INTEGER_CST: |
| u (TREE_INT_CST_NUNITS (t)); |
| u (TREE_INT_CST_EXT_NUNITS (t)); |
| u (TREE_INT_CST_OFFSET_NUNITS (t)); |
| break; |
| |
| case OMP_CLAUSE: |
| state->extensions |= SE_OPENMP; |
| u (OMP_CLAUSE_CODE (t)); |
| break; |
| |
| case STRING_CST: |
| str (TREE_STRING_POINTER (t), TREE_STRING_LENGTH (t)); |
| break; |
| |
| case VECTOR_CST: |
| u (VECTOR_CST_LOG2_NPATTERNS (t)); |
| u (VECTOR_CST_NELTS_PER_PATTERN (t)); |
| break; |
| |
| case TREE_BINFO: |
| u (BINFO_N_BASE_BINFOS (t)); |
| break; |
| |
| case TREE_VEC: |
| u (TREE_VEC_LENGTH (t)); |
| break; |
| |
| case FIXED_CST: |
| case POLY_INT_CST: |
| gcc_unreachable (); /* Not supported in C++. */ |
| break; |
| |
| case IDENTIFIER_NODE: |
| case SSA_NAME: |
| case TARGET_MEM_REF: |
| case TRANSLATION_UNIT_DECL: |
| /* We shouldn't meet these. */ |
| gcc_unreachable (); |
| break; |
| } |
| } |
| |
| /* Start tree read. Allocate the receiving node. */ |
| |
| tree |
| trees_in::start (unsigned code) |
| { |
| tree t = NULL_TREE; |
| |
| if (!code) |
| code = u (); |
| |
| switch (code) |
| { |
| default: |
| if (code >= MAX_TREE_CODES) |
| { |
| fail: |
| set_overrun (); |
| return NULL_TREE; |
| } |
| else if (TREE_CODE_CLASS (code) == tcc_vl_exp) |
| { |
| unsigned ops = u (); |
| t = build_vl_exp (tree_code (code), ops); |
| } |
| else |
| t = make_node (tree_code (code)); |
| break; |
| |
| case INTEGER_CST: |
| { |
| unsigned n = u (); |
| unsigned e = u (); |
| t = make_int_cst (n, e); |
| TREE_INT_CST_OFFSET_NUNITS(t) = u (); |
| } |
| break; |
| |
| case OMP_CLAUSE: |
| { |
| if (!(state->extensions & SE_OPENMP)) |
| goto fail; |
| |
| unsigned omp_code = u (); |
| t = build_omp_clause (UNKNOWN_LOCATION, omp_clause_code (omp_code)); |
| } |
| break; |
| |
| case STRING_CST: |
| { |
| size_t l; |
| const char *chars = str (&l); |
| t = build_string (l, chars); |
| } |
| break; |
| |
| case VECTOR_CST: |
| { |
| unsigned log2_npats = u (); |
| unsigned elts_per = u (); |
| t = make_vector (log2_npats, elts_per); |
| } |
| break; |
| |
| case TREE_BINFO: |
| t = make_tree_binfo (u ()); |
| break; |
| |
| case TREE_VEC: |
| t = make_tree_vec (u ()); |
| break; |
| |
| case FIXED_CST: |
| case IDENTIFIER_NODE: |
| case POLY_INT_CST: |
| case SSA_NAME: |
| case TARGET_MEM_REF: |
| case TRANSLATION_UNIT_DECL: |
| goto fail; |
| } |
| |
| return t; |
| } |
| |
| /* The structure streamers access the raw fields, because the |
| alternative, of using the accessor macros can require using |
| different accessors for the same underlying field, depending on the |
| tree code. That's both confusing and annoying. */ |
| |
| /* Read & write the core boolean flags. */ |
| |
| void |
| trees_out::core_bools (tree t) |
| { |
| #define WB(X) (b (X)) |
| tree_code code = TREE_CODE (t); |
| |
| WB (t->base.side_effects_flag); |
| WB (t->base.constant_flag); |
| WB (t->base.addressable_flag); |
| WB (t->base.volatile_flag); |
| WB (t->base.readonly_flag); |
| /* base.asm_written_flag is a property of the current TU's use of |
| this decl. */ |
| WB (t->base.nowarning_flag); |
| /* base.visited read as zero (it's set for writer, because that's |
| how we mark nodes). */ |
| /* base.used_flag is not streamed. Readers may set TREE_USED of |
| decls they use. */ |
| WB (t->base.nothrow_flag); |
| WB (t->base.static_flag); |
| if (TREE_CODE_CLASS (code) != tcc_type) |
| /* This is TYPE_CACHED_VALUES_P for types. */ |
| WB (t->base.public_flag); |
| WB (t->base.private_flag); |
| WB (t->base.protected_flag); |
| WB (t->base.deprecated_flag); |
| WB (t->base.default_def_flag); |
| |
| switch (code) |
| { |
| case CALL_EXPR: |
| case INTEGER_CST: |
| case SSA_NAME: |
| case TARGET_MEM_REF: |
| case TREE_VEC: |
| /* These use different base.u fields. */ |
| break; |
| |
| default: |
| WB (t->base.u.bits.lang_flag_0); |
| bool flag_1 = t->base.u.bits.lang_flag_1; |
| if (!flag_1) |
| ; |
| else if (code == TEMPLATE_INFO) |
| /* This is TI_PENDING_TEMPLATE_FLAG, not relevant to reader. */ |
| flag_1 = false; |
| else if (code == VAR_DECL) |
| { |
| /* This is DECL_INITIALIZED_P. */ |
| if (TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL) |
| /* We'll set this when reading the definition. */ |
| flag_1 = false; |
| } |
| WB (flag_1); |
| WB (t->base.u.bits.lang_flag_2); |
| WB (t->base.u.bits.lang_flag_3); |
| WB (t->base.u.bits.lang_flag_4); |
| WB (t->base.u.bits.lang_flag_5); |
| WB (t->base.u.bits.lang_flag_6); |
| WB (t->base.u.bits.saturating_flag); |
| WB (t->base.u.bits.unsigned_flag); |
| WB (t->base.u.bits.packed_flag); |
| WB (t->base.u.bits.user_align); |
| WB (t->base.u.bits.nameless_flag); |
| WB (t->base.u.bits.atomic_flag); |
| break; |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) |
| { |
| WB (t->type_common.no_force_blk_flag); |
| WB (t->type_common.needs_constructing_flag); |
| WB (t->type_common.transparent_aggr_flag); |
| WB (t->type_common.restrict_flag); |
| WB (t->type_common.string_flag); |
| WB (t->type_common.lang_flag_0); |
| WB (t->type_common.lang_flag_1); |
| WB (t->type_common.lang_flag_2); |
| WB (t->type_common.lang_flag_3); |
| WB (t->type_common.lang_flag_4); |
| WB (t->type_common.lang_flag_5); |
| WB (t->type_common.lang_flag_6); |
| WB (t->type_common.typeless_storage); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) |
| { |
| WB (t->decl_common.nonlocal_flag); |
| WB (t->decl_common.virtual_flag); |
| WB (t->decl_common.ignored_flag); |
| WB (t->decl_common.abstract_flag); |
| WB (t->decl_common.artificial_flag); |
| WB (t->decl_common.preserve_flag); |
| WB (t->decl_common.debug_expr_is_from); |
| WB (t->decl_common.lang_flag_0); |
| WB (t->decl_common.lang_flag_1); |
| WB (t->decl_common.lang_flag_2); |
| WB (t->decl_common.lang_flag_3); |
| WB (t->decl_common.lang_flag_4); |
| WB (t->decl_common.lang_flag_5); |
| WB (t->decl_common.lang_flag_6); |
| WB (t->decl_common.lang_flag_7); |
| WB (t->decl_common.lang_flag_8); |
| WB (t->decl_common.decl_flag_0); |
| |
| { |
| /* DECL_EXTERNAL -> decl_flag_1 |
| == it is defined elsewhere |
| DECL_NOT_REALLY_EXTERN -> base.not_really_extern |
| == that was a lie, it is here */ |
| |
| bool is_external = t->decl_common.decl_flag_1; |
| if (!is_external) |
| /* decl_flag_1 is DECL_EXTERNAL. Things we emit here, might |
| well be external from the POV of an importer. */ |
| // FIXME: Do we need to know if this is a TEMPLATE_RESULT -- |
| // a flag from the caller? |
| switch (code) |
| { |
| default: |
| break; |
| |
| case VAR_DECL: |
| if (TREE_PUBLIC (t) |
| && !DECL_VAR_DECLARED_INLINE_P (t)) |
| is_external = true; |
| break; |
| |
| case FUNCTION_DECL: |
| if (TREE_PUBLIC (t) |
| && !DECL_DECLARED_INLINE_P (t)) |
| is_external = true; |
| break; |
| } |
| WB (is_external); |
| } |
| |
| WB (t->decl_common.decl_flag_2); |
| WB (t->decl_common.decl_flag_3); |
| WB (t->decl_common.not_gimple_reg_flag); |
| WB (t->decl_common.decl_by_reference_flag); |
| WB (t->decl_common.decl_read_flag); |
| WB (t->decl_common.decl_nonshareable_flag); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) |
| { |
| WB (t->decl_with_vis.defer_output); |
| WB (t->decl_with_vis.hard_register); |
| WB (t->decl_with_vis.common_flag); |
| WB (t->decl_with_vis.in_text_section); |
| WB (t->decl_with_vis.in_constant_pool); |
| WB (t->decl_with_vis.dllimport_flag); |
| WB (t->decl_with_vis.weak_flag); |
| WB (t->decl_with_vis.seen_in_bind_expr); |
| WB (t->decl_with_vis.comdat_flag); |
| WB (t->decl_with_vis.visibility_specified); |
| WB (t->decl_with_vis.init_priority_p); |
| WB (t->decl_with_vis.shadowed_for_var_p); |
| WB (t->decl_with_vis.cxx_constructor); |
| WB (t->decl_with_vis.cxx_destructor); |
| WB (t->decl_with_vis.final); |
| WB (t->decl_with_vis.regdecl_flag); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL)) |
| { |
| WB (t->function_decl.static_ctor_flag); |
| WB (t->function_decl.static_dtor_flag); |
| WB (t->function_decl.uninlinable); |
| WB (t->function_decl.possibly_inlined); |
| WB (t->function_decl.novops_flag); |
| WB (t->function_decl.returns_twice_flag); |
| WB (t->function_decl.malloc_flag); |
| WB (t->function_decl.declared_inline_flag); |
| WB (t->function_decl.no_inline_warning_flag); |
| WB (t->function_decl.no_instrument_function_entry_exit); |
| WB (t->function_decl.no_limit_stack); |
| WB (t->function_decl.disregard_inline_limits); |
| WB (t->function_decl.pure_flag); |
| WB (t->function_decl.looping_const_or_pure_flag); |
| |
| WB (t->function_decl.has_debug_args_flag); |
| WB (t->function_decl.versioned_function); |
| |
| /* decl_type is a (misnamed) 2 bit discriminator. */ |
| unsigned kind = t->function_decl.decl_type; |
| WB ((kind >> 0) & 1); |
| WB ((kind >> 1) & 1); |
| } |
| #undef WB |
| } |
| |
| bool |
| trees_in::core_bools (tree t) |
| { |
| #define RB(X) ((X) = b ()) |
| tree_code code = TREE_CODE (t); |
| |
| RB (t->base.side_effects_flag); |
| RB (t->base.constant_flag); |
| RB (t->base.addressable_flag); |
| RB (t->base.volatile_flag); |
| RB (t->base.readonly_flag); |
| /* base.asm_written_flag is not streamed. */ |
| RB (t->base.nowarning_flag); |
| /* base.visited is not streamed. */ |
| /* base.used_flag is not streamed. */ |
| RB (t->base.nothrow_flag); |
| RB (t->base.static_flag); |
| if (TREE_CODE_CLASS (code) != tcc_type) |
| RB (t->base.public_flag); |
| RB (t->base.private_flag); |
| RB (t->base.protected_flag); |
| RB (t->base.deprecated_flag); |
| RB (t->base.default_def_flag); |
| |
| switch (code) |
| { |
| case CALL_EXPR: |
| case INTEGER_CST: |
| case SSA_NAME: |
| case TARGET_MEM_REF: |
| case TREE_VEC: |
| /* These use different base.u fields. */ |
| break; |
| |
| default: |
| RB (t->base.u.bits.lang_flag_0); |
| RB (t->base.u.bits.lang_flag_1); |
| RB (t->base.u.bits.lang_flag_2); |
| RB (t->base.u.bits.lang_flag_3); |
| RB (t->base.u.bits.lang_flag_4); |
| RB (t->base.u.bits.lang_flag_5); |
| RB (t->base.u.bits.lang_flag_6); |
| RB (t->base.u.bits.saturating_flag); |
| RB (t->base.u.bits.unsigned_flag); |
| RB (t->base.u.bits.packed_flag); |
| RB (t->base.u.bits.user_align); |
| RB (t->base.u.bits.nameless_flag); |
| RB (t->base.u.bits.atomic_flag); |
| break; |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) |
| { |
| RB (t->type_common.no_force_blk_flag); |
| RB (t->type_common.needs_constructing_flag); |
| RB (t->type_common.transparent_aggr_flag); |
| RB (t->type_common.restrict_flag); |
| RB (t->type_common.string_flag); |
| RB (t->type_common.lang_flag_0); |
| RB (t->type_common.lang_flag_1); |
| RB (t->type_common.lang_flag_2); |
| RB (t->type_common.lang_flag_3); |
| RB (t->type_common.lang_flag_4); |
| RB (t->type_common.lang_flag_5); |
| RB (t->type_common.lang_flag_6); |
| RB (t->type_common.typeless_storage); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) |
| { |
| RB (t->decl_common.nonlocal_flag); |
| RB (t->decl_common.virtual_flag); |
| RB (t->decl_common.ignored_flag); |
| RB (t->decl_common.abstract_flag); |
| RB (t->decl_common.artificial_flag); |
| RB (t->decl_common.preserve_flag); |
| RB (t->decl_common.debug_expr_is_from); |
| RB (t->decl_common.lang_flag_0); |
| RB (t->decl_common.lang_flag_1); |
| RB (t->decl_common.lang_flag_2); |
| RB (t->decl_common.lang_flag_3); |
| RB (t->decl_common.lang_flag_4); |
| RB (t->decl_common.lang_flag_5); |
| RB (t->decl_common.lang_flag_6); |
| RB (t->decl_common.lang_flag_7); |
| RB (t->decl_common.lang_flag_8); |
| RB (t->decl_common.decl_flag_0); |
| RB (t->decl_common.decl_flag_1); |
| RB (t->decl_common.decl_flag_2); |
| RB (t->decl_common.decl_flag_3); |
| RB (t->decl_common.not_gimple_reg_flag); |
| RB (t->decl_common.decl_by_reference_flag); |
| RB (t->decl_common.decl_read_flag); |
| RB (t->decl_common.decl_nonshareable_flag); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) |
| { |
| RB (t->decl_with_vis.defer_output); |
| RB (t->decl_with_vis.hard_register); |
| RB (t->decl_with_vis.common_flag); |
| RB (t->decl_with_vis.in_text_section); |
| RB (t->decl_with_vis.in_constant_pool); |
| RB (t->decl_with_vis.dllimport_flag); |
| RB (t->decl_with_vis.weak_flag); |
| RB (t->decl_with_vis.seen_in_bind_expr); |
| RB (t->decl_with_vis.comdat_flag); |
| RB (t->decl_with_vis.visibility_specified); |
| RB (t->decl_with_vis.init_priority_p); |
| RB (t->decl_with_vis.shadowed_for_var_p); |
| RB (t->decl_with_vis.cxx_constructor); |
| RB (t->decl_with_vis.cxx_destructor); |
| RB (t->decl_with_vis.final); |
| RB (t->decl_with_vis.regdecl_flag); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL)) |
| { |
| RB (t->function_decl.static_ctor_flag); |
| RB (t->function_decl.static_dtor_flag); |
| RB (t->function_decl.uninlinable); |
| RB (t->function_decl.possibly_inlined); |
| RB (t->function_decl.novops_flag); |
| RB (t->function_decl.returns_twice_flag); |
| RB (t->function_decl.malloc_flag); |
| RB (t->function_decl.declared_inline_flag); |
| RB (t->function_decl.no_inline_warning_flag); |
| RB (t->function_decl.no_instrument_function_entry_exit); |
| RB (t->function_decl.no_limit_stack); |
| RB (t->function_decl.disregard_inline_limits); |
| RB (t->function_decl.pure_flag); |
| RB (t->function_decl.looping_const_or_pure_flag); |
| |
| RB (t->function_decl.has_debug_args_flag); |
| RB (t->function_decl.versioned_function); |
| |
| /* decl_type is a (misnamed) 2 bit discriminator. */ |
| unsigned kind = 0; |
| kind |= unsigned (b ()) << 0; |
| kind |= unsigned (b ()) << 1; |
| t->function_decl.decl_type = function_decl_type (kind); |
| } |
| #undef RB |
| return !get_overrun (); |
| } |
| |
| void |
| trees_out::lang_decl_bools (tree t) |
| { |
| #define WB(X) (b (X)) |
| const struct lang_decl *lang = DECL_LANG_SPECIFIC (t); |
| |
| WB (lang->u.base.language == lang_cplusplus); |
| WB ((lang->u.base.use_template >> 0) & 1); |
| WB ((lang->u.base.use_template >> 1) & 1); |
| /* Do not write lang->u.base.not_really_extern, importer will set |
| when reading the definition (if any). */ |
| WB (lang->u.base.initialized_in_class); |
| WB (lang->u.base.threadprivate_or_deleted_p); |
| /* Do not write lang->u.base.anticipated_p, it is a property of the |
| current TU. */ |
| WB (lang->u.base.friend_or_tls); |
| WB (lang->u.base.unknown_bound_p); |
| /* Do not write lang->u.base.odr_used, importer will recalculate if |
| they do ODR use this decl. */ |
| WB (lang->u.base.concept_p); |
| WB (lang->u.base.var_declared_inline_p); |
| WB (lang->u.base.dependent_init_p); |
| /* When building a header unit, everthing is marked as purview, but |
| that's the GM purview, so not what the importer will mean */ |
| WB (lang->u.base.module_purview_p && !header_module_p ()); |
| if (VAR_OR_FUNCTION_DECL_P (t)) |
| WB (lang->u.base.module_attached_p); |
| switch (lang->u.base.selector) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case lds_fn: /* lang_decl_fn. */ |
| WB (lang->u.fn.global_ctor_p); |
| WB (lang->u.fn.global_dtor_p); |
| WB (lang->u.fn.static_function); |
| WB (lang->u.fn.pure_virtual); |
| WB (lang->u.fn.defaulted_p); |
| WB (lang->u.fn.has_in_charge_parm_p); |
| WB (lang->u.fn.has_vtt_parm_p); |
| /* There shouldn't be a pending inline at this point. */ |
| gcc_assert (!lang->u.fn.pending_inline_p); |
| WB (lang->u.fn.nonconverting); |
| WB (lang->u.fn.thunk_p); |
| WB (lang->u.fn.this_thunk_p); |
| /* Do not stream lang->u.hidden_friend_p, it is a property of |
| the TU. */ |
| WB (lang->u.fn.omp_declare_reduction_p); |
| WB (lang->u.fn.has_dependent_explicit_spec_p); |
| WB (lang->u.fn.immediate_fn_p); |
| WB (lang->u.fn.maybe_deleted); |
| goto lds_min; |
| |
| case lds_decomp: /* lang_decl_decomp. */ |
| /* No bools. */ |
| goto lds_min; |
| |
| case lds_min: /* lang_decl_min. */ |
| lds_min: |
| /* No bools. */ |
| break; |
| |
| case lds_ns: /* lang_decl_ns. */ |
| /* No bools. */ |
| break; |
| |
| case lds_parm: /* lang_decl_parm. */ |
| /* No bools. */ |
| break; |
| } |
| #undef WB |
| } |
| |
| bool |
| trees_in::lang_decl_bools (tree t) |
| { |
| #define RB(X) ((X) = b ()) |
| struct lang_decl *lang = DECL_LANG_SPECIFIC (t); |
| |
| lang->u.base.language = b () ? lang_cplusplus : lang_c; |
| unsigned v; |
| v = b () << 0; |
| v |= b () << 1; |
| lang->u.base.use_template = v; |
| /* lang->u.base.not_really_extern is not streamed. */ |
| RB (lang->u.base.initialized_in_class); |
| RB (lang->u.base.threadprivate_or_deleted_p); |
| /* lang->u.base.anticipated_p is not streamed. */ |
| RB (lang->u.base.friend_or_tls); |
| RB (lang->u.base.unknown_bound_p); |
| /* lang->u.base.odr_used is not streamed. */ |
| RB (lang->u.base.concept_p); |
| RB (lang->u.base.var_declared_inline_p); |
| RB (lang->u.base.dependent_init_p); |
| RB (lang->u.base.module_purview_p); |
| if (VAR_OR_FUNCTION_DECL_P (t)) |
| RB (lang->u.base.module_attached_p); |
| switch (lang->u.base.selector) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case lds_fn: /* lang_decl_fn. */ |
| RB (lang->u.fn.global_ctor_p); |
| RB (lang->u.fn.global_dtor_p); |
| RB (lang->u.fn.static_function); |
| RB (lang->u.fn.pure_virtual); |
| RB (lang->u.fn.defaulted_p); |
| RB (lang->u.fn.has_in_charge_parm_p); |
| RB (lang->u.fn.has_vtt_parm_p); |
| RB (lang->u.fn.nonconverting); |
| RB (lang->u.fn.thunk_p); |
| RB (lang->u.fn.this_thunk_p); |
| /* lang->u.fn.hidden_friend_p is not streamed. */ |
| RB (lang->u.fn.omp_declare_reduction_p); |
| RB (lang->u.fn.has_dependent_explicit_spec_p); |
| RB (lang->u.fn.immediate_fn_p); |
| RB (lang->u.fn.maybe_deleted); |
| goto lds_min; |
| |
| case lds_decomp: /* lang_decl_decomp. */ |
| /* No bools. */ |
| goto lds_min; |
| |
| case lds_min: /* lang_decl_min. */ |
| lds_min: |
| /* No bools. */ |
| break; |
| |
| case lds_ns: /* lang_decl_ns. */ |
| /* No bools. */ |
| break; |
| |
| case lds_parm: /* lang_decl_parm. */ |
| /* No bools. */ |
| break; |
| } |
| #undef RB |
| return !get_overrun (); |
| } |
| |
| void |
| trees_out::lang_type_bools (tree t) |
| { |
| #define WB(X) (b (X)) |
| const struct lang_type *lang = TYPE_LANG_SPECIFIC (t); |
| |
| WB (lang->has_type_conversion); |
| WB (lang->has_copy_ctor); |
| WB (lang->has_default_ctor); |
| WB (lang->const_needs_init); |
| WB (lang->ref_needs_init); |
| WB (lang->has_const_copy_assign); |
| WB ((lang->use_template >> 0) & 1); |
| WB ((lang->use_template >> 1) & 1); |
| |
| WB (lang->has_mutable); |
| WB (lang->com_interface); |
| WB (lang->non_pod_class); |
| WB (lang->nearly_empty_p); |
| WB (lang->user_align); |
| WB (lang->has_copy_assign); |
| WB (lang->has_new); |
| WB (lang->has_array_new); |
| |
| WB ((lang->gets_delete >> 0) & 1); |
| WB ((lang->gets_delete >> 1) & 1); |
| // Interfaceness is recalculated upon reading. May have to revisit? |
| // How do dllexport and dllimport interact across a module? |
| // lang->interface_only |
| // lang->interface_unknown |
| WB (lang->contains_empty_class_p); |
| WB (lang->anon_aggr); |
| WB (lang->non_zero_init); |
| WB (lang->empty_p); |
| |
| WB (lang->vec_new_uses_cookie); |
| WB (lang->declared_class); |
| WB (lang->diamond_shaped); |
| WB (lang->repeated_base); |
| gcc_assert (!lang->being_defined); |
| // lang->debug_requested |
| WB (lang->fields_readonly); |
| WB (lang->ptrmemfunc_flag); |
| |
| WB (lang->lazy_default_ctor); |
| WB (lang->lazy_copy_ctor); |
| WB (lang->lazy_copy_assign); |
| WB (lang->lazy_destructor); |
| WB (lang->has_const_copy_ctor); |
| WB (lang->has_complex_copy_ctor); |
| WB (lang->has_complex_copy_assign); |
| WB (lang->non_aggregate); |
| |
| WB (lang->has_complex_dflt); |
| WB (lang->has_list_ctor); |
| WB (lang->non_std_layout); |
| WB (lang->is_literal); |
| WB (lang->lazy_move_ctor); |
| WB (lang->lazy_move_assign); |
| WB (lang->has_complex_move_ctor); |
| WB (lang->has_complex_move_assign); |
| |
| WB (lang->has_constexpr_ctor); |
| WB (lang->unique_obj_representations); |
| WB (lang->unique_obj_representations_set); |
| #undef WB |
| } |
| |
| bool |
| trees_in::lang_type_bools (tree t) |
| { |
| #define RB(X) ((X) = b ()) |
| struct lang_type *lang = TYPE_LANG_SPECIFIC (t); |
| |
| RB (lang->has_type_conversion); |
| RB (lang->has_copy_ctor); |
| RB (lang->has_default_ctor); |
| RB (lang->const_needs_init); |
| RB (lang->ref_needs_init); |
| RB (lang->has_const_copy_assign); |
| unsigned v; |
| v = b () << 0; |
| v |= b () << 1; |
| lang->use_template = v; |
| |
| RB (lang->has_mutable); |
| RB (lang->com_interface); |
| RB (lang->non_pod_class); |
| RB (lang->nearly_empty_p); |
| RB (lang->user_align); |
| RB (lang->has_copy_assign); |
| RB (lang->has_new); |
| RB (lang->has_array_new); |
| |
| v = b () << 0; |
| v |= b () << 1; |
| lang->gets_delete = v; |
| // lang->interface_only |
| // lang->interface_unknown |
| lang->interface_unknown = true; // Redetermine interface |
| RB (lang->contains_empty_class_p); |
| RB (lang->anon_aggr); |
| RB (lang->non_zero_init); |
| RB (lang->empty_p); |
| |
| RB (lang->vec_new_uses_cookie); |
| RB (lang->declared_class); |
| RB (lang->diamond_shaped); |
| RB (lang->repeated_base); |
| gcc_assert (!lang->being_defined); |
| gcc_assert (!lang->debug_requested); |
| RB (lang->fields_readonly); |
| RB (lang->ptrmemfunc_flag); |
| |
| RB (lang->lazy_default_ctor); |
| RB (lang->lazy_copy_ctor); |
| RB (lang->lazy_copy_assign); |
| RB (lang->lazy_destructor); |
| RB (lang->has_const_copy_ctor); |
| RB (lang->has_complex_copy_ctor); |
| RB (lang->has_complex_copy_assign); |
| RB (lang->non_aggregate); |
| |
| RB (lang->has_complex_dflt); |
| RB (lang->has_list_ctor); |
| RB (lang->non_std_layout); |
| RB (lang->is_literal); |
| RB (lang->lazy_move_ctor); |
| RB (lang->lazy_move_assign); |
| RB (lang->has_complex_move_ctor); |
| RB (lang->has_complex_move_assign); |
| |
| RB (lang->has_constexpr_ctor); |
| RB (lang->unique_obj_representations); |
| RB (lang->unique_obj_representations_set); |
| #undef RB |
| return !get_overrun (); |
| } |
| |
| /* Read & write the core values and pointers. */ |
| |
| void |
| trees_out::core_vals (tree t) |
| { |
| #define WU(X) (u (X)) |
| #define WT(X) (tree_node (X)) |
| tree_code code = TREE_CODE (t); |
| |
| /* First by shape of the tree. */ |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL)) |
| { |
| /* Write this early, for better log information. */ |
| WT (t->decl_minimal.name); |
| if (!DECL_TEMPLATE_PARM_P (t)) |
| WT (t->decl_minimal.context); |
| |
| if (state) |
| state->write_location (*this, t->decl_minimal.locus); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) |
| { |
| /* The only types we write also have TYPE_NON_COMMON. */ |
| gcc_checking_assert (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON)); |
| |
| /* We only stream the main variant. */ |
| gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t); |
| |
| /* Stream the name & context first, for better log information */ |
| WT (t->type_common.name); |
| WT (t->type_common.context); |
| |
| /* By construction we want to make sure we have the canonical |
| and main variants already in the type table, so emit them |
| now. */ |
| WT (t->type_common.main_variant); |
| |
| tree canonical = t->type_common.canonical; |
| if (canonical && DECL_TEMPLATE_PARM_P (TYPE_NAME (t))) |
| /* We do not want to wander into different templates. |
| Reconstructed on stream in. */ |
| canonical = t; |
| WT (canonical); |
| |
| /* type_common.next_variant is internally manipulated. */ |
| /* type_common.pointer_to, type_common.reference_to. */ |
| |
| if (streaming_p ()) |
| { |
| WU (t->type_common.precision); |
| WU (t->type_common.contains_placeholder_bits); |
| WU (t->type_common.mode); |
| WU (t->type_common.align); |
| } |
| |
| if (!RECORD_OR_UNION_CODE_P (code)) |
| { |
| WT (t->type_common.size); |
| WT (t->type_common.size_unit); |
| } |
| WT (t->type_common.attributes); |
| |
| WT (t->type_common.common.chain); /* TYPE_STUB_DECL. */ |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) |
| { |
| if (streaming_p ()) |
| { |
| WU (t->decl_common.mode); |
| WU (t->decl_common.off_align); |
| WU (t->decl_common.align); |
| } |
| |
| /* For templates these hold instantiation (partial and/or |
| specialization) information. */ |
| if (code != TEMPLATE_DECL) |
| { |
| WT (t->decl_common.size); |
| WT (t->decl_common.size_unit); |
| } |
| |
| WT (t->decl_common.attributes); |
| // FIXME: Does this introduce cross-decl links? For instance |
| // from instantiation to the template. If so, we'll need more |
| // deduplication logic. I think we'll need to walk the blocks |
| // of the owning function_decl's abstract origin in tandem, to |
| // generate the locating data needed? |
| WT (t->decl_common.abstract_origin); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) |
| { |
| WT (t->decl_with_vis.assembler_name); |
| if (streaming_p ()) |
| WU (t->decl_with_vis.visibility); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON)) |
| { |
| /* Records and unions hold FIELDS, VFIELD & BINFO on these |
| things. */ |
| if (!RECORD_OR_UNION_CODE_P (code) && code != ENUMERAL_TYPE) |
| { |
| // FIXME: These are from tpl_parm_value's 'type' writing. |
| // Perhaps it should just be doing them directly? |
| gcc_checking_assert (code == TEMPLATE_TYPE_PARM |
| || code == TEMPLATE_TEMPLATE_PARM |
| || code == BOUND_TEMPLATE_TEMPLATE_PARM); |
| gcc_checking_assert (!TYPE_CACHED_VALUES_P (t)); |
| WT (t->type_non_common.values); |
| WT (t->type_non_common.maxval); |
| WT (t->type_non_common.minval); |
| } |
| |
| WT (t->type_non_common.lang_1); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_EXP)) |
| { |
| if (state) |
| state->write_location (*this, t->exp.locus); |
| |
| /* Walk in forward order, as (for instance) REQUIRES_EXPR has a |
| bunch of unscoped parms on its first operand. It's safer to |
| create those in order. */ |
| bool vl = TREE_CODE_CLASS (code) == tcc_vl_exp; |
| for (unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t) |
| : TREE_OPERAND_LENGTH (t)), |
| ix = unsigned (vl); ix != limit; ix++) |
| WT (TREE_OPERAND (t, ix)); |
| } |
| else |
| /* The CODE_CONTAINS tables were inaccurate when I started. */ |
| gcc_checking_assert (TREE_CODE_CLASS (code) != tcc_expression |
| && TREE_CODE_CLASS (code) != tcc_binary |
| && TREE_CODE_CLASS (code) != tcc_unary |
| && TREE_CODE_CLASS (code) != tcc_reference |
| && TREE_CODE_CLASS (code) != tcc_comparison |
| && TREE_CODE_CLASS (code) != tcc_statement |
| && TREE_CODE_CLASS (code) != tcc_vl_exp); |
| |
| /* Then by CODE. Special cases and/or 1:1 tree shape |
| correspondance. */ |
| switch (code) |
| { |
| default: |
| break; |
| |
| case ARGUMENT_PACK_SELECT: /* Transient during instantiation. */ |
| case DEFERRED_PARSE: /* Expanded upon completion of |
| outermost class. */ |
| case IDENTIFIER_NODE: /* Streamed specially. */ |
| case BINDING_VECTOR: /* Only in namespace-scope symbol |
| table. */ |
| case SSA_NAME: |
| case TRANSLATION_UNIT_DECL: /* There is only one, it is a |
| global_tree. */ |
| case USERDEF_LITERAL: /* Expanded during parsing. */ |
| gcc_unreachable (); /* Should never meet. */ |
| |
| /* Constants. */ |
| case COMPLEX_CST: |
| WT (TREE_REALPART (t)); |
| WT (TREE_IMAGPART (t)); |
| break; |
| |
| case FIXED_CST: |
| gcc_unreachable (); /* Not supported in C++. */ |
| |
| case INTEGER_CST: |
| if (streaming_p ()) |
| { |
| unsigned num = TREE_INT_CST_EXT_NUNITS (t); |
| for (unsigned ix = 0; ix != num; ix++) |
| wu (TREE_INT_CST_ELT (t, ix)); |
| } |
| break; |
| |
| case POLY_INT_CST: |
| gcc_unreachable (); /* Not supported in C++. */ |
| |
| case REAL_CST: |
| if (streaming_p ()) |
| buf (TREE_REAL_CST_PTR (t), sizeof (real_value)); |
| break; |
| |
| case STRING_CST: |
| /* Streamed during start. */ |
| break; |
| |
| case VECTOR_CST: |
| for (unsigned ix = vector_cst_encoded_nelts (t); ix--;) |
| WT (VECTOR_CST_ENCODED_ELT (t, ix)); |
| break; |
| |
| /* Decls. */ |
| case VAR_DECL: |
| if (DECL_CONTEXT (t) |
| && TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL) |
| break; |
| /* FALLTHROUGH */ |
| |
| case RESULT_DECL: |
| case PARM_DECL: |
| if (DECL_HAS_VALUE_EXPR_P (t)) |
| WT (DECL_VALUE_EXPR (t)); |
| /* FALLTHROUGH */ |
| |
| case CONST_DECL: |
| case IMPORTED_DECL: |
| WT (t->decl_common.initial); |
| break; |
| |
| case FIELD_DECL: |
| WT (t->field_decl.offset); |
| WT (t->field_decl.bit_field_type); |
| WT (t->field_decl.qualifier); /* bitfield unit. */ |
| WT (t->field_decl.bit_offset); |
| WT (t->field_decl.fcontext); |
| WT (t->decl_common.initial); |
| break; |
| |
| case LABEL_DECL: |
| if (streaming_p ()) |
| { |
| WU (t->label_decl.label_decl_uid); |
| WU (t->label_decl.eh_landing_pad_nr); |
| } |
| break; |
| |
| case FUNCTION_DECL: |
| if (streaming_p ()) |
| { |
| /* Builtins can be streamed by value when a header declares |
| them. */ |
| WU (DECL_BUILT_IN_CLASS (t)); |
| if (DECL_BUILT_IN_CLASS (t) != NOT_BUILT_IN) |
| WU (DECL_UNCHECKED_FUNCTION_CODE (t)); |
| } |
| |
| WT (t->function_decl.personality); |
| WT (t->function_decl.function_specific_target); |
| WT (t->function_decl.function_specific_optimization); |
| WT (t->function_decl.vindex); |
| break; |
| |
| case USING_DECL: |
| /* USING_DECL_DECLS */ |
| WT (t->decl_common.initial); |
| /* FALLTHROUGH */ |
| |
| case TYPE_DECL: |
| /* USING_DECL: USING_DECL_SCOPE */ |
| /* TYPE_DECL: DECL_ORIGINAL_TYPE */ |
| WT (t->decl_non_common.result); |
| break; |
| |
| /* Miscellaneous common nodes. */ |
| case BLOCK: |
| if (state) |
| { |
| state->write_location (*this, t->block.locus); |
| state->write_location (*this, t->block.end_locus); |
| } |
| |
| /* DECL_LOCAL_DECL_P decls are first encountered here and |
| streamed by value. */ |
| chained_decls (t->block.vars); |
| /* nonlocalized_vars is a middle-end thing. */ |
| WT (t->block.subblocks); |
| WT (t->block.supercontext); |
| // FIXME: As for decl's abstract_origin, does this introduce crosslinks? |
| WT (t->block.abstract_origin); |
| /* fragment_origin, fragment_chain are middle-end things. */ |
| WT (t->block.chain); |
| /* nonlocalized_vars, block_num & die are middle endy/debug |
| things. */ |
| break; |
| |
| case CALL_EXPR: |
| if (streaming_p ()) |
| WU (t->base.u.ifn); |
| break; |
| |
| case CONSTRUCTOR: |
| { |
| unsigned len = vec_safe_length (t->constructor.elts); |
| if (streaming_p ()) |
| WU (len); |
| if (len) |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| const constructor_elt &elt = (*t->constructor.elts)[ix]; |
| |
| WT (elt.index); |
| WT (elt.value); |
| } |
| } |
| break; |
| |
| case OMP_CLAUSE: |
| { |
| /* The ompcode is serialized in start. */ |
| if (streaming_p ()) |
| WU (t->omp_clause.subcode.map_kind); |
| if (state) |
| state->write_location (*this, t->omp_clause.locus); |
| |
| unsigned len = omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; |
| for (unsigned ix = 0; ix != len; ix++) |
| WT (t->omp_clause.ops[ix]); |
| } |
| break; |
| |
| case STATEMENT_LIST: |
| for (tree stmt : tsi_range (t)) |
| if (stmt) |
| WT (stmt); |
| WT (NULL_TREE); |
| break; |
| |
| case OPTIMIZATION_NODE: |
| case TARGET_OPTION_NODE: |
| // FIXME: Our representation for these two nodes is a cache of |
| // the resulting set of options. Not a record of the options |
| // that got changed by a particular attribute or pragma. Should |
| // we record that, or should we record the diff from the command |
| // line options? The latter seems the right behaviour, but is |
| // (a) harder, and I guess could introduce strangeness if the |
| // importer has set some incompatible set of optimization flags? |
| gcc_unreachable (); |
| break; |
| |
| case TREE_BINFO: |
| { |
| WT (t->binfo.common.chain); |
| WT (t->binfo.offset); |
| WT (t->binfo.inheritance); |
| WT (t->binfo.vptr_field); |
| |
| WT (t->binfo.vtable); |
| WT (t->binfo.virtuals); |
| WT (t->binfo.vtt_subvtt); |
| WT (t->binfo.vtt_vptr); |
| |
| tree_vec (BINFO_BASE_ACCESSES (t)); |
| unsigned num = vec_safe_length (BINFO_BASE_ACCESSES (t)); |
| for (unsigned ix = 0; ix != num; ix++) |
| WT (BINFO_BASE_BINFO (t, ix)); |
| } |
| break; |
| |
| case TREE_LIST: |
| WT (t->list.purpose); |
| WT (t->list.value); |
| WT (t->list.common.chain); |
| break; |
| |
| case TREE_VEC: |
| for (unsigned ix = TREE_VEC_LENGTH (t); ix--;) |
| WT (TREE_VEC_ELT (t, ix)); |
| /* We stash NON_DEFAULT_TEMPLATE_ARGS_COUNT on TREE_CHAIN! */ |
| gcc_checking_assert (!t->type_common.common.chain |
| || (TREE_CODE (t->type_common.common.chain) |
| == INTEGER_CST)); |
| WT (t->type_common.common.chain); |
| break; |
| |
| /* C++-specific nodes ... */ |
| case BASELINK: |
| WT (((lang_tree_node *)t)->baselink.binfo); |
| WT (((lang_tree_node *)t)->baselink.functions); |
| WT (((lang_tree_node *)t)->baselink.access_binfo); |
| break; |
| |
| case CONSTRAINT_INFO: |
| WT (((lang_tree_node *)t)->constraint_info.template_reqs); |
| WT (((lang_tree_node *)t)->constraint_info.declarator_reqs); |
| WT (((lang_tree_node *)t)->constraint_info.associated_constr); |
| break; |
| |
| case DEFERRED_NOEXCEPT: |
| WT (((lang_tree_node *)t)->deferred_noexcept.pattern); |
| WT (((lang_tree_node *)t)->deferred_noexcept.args); |
| break; |
| |
| case LAMBDA_EXPR: |
| WT (((lang_tree_node *)t)->lambda_expression.capture_list); |
| WT (((lang_tree_node *)t)->lambda_expression.this_capture); |
| WT (((lang_tree_node *)t)->lambda_expression.extra_scope); |
| /* pending_proxies is a parse-time thing. */ |
| gcc_assert (!((lang_tree_node *)t)->lambda_expression.pending_proxies); |
| if (state) |
| state->write_location |
| (*this, ((lang_tree_node *)t)->lambda_expression.locus); |
| if (streaming_p ()) |
| { |
| WU (((lang_tree_node *)t)->lambda_expression.default_capture_mode); |
| WU (((lang_tree_node *)t)->lambda_expression.discriminator); |
| } |
| break; |
| |
| case OVERLOAD: |
| WT (((lang_tree_node *)t)->overload.function); |
| WT (t->common.chain); |
| break; |
| |
| case PTRMEM_CST: |
| WT (((lang_tree_node *)t)->ptrmem.member); |
| break; |
| |
| case STATIC_ASSERT: |
| WT (((lang_tree_node *)t)->static_assertion.condition); |
| WT (((lang_tree_node *)t)->static_assertion.message); |
| if (state) |
| state->write_location |
| (*this, ((lang_tree_node *)t)->static_assertion.location); |
| break; |
| |
| case TEMPLATE_DECL: |
| /* Streamed with the template_decl node itself. */ |
| gcc_checking_assert |
| (TREE_VISITED (((lang_tree_node *)t)->template_decl.arguments)); |
| gcc_checking_assert |
| (TREE_VISITED (((lang_tree_node *)t)->template_decl.result) |
| || dep_hash->find_dependency (t)->is_alias_tmpl_inst ()); |
| if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (t)) |
| WT (DECL_CHAIN (t)); |
| break; |
| |
| case TEMPLATE_INFO: |
| { |
| WT (((lang_tree_node *)t)->template_info.tmpl); |
| WT (((lang_tree_node *)t)->template_info.args); |
| |
| const auto *ac = (((lang_tree_node *)t) |
| ->template_info.deferred_access_checks); |
| unsigned len = vec_safe_length (ac); |
| if (streaming_p ()) |
| u (len); |
| if (len) |
| { |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| const auto &m = (*ac)[ix]; |
| WT (m.binfo); |
| WT (m.decl); |
| WT (m.diag_decl); |
| if (state) |
| state->write_location (*this, m.loc); |
| } |
| } |
| } |
| break; |
| |
| case TEMPLATE_PARM_INDEX: |
| if (streaming_p ()) |
| { |
| WU (((lang_tree_node *)t)->tpi.index); |
| WU (((lang_tree_node *)t)->tpi.level); |
| WU (((lang_tree_node *)t)->tpi.orig_level); |
| } |
| WT (((lang_tree_node *)t)->tpi.decl); |
| /* TEMPLATE_PARM_DESCENDANTS (AKA TREE_CHAIN) is an internal |
| cache, do not stream. */ |
| break; |
| |
| case TRAIT_EXPR: |
| WT (((lang_tree_node *)t)->trait_expression.type1); |
| WT (((lang_tree_node *)t)->trait_expression.type2); |
| if (streaming_p ()) |
| WU (((lang_tree_node *)t)->trait_expression.kind); |
| break; |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPED)) |
| { |
| /* We want to stream the type of a expression-like nodes /after/ |
| we've streamed the operands. The type often contains (bits |
| of the) types of the operands, and with things like decltype |
| and noexcept in play, we really want to stream the decls |
| defining the type before we try and stream the type on its |
| own. Otherwise we can find ourselves trying to read in a |
| decl, when we're already partially reading in a component of |
| its type. And that's bad. */ |
| tree type = t->typed.type; |
| unsigned prec = 0; |
| |
| switch (code) |
| { |
| default: |
| break; |
| |
| case TEMPLATE_DECL: |
| /* We fill in the template's type separately. */ |
| type = NULL_TREE; |
| break; |
| |
| case TYPE_DECL: |
| if (DECL_ORIGINAL_TYPE (t) && t == TYPE_NAME (type)) |
| /* This is a typedef. We set its type separately. */ |
| type = NULL_TREE; |
| break; |
| |
| case ENUMERAL_TYPE: |
| if (type && !ENUM_FIXED_UNDERLYING_TYPE_P (t)) |
| { |
| /* Type is a restricted range integer type derived from the |
| integer_types. Find the right one. */ |
| prec = TYPE_PRECISION (type); |
| tree name = DECL_NAME (TYPE_NAME (type)); |
| |
| for (unsigned itk = itk_none; itk--;) |
| if (integer_types[itk] |
| && DECL_NAME (TYPE_NAME (integer_types[itk])) == name) |
| { |
| type = integer_types[itk]; |
| break; |
| } |
| gcc_assert (type != t->typed.type); |
| } |
| break; |
| } |
| |
| WT (type); |
| if (prec && streaming_p ()) |
| WU (prec); |
| } |
| |
| #undef WT |
| #undef WU |
| } |
| |
| // Streaming in a reference to a decl can cause that decl to be |
| // TREE_USED, which is the mark_used behaviour we need most of the |
| // time. The trees_in::unused can be incremented to inhibit this, |
| // which is at least needed for vtables. |
| |
| bool |
| trees_in::core_vals (tree t) |
| { |
| #define RU(X) ((X) = u ()) |
| #define RUC(T,X) ((X) = T (u ())) |
| #define RT(X) ((X) = tree_node ()) |
| #define RTU(X) ((X) = tree_node (true)) |
| tree_code code = TREE_CODE (t); |
| |
| /* First by tree shape. */ |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL)) |
| { |
| RT (t->decl_minimal.name); |
| if (!DECL_TEMPLATE_PARM_P (t)) |
| RT (t->decl_minimal.context); |
| |
| /* Don't zap the locus just yet, we don't record it correctly |
| and thus lose all location information. */ |
| t->decl_minimal.locus = state->read_location (*this); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) |
| { |
| RT (t->type_common.name); |
| RT (t->type_common.context); |
| |
| RT (t->type_common.main_variant); |
| RT (t->type_common.canonical); |
| |
| /* type_common.next_variant is internally manipulated. */ |
| /* type_common.pointer_to, type_common.reference_to. */ |
| |
| RU (t->type_common.precision); |
| RU (t->type_common.contains_placeholder_bits); |
| RUC (machine_mode, t->type_common.mode); |
| RU (t->type_common.align); |
| |
| if (!RECORD_OR_UNION_CODE_P (code)) |
| { |
| RT (t->type_common.size); |
| RT (t->type_common.size_unit); |
| } |
| RT (t->type_common.attributes); |
| |
| RT (t->type_common.common.chain); /* TYPE_STUB_DECL. */ |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) |
| { |
| RUC (machine_mode, t->decl_common.mode); |
| RU (t->decl_common.off_align); |
| RU (t->decl_common.align); |
| |
| if (code != TEMPLATE_DECL) |
| { |
| RT (t->decl_common.size); |
| RT (t->decl_common.size_unit); |
| } |
| |
| RT (t->decl_common.attributes); |
| RT (t->decl_common.abstract_origin); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) |
| { |
| RT (t->decl_with_vis.assembler_name); |
| RUC (symbol_visibility, t->decl_with_vis.visibility); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPE_NON_COMMON)) |
| { |
| /* Records and unions hold FIELDS, VFIELD & BINFO on these |
| things. */ |
| if (!RECORD_OR_UNION_CODE_P (code) && code != ENUMERAL_TYPE) |
| { |
| /* This is not clobbering TYPE_CACHED_VALUES, because this |
| is a type that doesn't have any. */ |
| gcc_checking_assert (!TYPE_CACHED_VALUES_P (t)); |
| RT (t->type_non_common.values); |
| RT (t->type_non_common.maxval); |
| RT (t->type_non_common.minval); |
| } |
| |
| RT (t->type_non_common.lang_1); |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_EXP)) |
| { |
| t->exp.locus = state->read_location (*this); |
| |
| bool vl = TREE_CODE_CLASS (code) == tcc_vl_exp; |
| for (unsigned limit = (vl ? VL_EXP_OPERAND_LENGTH (t) |
| : TREE_OPERAND_LENGTH (t)), |
| ix = unsigned (vl); ix != limit; ix++) |
| RTU (TREE_OPERAND (t, ix)); |
| } |
| |
| /* Then by CODE. Special cases and/or 1:1 tree shape |
| correspondance. */ |
| switch (code) |
| { |
| default: |
| break; |
| |
| case ARGUMENT_PACK_SELECT: |
| case DEFERRED_PARSE: |
| case IDENTIFIER_NODE: |
| case BINDING_VECTOR: |
| case SSA_NAME: |
| case TRANSLATION_UNIT_DECL: |
| case USERDEF_LITERAL: |
| return false; /* Should never meet. */ |
| |
| /* Constants. */ |
| case COMPLEX_CST: |
| RT (TREE_REALPART (t)); |
| RT (TREE_IMAGPART (t)); |
| break; |
| |
| case FIXED_CST: |
| /* Not suported in C++. */ |
| return false; |
| |
| case INTEGER_CST: |
| { |
| unsigned num = TREE_INT_CST_EXT_NUNITS (t); |
| for (unsigned ix = 0; ix != num; ix++) |
| TREE_INT_CST_ELT (t, ix) = wu (); |
| } |
| break; |
| |
| case POLY_INT_CST: |
| /* Not suported in C++. */ |
| return false; |
| |
| case REAL_CST: |
| if (const void *bytes = buf (sizeof (real_value))) |
| TREE_REAL_CST_PTR (t) |
| = reinterpret_cast<real_value *> (memcpy (ggc_alloc<real_value> (), |
| bytes, sizeof (real_value))); |
| break; |
| |
| case STRING_CST: |
| /* Streamed during start. */ |
| break; |
| |
| case VECTOR_CST: |
| for (unsigned ix = vector_cst_encoded_nelts (t); ix--;) |
| RT (VECTOR_CST_ENCODED_ELT (t, ix)); |
| break; |
| |
| /* Decls. */ |
| case VAR_DECL: |
| if (DECL_CONTEXT (t) |
| && TREE_CODE (DECL_CONTEXT (t)) != FUNCTION_DECL) |
| break; |
| /* FALLTHROUGH */ |
| |
| case RESULT_DECL: |
| case PARM_DECL: |
| if (DECL_HAS_VALUE_EXPR_P (t)) |
| { |
| /* The DECL_VALUE hash table is a cache, thus if we're |
| reading a duplicate (which we end up discarding), the |
| value expr will also be cleaned up at the next gc. */ |
| tree val = tree_node (); |
| SET_DECL_VALUE_EXPR (t, val); |
| } |
| /* FALLTHROUGH */ |
| |
| case CONST_DECL: |
| case IMPORTED_DECL: |
| RT (t->decl_common.initial); |
| break; |
| |
| case FIELD_DECL: |
| RT (t->field_decl.offset); |
| RT (t->field_decl.bit_field_type); |
| RT (t->field_decl.qualifier); |
| RT (t->field_decl.bit_offset); |
| RT (t->field_decl.fcontext); |
| RT (t->decl_common.initial); |
| break; |
| |
| case LABEL_DECL: |
| RU (t->label_decl.label_decl_uid); |
| RU (t->label_decl.eh_landing_pad_nr); |
| break; |
| |
| case FUNCTION_DECL: |
| { |
| unsigned bltin = u (); |
| t->function_decl.built_in_class = built_in_class (bltin); |
| if (bltin != NOT_BUILT_IN) |
| { |
| bltin = u (); |
| DECL_UNCHECKED_FUNCTION_CODE (t) = built_in_function (bltin); |
| } |
| |
| RT (t->function_decl.personality); |
| RT (t->function_decl.function_specific_target); |
| RT (t->function_decl.function_specific_optimization); |
| RT (t->function_decl.vindex); |
| } |
| break; |
| |
| case USING_DECL: |
| /* USING_DECL_DECLS */ |
| RT (t->decl_common.initial); |
| /* FALLTHROUGH */ |
| |
| case TYPE_DECL: |
| /* USING_DECL: USING_DECL_SCOPE */ |
| /* TYPE_DECL: DECL_ORIGINAL_TYPE */ |
| RT (t->decl_non_common.result); |
| break; |
| |
| /* Miscellaneous common nodes. */ |
| case BLOCK: |
| t->block.locus = state->read_location (*this); |
| t->block.end_locus = state->read_location (*this); |
| t->block.vars = chained_decls (); |
| /* nonlocalized_vars is middle-end. */ |
| RT (t->block.subblocks); |
| RT (t->block.supercontext); |
| RT (t->block.abstract_origin); |
| /* fragment_origin, fragment_chain are middle-end. */ |
| RT (t->block.chain); |
| /* nonlocalized_vars, block_num, die are middle endy/debug |
| things. */ |
| break; |
| |
| case CALL_EXPR: |
| RUC (internal_fn, t->base.u.ifn); |
| break; |
| |
| case CONSTRUCTOR: |
| if (unsigned len = u ()) |
| { |
| vec_alloc (t->constructor.elts, len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| constructor_elt elt; |
| |
| RT (elt.index); |
| RTU (elt.value); |
| t->constructor.elts->quick_push (elt); |
| } |
| } |
| break; |
| |
| case OMP_CLAUSE: |
| { |
| RU (t->omp_clause.subcode.map_kind); |
| t->omp_clause.locus = state->read_location (*this); |
| |
| unsigned len = omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; |
| for (unsigned ix = 0; ix != len; ix++) |
| RT (t->omp_clause.ops[ix]); |
| } |
| break; |
| |
| case STATEMENT_LIST: |
| { |
| tree_stmt_iterator iter = tsi_start (t); |
| for (tree stmt; RT (stmt);) |
| tsi_link_after (&iter, stmt, TSI_CONTINUE_LINKING); |
| } |
| break; |
| |
| case OPTIMIZATION_NODE: |
| case TARGET_OPTION_NODE: |
| /* Not yet implemented, see trees_out::core_vals. */ |
| gcc_unreachable (); |
| break; |
| |
| case TREE_BINFO: |
| RT (t->binfo.common.chain); |
| RT (t->binfo.offset); |
| RT (t->binfo.inheritance); |
| RT (t->binfo.vptr_field); |
| |
| /* Do not mark the vtables as USED in the address expressions |
| here. */ |
| unused++; |
| RT (t->binfo.vtable); |
| RT (t->binfo.virtuals); |
| RT (t->binfo.vtt_subvtt); |
| RT (t->binfo.vtt_vptr); |
| unused--; |
| |
| BINFO_BASE_ACCESSES (t) = tree_vec (); |
| if (!get_overrun ()) |
| { |
| unsigned num = vec_safe_length (BINFO_BASE_ACCESSES (t)); |
| for (unsigned ix = 0; ix != num; ix++) |
| BINFO_BASE_APPEND (t, tree_node ()); |
| } |
| break; |
| |
| case TREE_LIST: |
| RT (t->list.purpose); |
| RT (t->list.value); |
| RT (t->list.common.chain); |
| break; |
| |
| case TREE_VEC: |
| for (unsigned ix = TREE_VEC_LENGTH (t); ix--;) |
| RT (TREE_VEC_ELT (t, ix)); |
| RT (t->type_common.common.chain); |
| break; |
| |
| /* C++-specific nodes ... */ |
| case BASELINK: |
| RT (((lang_tree_node *)t)->baselink.binfo); |
| RTU (((lang_tree_node *)t)->baselink.functions); |
| RT (((lang_tree_node *)t)->baselink.access_binfo); |
| break; |
| |
| case CONSTRAINT_INFO: |
| RT (((lang_tree_node *)t)->constraint_info.template_reqs); |
| RT (((lang_tree_node *)t)->constraint_info.declarator_reqs); |
| RT (((lang_tree_node *)t)->constraint_info.associated_constr); |
| break; |
| |
| case DEFERRED_NOEXCEPT: |
| RT (((lang_tree_node *)t)->deferred_noexcept.pattern); |
| RT (((lang_tree_node *)t)->deferred_noexcept.args); |
| break; |
| |
| case LAMBDA_EXPR: |
| RT (((lang_tree_node *)t)->lambda_expression.capture_list); |
| RT (((lang_tree_node *)t)->lambda_expression.this_capture); |
| RT (((lang_tree_node *)t)->lambda_expression.extra_scope); |
| /* lambda_expression.pending_proxies is NULL */ |
| ((lang_tree_node *)t)->lambda_expression.locus |
| = state->read_location (*this); |
| RUC (cp_lambda_default_capture_mode_type, |
| ((lang_tree_node *)t)->lambda_expression.default_capture_mode); |
| RU (((lang_tree_node *)t)->lambda_expression.discriminator); |
| break; |
| |
| case OVERLOAD: |
| RT (((lang_tree_node *)t)->overload.function); |
| RT (t->common.chain); |
| break; |
| |
| case PTRMEM_CST: |
| RT (((lang_tree_node *)t)->ptrmem.member); |
| break; |
| |
| case STATIC_ASSERT: |
| RT (((lang_tree_node *)t)->static_assertion.condition); |
| RT (((lang_tree_node *)t)->static_assertion.message); |
| ((lang_tree_node *)t)->static_assertion.location |
| = state->read_location (*this); |
| break; |
| |
| case TEMPLATE_DECL: |
| /* Streamed when reading the raw template decl itself. */ |
| gcc_assert (((lang_tree_node *)t)->template_decl.arguments); |
| gcc_assert (((lang_tree_node *)t)->template_decl.result); |
| if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (t)) |
| RT (DECL_CHAIN (t)); |
| break; |
| |
| case TEMPLATE_INFO: |
| RT (((lang_tree_node *)t)->template_info.tmpl); |
| RT (((lang_tree_node *)t)->template_info.args); |
| if (unsigned len = u ()) |
| { |
| auto &ac = (((lang_tree_node *)t) |
| ->template_info.deferred_access_checks); |
| vec_alloc (ac, len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| deferred_access_check m; |
| |
| RT (m.binfo); |
| RT (m.decl); |
| RT (m.diag_decl); |
| m.loc = state->read_location (*this); |
| ac->quick_push (m); |
| } |
| } |
| break; |
| |
| case TEMPLATE_PARM_INDEX: |
| RU (((lang_tree_node *)t)->tpi.index); |
| RU (((lang_tree_node *)t)->tpi.level); |
| RU (((lang_tree_node *)t)->tpi.orig_level); |
| RT (((lang_tree_node *)t)->tpi.decl); |
| break; |
| |
| case TRAIT_EXPR: |
| RT (((lang_tree_node *)t)->trait_expression.type1); |
| RT (((lang_tree_node *)t)->trait_expression.type2); |
| RUC (cp_trait_kind, ((lang_tree_node *)t)->trait_expression.kind); |
| break; |
| } |
| |
| if (CODE_CONTAINS_STRUCT (code, TS_TYPED)) |
| { |
| tree type = tree_node (); |
| |
| if (type && code == ENUMERAL_TYPE && !ENUM_FIXED_UNDERLYING_TYPE_P (t)) |
| { |
| unsigned precision = u (); |
| |
| type = build_distinct_type_copy (type); |
| TYPE_PRECISION (type) = precision; |
| set_min_and_max_values_for_integral_type (type, precision, |
| TYPE_SIGN (type)); |
| } |
| |
| if (code != TEMPLATE_DECL) |
| t->typed.type = type; |
| } |
| |
| #undef RT |
| #undef RM |
| #undef RU |
| return !get_overrun (); |
| } |
| |
| void |
| trees_out::lang_decl_vals (tree t) |
| { |
| const struct lang_decl *lang = DECL_LANG_SPECIFIC (t); |
| #define WU(X) (u (X)) |
| #define WT(X) (tree_node (X)) |
| /* Module index already written. */ |
| switch (lang->u.base.selector) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case lds_fn: /* lang_decl_fn. */ |
| if (streaming_p ()) |
| { |
| if (DECL_NAME (t) && IDENTIFIER_OVL_OP_P (DECL_NAME (t))) |
| WU (lang->u.fn.ovl_op_code); |
| } |
| |
| if (DECL_CLASS_SCOPE_P (t)) |
| WT (lang->u.fn.context); |
| |
| if (lang->u.fn.thunk_p) |
| { |
| /* The thunked-to function. */ |
| WT (lang->u.fn.befriending_classes); |
| if (streaming_p ()) |
| wi (lang->u.fn.u5.fixed_offset); |
| } |
| else |
| WT (lang->u.fn.u5.cloned_function); |
| |
| if (FNDECL_USED_AUTO (t)) |
| WT (lang->u.fn.u.saved_auto_return_type); |
| |
| goto lds_min; |
| |
| case lds_decomp: /* lang_decl_decomp. */ |
| WT (lang->u.decomp.base); |
| goto lds_min; |
| |
| case lds_min: /* lang_decl_min. */ |
| lds_min: |
| WT (lang->u.min.template_info); |
| { |
| tree access = lang->u.min.access; |
| |
| /* DECL_ACCESS needs to be maintained by the definition of the |
| (derived) class that changes the access. The other users |
| of DECL_ACCESS need to write it here. */ |
| if (!DECL_THUNK_P (t) |
| && (DECL_CONTEXT (t) && TYPE_P (DECL_CONTEXT (t)))) |
| access = NULL_TREE; |
| |
| WT (access); |
| } |
| break; |
| |
| case lds_ns: /* lang_decl_ns. */ |
| break; |
| |
| case lds_parm: /* lang_decl_parm. */ |
| if (streaming_p ()) |
| { |
| WU (lang->u.parm.level); |
| WU (lang->u.parm.index); |
| } |
| break; |
| } |
| #undef WU |
| #undef WT |
| } |
| |
| bool |
| trees_in::lang_decl_vals (tree t) |
| { |
| struct lang_decl *lang = DECL_LANG_SPECIFIC (t); |
| #define RU(X) ((X) = u ()) |
| #define RT(X) ((X) = tree_node ()) |
| |
| /* Module index already read. */ |
| switch (lang->u.base.selector) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case lds_fn: /* lang_decl_fn. */ |
| if (DECL_NAME (t) && IDENTIFIER_OVL_OP_P (DECL_NAME (t))) |
| { |
| unsigned code = u (); |
| |
| /* Check consistency. */ |
| if (code >= OVL_OP_MAX |
| || (ovl_op_info[IDENTIFIER_ASSIGN_OP_P (DECL_NAME (t))][code] |
| .ovl_op_code) == OVL_OP_ERROR_MARK) |
| set_overrun (); |
| else |
| lang->u.fn.ovl_op_code = code; |
| } |
| |
| if (DECL_CLASS_SCOPE_P (t)) |
| RT (lang->u.fn.context); |
| |
| if (lang->u.fn.thunk_p) |
| { |
| RT (lang->u.fn.befriending_classes); |
| lang->u.fn.u5.fixed_offset = wi (); |
| } |
| else |
| RT (lang->u.fn.u5.cloned_function); |
| |
| if (FNDECL_USED_AUTO (t)) |
| RT (lang->u.fn.u.saved_auto_return_type); |
| goto lds_min; |
| |
| case lds_decomp: /* lang_decl_decomp. */ |
| RT (lang->u.decomp.base); |
| goto lds_min; |
| |
| case lds_min: /* lang_decl_min. */ |
| lds_min: |
| RT (lang->u.min.template_info); |
| RT (lang->u.min.access); |
| break; |
| |
| case lds_ns: /* lang_decl_ns. */ |
| break; |
| |
| case lds_parm: /* lang_decl_parm. */ |
| RU (lang->u.parm.level); |
| RU (lang->u.parm.index); |
| break; |
| } |
| #undef RU |
| #undef RT |
| return !get_overrun (); |
| } |
| |
| /* Most of the value contents of lang_type is streamed in |
| define_class. */ |
| |
| void |
| trees_out::lang_type_vals (tree t) |
| { |
| const struct lang_type *lang = TYPE_LANG_SPECIFIC (t); |
| #define WU(X) (u (X)) |
| #define WT(X) (tree_node (X)) |
| if (streaming_p ()) |
| WU (lang->align); |
| #undef WU |
| #undef WT |
| } |
| |
| bool |
| trees_in::lang_type_vals (tree t) |
| { |
| struct lang_type *lang = TYPE_LANG_SPECIFIC (t); |
| #define RU(X) ((X) = u ()) |
| #define RT(X) ((X) = tree_node ()) |
| RU (lang->align); |
| #undef RU |
| #undef RT |
| return !get_overrun (); |
| } |
| |
| /* Write out the bools of T, including information about any |
| LANG_SPECIFIC information. Including allocation of any lang |
| specific object. */ |
| |
| void |
| trees_out::tree_node_bools (tree t) |
| { |
| gcc_checking_assert (streaming_p ()); |
| |
| /* We should never stream a namespace. */ |
| gcc_checking_assert (TREE_CODE (t) != NAMESPACE_DECL |
| || DECL_NAMESPACE_ALIAS (t)); |
| |
| core_bools (t); |
| |
| switch (TREE_CODE_CLASS (TREE_CODE (t))) |
| { |
| case tcc_declaration: |
| { |
| bool specific = DECL_LANG_SPECIFIC (t) != NULL; |
| b (specific); |
| if (specific && VAR_P (t)) |
| b (DECL_DECOMPOSITION_P (t)); |
| if (specific) |
| lang_decl_bools (t); |
| } |
| break; |
| |
| case tcc_type: |
| { |
| bool specific = (TYPE_MAIN_VARIANT (t) == t |
| && TYPE_LANG_SPECIFIC (t) != NULL); |
| gcc_assert (TYPE_LANG_SPECIFIC (t) |
| == TYPE_LANG_SPECIFIC (TYPE_MAIN_VARIANT (t))); |
| |
| b (specific); |
| if (specific) |
| lang_type_bools (t); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| bflush (); |
| } |
| |
| bool |
| trees_in::tree_node_bools (tree t) |
| { |
| bool ok = core_bools (t); |
| |
| if (ok) |
| switch (TREE_CODE_CLASS (TREE_CODE (t))) |
| { |
| case tcc_declaration: |
| if (b ()) |
| { |
| bool decomp = VAR_P (t) && b (); |
| |
| ok = maybe_add_lang_decl_raw (t, decomp); |
| if (ok) |
| ok = lang_decl_bools (t); |
| } |
| break; |
| |
| case tcc_type: |
| if (b ()) |
| { |
| ok = maybe_add_lang_type_raw (t); |
| if (ok) |
| ok = lang_type_bools (t); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| bflush (); |
| if (!ok || get_overrun ()) |
| return false; |
| |
| return true; |
| } |
| |
| |
| /* Write out the lang-specifc vals of node T. */ |
| |
| void |
| trees_out::lang_vals (tree t) |
| { |
| switch (TREE_CODE_CLASS (TREE_CODE (t))) |
| { |
| case tcc_declaration: |
| if (DECL_LANG_SPECIFIC (t)) |
| lang_decl_vals (t); |
| break; |
| |
| case tcc_type: |
| if (TYPE_MAIN_VARIANT (t) == t && TYPE_LANG_SPECIFIC (t)) |
| lang_type_vals (t); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| bool |
| trees_in::lang_vals (tree t) |
| { |
| bool ok = true; |
| |
| switch (TREE_CODE_CLASS (TREE_CODE (t))) |
| { |
| case tcc_declaration: |
| if (DECL_LANG_SPECIFIC (t)) |
| ok = lang_decl_vals (t); |
| break; |
| |
| case tcc_type: |
| if (TYPE_LANG_SPECIFIC (t)) |
| ok = lang_type_vals (t); |
| else |
| TYPE_LANG_SPECIFIC (t) = TYPE_LANG_SPECIFIC (TYPE_MAIN_VARIANT (t)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return ok; |
| } |
| |
| /* Write out the value fields of node T. */ |
| |
| void |
| trees_out::tree_node_vals (tree t) |
| { |
| core_vals (t); |
| lang_vals (t); |
| } |
| |
| bool |
| trees_in::tree_node_vals (tree t) |
| { |
| bool ok = core_vals (t); |
| if (ok) |
| ok = lang_vals (t); |
| |
| return ok; |
| } |
| |
| |
| /* If T is a back reference, fixed reference or NULL, write out it's |
| code and return WK_none. Otherwise return WK_value if we must write |
| by value, or WK_normal otherwise. */ |
| |
| walk_kind |
| trees_out::ref_node (tree t) |
| { |
| if (!t) |
| { |
| if (streaming_p ()) |
| { |
| /* NULL_TREE -> tt_null. */ |
| null_count++; |
| i (tt_null); |
| } |
| return WK_none; |
| } |
| |
| if (!TREE_VISITED (t)) |
| return WK_normal; |
| |
| /* An already-visited tree. It must be in the map. */ |
| int val = get_tag (t); |
| |
| if (val == tag_value) |
| /* An entry we should walk into. */ |
| return WK_value; |
| |
| const char *kind; |
| |
| if (val <= tag_backref) |
| { |
| /* Back reference -> -ve number */ |
| if (streaming_p ()) |
| i (val); |
| kind = "backref"; |
| } |
| else if (val >= tag_fixed) |
| { |
| /* Fixed reference -> tt_fixed */ |
| val -= tag_fixed; |
| if (streaming_p ()) |
| i (tt_fixed), u (val); |
| kind = "fixed"; |
| } |
| |
| if (streaming_p ()) |
| { |
| back_ref_count++; |
| dump (dumper::TREE) |
| && dump ("Wrote %s:%d %C:%N%S", kind, val, TREE_CODE (t), t, t); |
| } |
| return WK_none; |
| } |
| |
| tree |
| trees_in::back_ref (int tag) |
| { |
| tree res = NULL_TREE; |
| |
| if (tag < 0 && unsigned (~tag) < back_refs.length ()) |
| res = back_refs[~tag]; |
| |
| if (!res |
| /* Checking TREE_CODE is a dereference, so we know this is not a |
| wild pointer. Checking the code provides evidence we've not |
| corrupted something. */ |
| || TREE_CODE (res) >= MAX_TREE_CODES) |
| set_overrun (); |
| else |
| dump (dumper::TREE) && dump ("Read backref:%d found %C:%N%S", tag, |
| TREE_CODE (res), res, res); |
| return res; |
| } |
| |
| unsigned |
| trees_out::add_indirect_tpl_parms (tree parms) |
| { |
| unsigned len = 0; |
| for (; parms; parms = TREE_CHAIN (parms), len++) |
| { |
| if (TREE_VISITED (parms)) |
| break; |
| |
| int tag = insert (parms); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Indirect:%d template's parameter %u %C:%N", |
| tag, len, TREE_CODE (parms), parms); |
| } |
| |
| if (streaming_p ()) |
| u (len); |
| |
| return len; |
| } |
| |
| unsigned |
| trees_in::add_indirect_tpl_parms (tree parms) |
| { |
| unsigned len = u (); |
| for (unsigned ix = 0; ix != len; parms = TREE_CHAIN (parms), ix++) |
| { |
| int tag = insert (parms); |
| dump (dumper::TREE) |
| && dump ("Indirect:%d template's parameter %u %C:%N", |
| tag, ix, TREE_CODE (parms), parms); |
| } |
| |
| return len; |
| } |
| |
| /* We've just found DECL by name. Insert nodes that come with it, but |
| cannot be found by name, so we'll not accidentally walk into them. */ |
| |
| void |
| trees_out::add_indirects (tree decl) |
| { |
| unsigned count = 0; |
| |
| // FIXME:OPTIMIZATION We'll eventually want default fn parms of |
| // templates and perhaps default template parms too. The former can |
| // be referenced from instantiations (as they are lazily |
| // instantiated). Also (deferred?) exception specifications of |
| // templates. See the note about PARM_DECLs in trees_out::decl_node. |
| tree inner = decl; |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| count += add_indirect_tpl_parms (DECL_TEMPLATE_PARMS (decl)); |
| |
| inner = DECL_TEMPLATE_RESULT (decl); |
| int tag = insert (inner); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Indirect:%d template's result %C:%N", |
| tag, TREE_CODE (inner), inner); |
| count++; |
| } |
| |
| if (TREE_CODE (inner) == TYPE_DECL) |
| { |
| /* Make sure the type is in the map too. Otherwise we get |
| different RECORD_TYPEs for the same type, and things go |
| south. */ |
| tree type = TREE_TYPE (inner); |
| gcc_checking_assert (DECL_ORIGINAL_TYPE (inner) |
| || TYPE_NAME (type) == inner); |
| int tag = insert (type); |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Indirect:%d decl's type %C:%N", tag, |
| TREE_CODE (type), type); |
| count++; |
| } |
| |
| if (streaming_p ()) |
| { |
| u (count); |
| dump (dumper::TREE) && dump ("Inserted %u indirects", count); |
| } |
| } |
| |
| bool |
| trees_in::add_indirects (tree decl) |
| { |
| unsigned count = 0; |
| |
| tree inner = decl; |
| if (TREE_CODE (inner) == TEMPLATE_DECL) |
| { |
| count += add_indirect_tpl_parms (DECL_TEMPLATE_PARMS (decl)); |
| |
| inner = DECL_TEMPLATE_RESULT (decl); |
| int tag = insert (inner); |
| dump (dumper::TREE) |
| && dump ("Indirect:%d templates's result %C:%N", tag, |
| TREE_CODE (inner), inner); |
| count++; |
| } |
| |
| if (TREE_CODE (inner) == TYPE_DECL) |
| { |
| tree type = TREE_TYPE (inner); |
| gcc_checking_assert (DECL_ORIGINAL_TYPE (inner) |
| || TYPE_NAME (type) == inner); |
| int tag = insert (type); |
| dump (dumper::TREE) |
| && dump ("Indirect:%d decl's type %C:%N", tag, TREE_CODE (type), type); |
| count++; |
| } |
| |
| dump (dumper::TREE) && dump ("Inserted %u indirects", count); |
| return count == u (); |
| } |
| |
| /* Stream a template parameter. There are 4.5 kinds of parameter: |
| a) Template - TEMPLATE_DECL->TYPE_DECL->TEMPLATE_TEMPLATE_PARM |
| TEMPLATE_TYPE_PARM_INDEX TPI |
| b) Type - TYPE_DECL->TEMPLATE_TYPE_PARM TEMPLATE_TYPE_PARM_INDEX TPI |
| c.1) NonTYPE - PARM_DECL DECL_INITIAL TPI We meet this first |
| c.2) NonTYPE - CONST_DECL DECL_INITIAL Same TPI |
| d) BoundTemplate - TYPE_DECL->BOUND_TEMPLATE_TEMPLATE_PARM |
| TEMPLATE_TYPE_PARM_INDEX->TPI |
| TEMPLATE_TEMPLATE_PARM_INFO->TEMPLATE_INFO |
| |
| All of these point to a TEMPLATE_PARM_INDEX, and #B also has a TEMPLATE_INFO |
| */ |
| |
| void |
| trees_out::tpl_parm_value (tree parm) |
| { |
| gcc_checking_assert (DECL_P (parm) && DECL_TEMPLATE_PARM_P (parm)); |
| |
| int parm_tag = insert (parm); |
| if (streaming_p ()) |
| { |
| i (tt_tpl_parm); |
| dump (dumper::TREE) && dump ("Writing template parm:%d %C:%N", |
| parm_tag, TREE_CODE (parm), parm); |
| start (parm); |
| tree_node_bools (parm); |
| } |
| |
| tree inner = parm; |
| if (TREE_CODE (inner) == TEMPLATE_DECL) |
| { |
| inner = DECL_TEMPLATE_RESULT (inner); |
| int inner_tag = insert (inner); |
| if (streaming_p ()) |
| { |
| dump (dumper::TREE) && dump ("Writing inner template parm:%d %C:%N", |
| inner_tag, TREE_CODE (inner), inner); |
| start (inner); |
| tree_node_bools (inner); |
| } |
| } |
| |
| tree type = NULL_TREE; |
| if (TREE_CODE (inner) == TYPE_DECL) |
| { |
| type = TREE_TYPE (inner); |
| int type_tag = insert (type); |
| if (streaming_p ()) |
| { |
| dump (dumper::TREE) && dump ("Writing template parm type:%d %C:%N", |
| type_tag, TREE_CODE (type), type); |
| start (type); |
| tree_node_bools (type); |
| } |
| } |
| |
| if (inner != parm) |
| { |
| /* This is a template-template parameter. */ |
| unsigned tpl_levels = 0; |
| tpl_header (parm, &tpl_levels); |
| tpl_parms_fini (parm, tpl_levels); |
| } |
| |
| tree_node_vals (parm); |
| if (inner != parm) |
| tree_node_vals (inner); |
| if (type) |
| { |
| tree_node_vals (type); |
| if (DECL_NAME (inner) == auto_identifier |
| || DECL_NAME (inner) == decltype_auto_identifier) |
| { |
| /* Placeholder auto. */ |
| tree_node (DECL_INITIAL (inner)); |
| tree_node (DECL_SIZE_UNIT (inner)); |
| } |
| } |
| |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Wrote template parm:%d %C:%N", |
| parm_tag, TREE_CODE (parm), parm); |
| } |
| |
| tree |
| trees_in::tpl_parm_value () |
| { |
| tree parm = start (); |
| if (!parm || !tree_node_bools (parm)) |
| return NULL_TREE; |
| |
| int parm_tag = insert (parm); |
| dump (dumper::TREE) && dump ("Reading template parm:%d %C:%N", |
| parm_tag, TREE_CODE (parm), parm); |
| |
| tree inner = parm; |
| if (TREE_CODE (inner) == TEMPLATE_DECL) |
| { |
| inner = start (); |
| if (!inner || !tree_node_bools (inner)) |
| return NULL_TREE; |
| int inner_tag = insert (inner); |
| dump (dumper::TREE) && dump ("Reading inner template parm:%d %C:%N", |
| inner_tag, TREE_CODE (inner), inner); |
| DECL_TEMPLATE_RESULT (parm) = inner; |
| } |
| |
| tree type = NULL_TREE; |
| if (TREE_CODE (inner) == TYPE_DECL) |
| { |
| type = start (); |
| if (!type || !tree_node_bools (type)) |
| return NULL_TREE; |
| int type_tag = insert (type); |
| dump (dumper::TREE) && dump ("Reading template parm type:%d %C:%N", |
| type_tag, TREE_CODE (type), type); |
| |
| TREE_TYPE (inner) = TREE_TYPE (parm) = type; |
| TYPE_NAME (type) = parm; |
| } |
| |
| if (inner != parm) |
| { |
| /* A template template parameter. */ |
| unsigned tpl_levels = 0; |
| tpl_header (parm, &tpl_levels); |
| tpl_parms_fini (parm, tpl_levels); |
| } |
| |
| tree_node_vals (parm); |
| if (inner != parm) |
| tree_node_vals (inner); |
| if (type) |
| { |
| tree_node_vals (type); |
| if (DECL_NAME (inner) == auto_identifier |
| || DECL_NAME (inner) == decltype_auto_identifier) |
| { |
| /* Placeholder auto. */ |
| DECL_INITIAL (inner) = tree_node (); |
| DECL_SIZE_UNIT (inner) = tree_node (); |
| } |
| if (TYPE_CANONICAL (type)) |
| { |
| gcc_checking_assert (TYPE_CANONICAL (type) == type); |
| TYPE_CANONICAL (type) = canonical_type_parameter (type); |
| } |
| } |
| |
| dump (dumper::TREE) && dump ("Read template parm:%d %C:%N", |
| parm_tag, TREE_CODE (parm), parm); |
| |
| return parm; |
| } |
| |
| void |
| trees_out::install_entity (tree decl, depset *dep) |
| { |
| gcc_checking_assert (streaming_p ()); |
| |
| /* Write the entity index, so we can insert it as soon as we |
| know this is new. */ |
| u (dep ? dep->cluster + 1 : 0); |
| if (CHECKING_P && dep) |
| { |
| /* Add it to the entity map, such that we can tell it is |
| part of us. */ |
| bool existed; |
| unsigned *slot = &entity_map->get_or_insert |
| (DECL_UID (decl), &existed); |
| if (existed) |
| /* If it existed, it should match. */ |
| gcc_checking_assert (decl == (*entity_ary)[*slot]); |
| *slot = ~dep->cluster; |
| } |
| } |
| |
| bool |
| trees_in::install_entity (tree decl) |
| { |
| unsigned entity_index = u (); |
| if (!entity_index) |
| return false; |
| |
| if (entity_index > state->entity_num) |
| { |
| set_overrun (); |
| return false; |
| } |
| |
| /* Insert the real decl into the entity ary. */ |
| unsigned ident = state->entity_lwm + entity_index - 1; |
| (*entity_ary)[ident] = decl; |
| |
| /* And into the entity map, if it's not already there. */ |
| tree not_tmpl = STRIP_TEMPLATE (decl); |
| if (!DECL_LANG_SPECIFIC (not_tmpl) |
| || !DECL_MODULE_ENTITY_P (not_tmpl)) |
| { |
| retrofit_lang_decl (not_tmpl); |
| DECL_MODULE_ENTITY_P (not_tmpl) = true; |
| |
| /* Insert into the entity hash (it cannot already be there). */ |
| bool existed; |
| unsigned &slot = entity_map->get_or_insert (DECL_UID (decl), &existed); |
| gcc_checking_assert (!existed); |
| slot = ident; |
| } |
| |
| return true; |
| } |
| |
| static bool has_definition (tree decl); |
| |
| /* DECL is a decl node that must be written by value. DEP is the |
| decl's depset. */ |
| |
| void |
| trees_out::decl_value (tree decl, depset *dep) |
| { |
| /* We should not be writing clones or template parms. */ |
| gcc_checking_assert (DECL_P (decl) |
| && !DECL_CLONED_FUNCTION_P (decl) |
| && !DECL_TEMPLATE_PARM_P (decl)); |
| |
| /* We should never be writing non-typedef ptrmemfuncs by value. */ |
| gcc_checking_assert (TREE_CODE (decl) != TYPE_DECL |
| || DECL_ORIGINAL_TYPE (decl) |
| || !TYPE_PTRMEMFUNC_P (TREE_TYPE (decl))); |
| |
| merge_kind mk = get_merge_kind (decl, dep); |
| |
| if (CHECKING_P) |
| { |
| /* Never start in the middle of a template. */ |
| int use_tpl = -1; |
| if (tree ti = node_template_info (decl, use_tpl)) |
| gcc_checking_assert (TREE_CODE (TI_TEMPLATE (ti)) == OVERLOAD |
| || TREE_CODE (TI_TEMPLATE (ti)) == FIELD_DECL |
| || (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) |
| != decl)); |
| } |
| |
| if (streaming_p ()) |
| { |
| /* A new node -> tt_decl. */ |
| decl_val_count++; |
| i (tt_decl); |
| u (mk); |
| start (decl); |
| |
| if (mk != MK_unique) |
| { |
| if (!(mk & MK_template_mask) && !state->is_header ()) |
| { |
| /* Tell the importer whether this is a global module entity, |
| or a module entity. This bool merges into the next block |
| of bools. Sneaky. */ |
| tree o = get_originating_module_decl (decl); |
| bool is_mod = false; |
| |
| tree not_tmpl = STRIP_TEMPLATE (o); |
| if (DECL_LANG_SPECIFIC (not_tmpl) |
| && DECL_MODULE_PURVIEW_P (not_tmpl)) |
| is_mod = true; |
| |
| b (is_mod); |
| } |
| b (dep && dep->has_defn ()); |
| } |
| tree_node_bools (decl); |
| } |
| |
| int tag = insert (decl, WK_value); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Writing %s:%d %C:%N%S", merge_kind_name[mk], tag, |
| TREE_CODE (decl), decl, decl); |
| |
| tree inner = decl; |
| int inner_tag = 0; |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| inner = DECL_TEMPLATE_RESULT (decl); |
| inner_tag = insert (inner, WK_value); |
| |
| if (streaming_p ()) |
| { |
| int code = TREE_CODE (inner); |
| u (code); |
| start (inner, true); |
| tree_node_bools (inner); |
| dump (dumper::TREE) |
| && dump ("Writing %s:%d %C:%N%S", merge_kind_name[mk], inner_tag, |
| TREE_CODE (inner), inner, inner); |
| } |
| } |
| |
| tree type = NULL_TREE; |
| int type_tag = 0; |
| tree stub_decl = NULL_TREE; |
| int stub_tag = 0; |
| if (TREE_CODE (inner) == TYPE_DECL) |
| { |
| type = TREE_TYPE (inner); |
| bool has_type = (type == TYPE_MAIN_VARIANT (type) |
| && TYPE_NAME (type) == inner); |
| |
| if (streaming_p ()) |
| u (has_type ? TREE_CODE (type) : 0); |
| |
| if (has_type) |
| { |
| type_tag = insert (type, WK_value); |
| if (streaming_p ()) |
| { |
| start (type, true); |
| tree_node_bools (type); |
| dump (dumper::TREE) |
| && dump ("Writing type:%d %C:%N", type_tag, |
| TREE_CODE (type), type); |
| } |
| |
| stub_decl = TYPE_STUB_DECL (type); |
| bool has_stub = inner != stub_decl; |
| if (streaming_p ()) |
| u (has_stub ? TREE_CODE (stub_decl) : 0); |
| if (has_stub) |
| { |
| stub_tag = insert (stub_decl); |
| if (streaming_p ()) |
| { |
| start (stub_decl, true); |
| tree_node_bools (stub_decl); |
| dump (dumper::TREE) |
| && dump ("Writing stub_decl:%d %C:%N", stub_tag, |
| TREE_CODE (stub_decl), stub_decl); |
| } |
| } |
| else |
| stub_decl = NULL_TREE; |
| } |
| else |
| /* Regular typedef. */ |
| type = NULL_TREE; |
| } |
| |
| /* Stream the container, we want it correctly canonicalized before |
| we start emitting keys for this decl. */ |
| tree container = decl_container (decl); |
| |
| unsigned tpl_levels = 0; |
| if (decl != inner) |
| tpl_header (decl, &tpl_levels); |
| if (TREE_CODE (inner) == FUNCTION_DECL) |
| fn_parms_init (inner); |
| |
| /* Now write out the merging information, and then really |
| install the tag values. */ |
| key_mergeable (tag, mk, decl, inner, container, dep); |
| |
| if (streaming_p ()) |
| dump (dumper::MERGE) |
| && dump ("Wrote:%d's %s merge key %C:%N", tag, |
| merge_kind_name[mk], TREE_CODE (decl), decl); |
| |
| if (TREE_CODE (inner) == FUNCTION_DECL) |
| fn_parms_fini (inner); |
| |
| if (!is_key_order ()) |
| tree_node_vals (decl); |
| |
| if (inner_tag) |
| { |
| if (!is_key_order ()) |
| tree_node_vals (inner); |
| tpl_parms_fini (decl, tpl_levels); |
| } |
| |
| if (type && !is_key_order ()) |
| { |
| tree_node_vals (type); |
| if (stub_decl) |
| tree_node_vals (stub_decl); |
| } |
| |
| if (!is_key_order ()) |
| { |
| if (mk & MK_template_mask |
| || mk == MK_partial |
| || mk == MK_friend_spec) |
| { |
| if (mk != MK_partial) |
| { |
| // FIXME: We should make use of the merge-key by |
| // exposing it outside of key_mergeable. But this gets |
| // the job done. |
| auto *entry = reinterpret_cast <spec_entry *> (dep->deps[0]); |
| |
| if (streaming_p ()) |
| u (get_mergeable_specialization_flags (entry->tmpl, decl)); |
| tree_node (entry->tmpl); |
| tree_node (entry->args); |
| } |
| else |
| { |
| tree_node (CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner))); |
| tree_node (CLASSTYPE_TI_ARGS (TREE_TYPE (inner))); |
| } |
| } |
| tree_node (get_constraints (decl)); |
| } |
| |
| if (streaming_p ()) |
| { |
| /* Do not stray outside this section. */ |
| gcc_checking_assert (!dep || dep->section == dep_hash->section); |
| |
| /* Write the entity index, so we can insert it as soon as we |
| know this is new. */ |
| install_entity (decl, dep); |
| } |
| |
| if (VAR_OR_FUNCTION_DECL_P (inner) |
| && DECL_LANG_SPECIFIC (inner) |
| && DECL_MODULE_ATTACHMENTS_P (inner) |
| && !is_key_order ()) |
| { |
| /* Stream the attached entities. */ |
| auto *attach_vec = attached_table->get (inner); |
| unsigned num = attach_vec->length (); |
| if (streaming_p ()) |
| u (num); |
| for (unsigned ix = 0; ix != num; ix++) |
| { |
| tree attached = (*attach_vec)[ix]; |
| tree_node (attached); |
| if (streaming_p ()) |
| dump (dumper::MERGE) |
| && dump ("Written %d[%u] attached decl %N", tag, ix, attached); |
| } |
| } |
| |
| bool is_typedef = false; |
| if (!type && TREE_CODE (inner) == TYPE_DECL) |
| { |
| tree t = TREE_TYPE (inner); |
| unsigned tdef_flags = 0; |
| if (DECL_ORIGINAL_TYPE (inner) |
| && TYPE_NAME (TREE_TYPE (inner)) == inner) |
| { |
| tdef_flags |= 1; |
| if (TYPE_STRUCTURAL_EQUALITY_P (t) |
| && TYPE_DEPENDENT_P_VALID (t) |
| && TYPE_DEPENDENT_P (t)) |
| tdef_flags |= 2; |
| } |
| if (streaming_p ()) |
| u (tdef_flags); |
| |
| if (tdef_flags & 1) |
| { |
| /* A typedef type. */ |
| int type_tag = insert (t); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Cloned:%d %s %C:%N", type_tag, |
| tdef_flags & 2 ? "depalias" : "typedef", |
| TREE_CODE (t), t); |
| |
| is_typedef = true; |
| } |
| } |
| |
| if (streaming_p () && DECL_MAYBE_IN_CHARGE_CDTOR_P (decl)) |
| { |
| bool cloned_p |
| = (DECL_CHAIN (decl) && DECL_CLONED_FUNCTION_P (DECL_CHAIN (decl))); |
| bool needs_vtt_parm_p |
| = (cloned_p && CLASSTYPE_VBASECLASSES (DECL_CONTEXT (decl))); |
| bool omit_inherited_parms_p |
| = (cloned_p && DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl) |
| && base_ctor_omit_inherited_parms (decl)); |
| unsigned flags = (int (cloned_p) << 0 |
| | int (needs_vtt_parm_p) << 1 |
| | int (omit_inherited_parms_p) << 2); |
| u (flags); |
| dump (dumper::TREE) && dump ("CDTOR %N is %scloned", |
| decl, cloned_p ? "" : "not "); |
| } |
| |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Written decl:%d %C:%N", tag, |
| TREE_CODE (decl), decl); |
| |
| if (NAMESPACE_SCOPE_P (inner)) |
| gcc_checking_assert (!dep == (VAR_OR_FUNCTION_DECL_P (inner) |
| && DECL_LOCAL_DECL_P (inner))); |
| else if ((TREE_CODE (inner) == TYPE_DECL |
| && !is_typedef |
| && TYPE_NAME (TREE_TYPE (inner)) == inner) |
| || TREE_CODE (inner) == FUNCTION_DECL) |
| { |
| bool write_defn = !dep && has_definition (decl); |
| if (streaming_p ()) |
| u (write_defn); |
| if (write_defn) |
| write_definition (decl); |
| } |
| } |
| |
| tree |
| trees_in::decl_value () |
| { |
| int tag = 0; |
| bool is_mod = false; |
| bool has_defn = false; |
| unsigned mk_u = u (); |
| if (mk_u >= MK_hwm || !merge_kind_name[mk_u]) |
| { |
| set_overrun (); |
| return NULL_TREE; |
| } |
| |
| unsigned saved_unused = unused; |
| unused = 0; |
| |
| merge_kind mk = merge_kind (mk_u); |
| |
| tree decl = start (); |
| if (decl) |
| { |
| if (mk != MK_unique) |
| { |
| if (!(mk & MK_template_mask) && !state->is_header ()) |
| /* See note in trees_out about where this bool is sequenced. */ |
| is_mod = b (); |
| |
| has_defn = b (); |
| } |
| |
| if (!tree_node_bools (decl)) |
| decl = NULL_TREE; |
| } |
| |
| /* Insert into map. */ |
| tag = insert (decl); |
| if (decl) |
| dump (dumper::TREE) |
| && dump ("Reading:%d %C", tag, TREE_CODE (decl)); |
| |
| tree inner = decl; |
| int inner_tag = 0; |
| if (decl && TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| int code = u (); |
| inner = start (code); |
| if (inner && tree_node_bools (inner)) |
| DECL_TEMPLATE_RESULT (decl) = inner; |
| else |
| decl = NULL_TREE; |
| |
| inner_tag = insert (inner); |
| if (decl) |
| dump (dumper::TREE) |
| && dump ("Reading:%d %C", inner_tag, TREE_CODE (inner)); |
| } |
| |
| tree type = NULL_TREE; |
| int type_tag = 0; |
| tree stub_decl = NULL_TREE; |
| int stub_tag = 0; |
| if (decl && TREE_CODE (inner) == TYPE_DECL) |
| { |
| if (unsigned type_code = u ()) |
| { |
| type = start (type_code); |
| if (type && tree_node_bools (type)) |
| { |
| TREE_TYPE (inner) = type; |
| TYPE_NAME (type) = inner; |
| } |
| else |
| decl = NULL_TREE; |
| |
| type_tag = insert (type); |
| if (decl) |
| dump (dumper::TREE) |
| && dump ("Reading type:%d %C", type_tag, TREE_CODE (type)); |
| |
| if (unsigned stub_code = u ()) |
| { |
| stub_decl = start (stub_code); |
| if (stub_decl && tree_node_bools (stub_decl)) |
| { |
| TREE_TYPE (stub_decl) = type; |
| TYPE_STUB_DECL (type) = stub_decl; |
| } |
| else |
| decl = NULL_TREE; |
| |
| stub_tag = insert (stub_decl); |
| if (decl) |
| dump (dumper::TREE) |
| && dump ("Reading stub_decl:%d %C", stub_tag, |
| TREE_CODE (stub_decl)); |
| } |
| } |
| } |
| |
| if (!decl) |
| { |
| bail: |
| if (inner_tag != 0) |
| back_refs[~inner_tag] = NULL_TREE; |
| if (type_tag != 0) |
| back_refs[~type_tag] = NULL_TREE; |
| if (stub_tag != 0) |
| back_refs[~stub_tag] = NULL_TREE; |
| if (tag != 0) |
| back_refs[~tag] = NULL_TREE; |
| set_overrun (); |
| /* Bail. */ |
| unused = saved_unused; |
| return NULL_TREE; |
| } |
| |
| /* Read the container, to ensure it's already been streamed in. */ |
| tree container = decl_container (); |
| unsigned tpl_levels = 0; |
| |
| /* Figure out if this decl is already known about. */ |
| int parm_tag = 0; |
| |
| if (decl != inner) |
| if (!tpl_header (decl, &tpl_levels)) |
| goto bail; |
| if (TREE_CODE (inner) == FUNCTION_DECL) |
| parm_tag = fn_parms_init (inner); |
| |
| tree existing = key_mergeable (tag, mk, decl, inner, type, container, is_mod); |
| tree existing_inner = existing; |
| if (existing) |
| { |
| if (existing == error_mark_node) |
| goto bail; |
| |
| if (TREE_CODE (STRIP_TEMPLATE (existing)) == TYPE_DECL) |
| { |
| tree etype = TREE_TYPE (existing); |
| if (TYPE_LANG_SPECIFIC (etype) |
| && COMPLETE_TYPE_P (etype) |
| && !CLASSTYPE_MEMBER_VEC (etype)) |
| /* Give it a member vec, we're likely gonna be looking |
| inside it. */ |
| set_class_bindings (etype, -1); |
| } |
| |
| /* Install the existing decl into the back ref array. */ |
| register_duplicate (decl, existing); |
| back_refs[~tag] = existing; |
| if (inner_tag != 0) |
| { |
| existing_inner = DECL_TEMPLATE_RESULT (existing); |
| back_refs[~inner_tag] = existing_inner; |
| } |
| |
| if (type_tag != 0) |
| { |
| tree existing_type = TREE_TYPE (existing); |
| back_refs[~type_tag] = existing_type; |
| if (stub_tag != 0) |
| back_refs[~stub_tag] = TYPE_STUB_DECL (existing_type); |
| } |
| } |
| |
| if (parm_tag) |
| fn_parms_fini (parm_tag, inner, existing_inner, has_defn); |
| |
| if (!tree_node_vals (decl)) |
| goto bail; |
| |
| if (inner_tag) |
| { |
| gcc_checking_assert (DECL_TEMPLATE_RESULT (decl) == inner); |
| |
| if (!tree_node_vals (inner)) |
| goto bail; |
| |
| if (!tpl_parms_fini (decl, tpl_levels)) |
| goto bail; |
| } |
| |
| if (type && (!tree_node_vals (type) |
| || (stub_decl && !tree_node_vals (stub_decl)))) |
| goto bail; |
| |
| spec_entry spec; |
| unsigned spec_flags = 0; |
| if (mk & MK_template_mask |
| || mk == MK_partial |
| || mk == MK_friend_spec) |
| { |
| if (mk == MK_partial) |
| spec_flags = 2; |
| else |
| spec_flags = u (); |
| |
| spec.tmpl = tree_node (); |
| spec.args = tree_node (); |
| } |
| /* Hold constraints on the spec field, for a short while. */ |
| spec.spec = tree_node (); |
| |
| dump (dumper::TREE) && dump ("Read:%d %C:%N", tag, TREE_CODE (decl), decl); |
| |
| existing = back_refs[~tag]; |
| bool installed = install_entity (existing); |
| bool is_new = existing == decl; |
| |
| if (VAR_OR_FUNCTION_DECL_P (inner) |
| && DECL_LANG_SPECIFIC (inner) |
| && DECL_MODULE_ATTACHMENTS_P (inner)) |
| { |
| /* Read and maybe install the attached entities. */ |
| bool existed; |
| auto &set = attached_table->get_or_insert (STRIP_TEMPLATE (existing), |
| &existed); |
| unsigned num = u (); |
| if (is_new == existed) |
| set_overrun (); |
| if (is_new) |
| set.reserve (num); |
| for (unsigned ix = 0; !get_overrun () && ix != num; ix++) |
| { |
| tree attached = tree_node (); |
| dump (dumper::MERGE) |
| && dump ("Read %d[%u] %s attached decl %N", tag, ix, |
| is_new ? "new" : "matched", attached); |
| if (is_new) |
| set.quick_push (attached); |
| else if (set[ix] != attached) |
| set_overrun (); |
| } |
| } |
| |
| /* Regular typedefs will have a NULL TREE_TYPE at this point. */ |
| unsigned tdef_flags = 0; |
| bool is_typedef = false; |
| if (!type && TREE_CODE (inner) == TYPE_DECL) |
| { |
| tdef_flags = u (); |
| if (tdef_flags & 1) |
| is_typedef = true; |
| } |
| |
| if (is_new) |
| { |
| /* A newly discovered node. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL && DECL_VIRTUAL_P (decl)) |
| /* Mark this identifier as naming a virtual function -- |
| lookup_overrides relies on this optimization. */ |
| IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = true; |
| |
| if (installed) |
| { |
| /* Mark the entity as imported. */ |
| retrofit_lang_decl (inner); |
| DECL_MODULE_IMPORT_P (inner) = true; |
| } |
| |
| if (spec.spec) |
| set_constraints (decl, spec.spec); |
| |
| if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl)) |
| { |
| decl = cache_integer_cst (decl, true); |
| back_refs[~tag] = decl; |
| } |
| |
| if (is_typedef) |
| { |
| /* Frob it to be ready for cloning. */ |
| TREE_TYPE (inner) = DECL_ORIGINAL_TYPE (inner); |
| DECL_ORIGINAL_TYPE (inner) = NULL_TREE; |
| set_underlying_type (inner); |
| if (tdef_flags & 2) |
| { |
| /* Match instantiate_alias_template's handling. */ |
| tree type = TREE_TYPE (inner); |
| TYPE_DEPENDENT_P (type) = true; |
| TYPE_DEPENDENT_P_VALID (type) = true; |
| SET_TYPE_STRUCTURAL_EQUALITY (type); |
| } |
| } |
| |
| if (inner_tag) |
| /* Set the TEMPLATE_DECL's type. */ |
| TREE_TYPE (decl) = TREE_TYPE (inner); |
| |
| if (mk & MK_template_mask |
| || mk == MK_partial) |
| { |
| /* Add to specialization tables now that constraints etc are |
| added. */ |
| bool is_type = mk == MK_partial || !(mk & MK_tmpl_decl_mask); |
| |
| spec.spec = is_type ? type : mk & MK_tmpl_tmpl_mask ? inner : decl; |
| add_mergeable_specialization (!is_type, |
| !is_type && mk & MK_tmpl_alias_mask, |
| &spec, decl, spec_flags); |
| } |
| |
| if (NAMESPACE_SCOPE_P (decl) |
| && (mk == MK_named || mk == MK_unique |
| || mk == MK_enum || mk == MK_friend_spec) |
| && !(VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl))) |
| add_module_namespace_decl (CP_DECL_CONTEXT (decl), decl); |
| |
| if (DECL_ARTIFICIAL (decl) |
| && TREE_CODE (decl) == FUNCTION_DECL |
| && !DECL_TEMPLATE_INFO (decl) |
| && DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)) |
| && TYPE_SIZE (DECL_CONTEXT (decl)) |
| && !DECL_THUNK_P (decl)) |
| /* A new implicit member function, when the class is |
| complete. This means the importee declared it, and |
| we must now add it to the class. Note that implicit |
| member fns of template instantiations do not themselves |
| look like templates. */ |
| if (!install_implicit_member (inner)) |
| set_overrun (); |
| } |
| else |
| { |
| /* DECL is the to-be-discarded decl. Its internal pointers will |
| be to the EXISTING's structure. Frob it to point to its |
| own other structures, so loading its definition will alter |
| it, and not the existing decl. */ |
| dump (dumper::MERGE) && dump ("Deduping %N", existing); |
| |
| if (inner_tag) |
| DECL_TEMPLATE_RESULT (decl) = inner; |
| |
| if (type) |
| { |
| /* Point at the to-be-discarded type & decl. */ |
| TYPE_NAME (type) = inner; |
| TREE_TYPE (inner) = type; |
| |
| TYPE_STUB_DECL (type) = stub_decl ? stub_decl : inner; |
| if (stub_decl) |
| TREE_TYPE (stub_decl) = type; |
| } |
| |
| if (inner_tag) |
| /* Set the TEMPLATE_DECL's type. */ |
| TREE_TYPE (decl) = TREE_TYPE (inner); |
| |
| if (!is_matching_decl (existing, decl, is_typedef)) |
| unmatched_duplicate (existing); |
| |
| if (TREE_CODE (inner) == FUNCTION_DECL) |
| { |
| tree e_inner = STRIP_TEMPLATE (existing); |
| for (auto parm = DECL_ARGUMENTS (inner); |
| parm; parm = DECL_CHAIN (parm)) |
| DECL_CONTEXT (parm) = e_inner; |
| } |
| |
| /* And our result is the existing node. */ |
| decl = existing; |
| } |
| |
| if (mk == MK_friend_spec) |
| { |
| tree e = match_mergeable_specialization (true, &spec); |
| if (!e) |
| { |
| spec.spec = inner; |
| add_mergeable_specialization (true, false, &spec, decl, spec_flags); |
| } |
| else if (e != existing) |
| set_overrun (); |
| } |
| |
| if (is_typedef) |
| { |
| /* Insert the type into the array now. */ |
| tag = insert (TREE_TYPE (decl)); |
| dump (dumper::TREE) |
| && dump ("Cloned:%d typedef %C:%N", |
| tag, TREE_CODE (TREE_TYPE (decl)), TREE_TYPE (decl)); |
| } |
| |
| unused = saved_unused; |
| |
| if (DECL_MAYBE_IN_CHARGE_CDTOR_P (decl)) |
| { |
| unsigned flags = u (); |
| |
| if (is_new) |
| { |
| bool cloned_p = flags & 1; |
| dump (dumper::TREE) && dump ("CDTOR %N is %scloned", |
| decl, cloned_p ? "" : "not "); |
| if (cloned_p) |
| build_cdtor_clones (decl, flags & 2, flags & 4, |
| /* Update the member vec, if there is |
| one (we're in a different cluster |
| to the class defn). */ |
| CLASSTYPE_MEMBER_VEC (DECL_CONTEXT (decl))); |
| } |
| } |
| |
| if (!NAMESPACE_SCOPE_P (inner) |
| && ((TREE_CODE (inner) == TYPE_DECL |
| && !is_typedef |
| && TYPE_NAME (TREE_TYPE (inner)) == inner) |
| || TREE_CODE (inner) == FUNCTION_DECL) |
| && u ()) |
| read_definition (decl); |
| |
| return decl; |
| } |
| |
| /* DECL is an unnameable member of CTX. Return a suitable identifying |
| index. */ |
| |
| static unsigned |
| get_field_ident (tree ctx, tree decl) |
| { |
| gcc_checking_assert (TREE_CODE (decl) == USING_DECL |
| || !DECL_NAME (decl) |
| || IDENTIFIER_ANON_P (DECL_NAME (decl))); |
| |
| unsigned ix = 0; |
| for (tree fields = TYPE_FIELDS (ctx); |
| fields; fields = DECL_CHAIN (fields)) |
| { |
| if (fields == decl) |
| return ix; |
| |
| if (DECL_CONTEXT (fields) == ctx |
| && (TREE_CODE (fields) == USING_DECL |
| || (TREE_CODE (fields) == FIELD_DECL |
| && (!DECL_NAME (fields) |
| || IDENTIFIER_ANON_P (DECL_NAME (fields)))))) |
| /* Count this field. */ |
| ix++; |
| } |
| gcc_unreachable (); |
| } |
| |
| static tree |
| lookup_field_ident (tree ctx, unsigned ix) |
| { |
| for (tree fields = TYPE_FIELDS (ctx); |
| fields; fields = DECL_CHAIN (fields)) |
| if (DECL_CONTEXT (fields) == ctx |
| && (TREE_CODE (fields) == USING_DECL |
| || (TREE_CODE (fields) == FIELD_DECL |
| && (!DECL_NAME (fields) |
| || IDENTIFIER_ANON_P (DECL_NAME (fields)))))) |
| if (!ix--) |
| return fields; |
| |
| return NULL_TREE; |
| } |
| |
| /* Reference DECL. REF indicates the walk kind we are performing. |
| Return true if we should write this decl by value. */ |
| |
| bool |
| trees_out::decl_node (tree decl, walk_kind ref) |
| { |
| gcc_checking_assert (DECL_P (decl) && !DECL_TEMPLATE_PARM_P (decl) |
| && DECL_CONTEXT (decl)); |
| |
| if (ref == WK_value) |
| { |
| depset *dep = dep_hash->find_dependency (decl); |
| decl_value (decl, dep); |
| return false; |
| } |
| |
| switch (TREE_CODE (decl)) |
| { |
| default: |
| break; |
| |
| case FUNCTION_DECL: |
| gcc_checking_assert (!DECL_LOCAL_DECL_P (decl)); |
| break; |
| |
| case RESULT_DECL: |
| /* Unlike PARM_DECLs, RESULT_DECLs are only generated and |
| referenced when we're inside the function itself. */ |
| return true; |
| |
| case PARM_DECL: |
| { |
| if (streaming_p ()) |
| i (tt_parm); |
| tree_node (DECL_CONTEXT (decl)); |
| if (streaming_p ()) |
| { |
| /* That must have put this in the map. */ |
| walk_kind ref = ref_node (decl); |
| if (ref != WK_none) |
| // FIXME:OPTIMIZATION We can wander into bits of the |
| // template this was instantiated from. For instance |
| // deferred noexcept and default parms. Currently we'll |
| // end up cloning those bits of tree. It would be nice |
| // to reference those specific nodes. I think putting |
| // those things in the map when we reference their |
| // template by name. See the note in add_indirects. |
| return true; |
| |
| dump (dumper::TREE) |
| && dump ("Wrote %s reference %N", |
| TREE_CODE (decl) == PARM_DECL ? "parameter" : "result", |
| decl); |
| } |
| } |
| return false; |
| |
| case IMPORTED_DECL: |
| /* This describes a USING_DECL to the ME's debug machinery. It |
| originates from the fortran FE, and has nothing to do with |
| C++ modules. */ |
| return true; |
| |
| case LABEL_DECL: |
| return true; |
| |
| case CONST_DECL: |
| { |
| /* If I end up cloning enum decls, implementing C++20 using |
| E::v, this will need tweaking. */ |
| if (streaming_p ()) |
| i (tt_enum_decl); |
| tree ctx = DECL_CONTEXT (decl); |
| gcc_checking_assert (TREE_CODE (ctx) == ENUMERAL_TYPE); |
| tree_node (ctx); |
| tree_node (DECL_NAME (decl)); |
| |
| int tag = insert (decl); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote enum decl:%d %C:%N", tag, TREE_CODE (decl), decl); |
| return false; |
| } |
| break; |
| |
| case USING_DECL: |
| if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL) |
| break; |
| /* FALLTHROUGH */ |
| |
| case FIELD_DECL: |
| { |
| if (streaming_p ()) |
| i (tt_data_member); |
| |
| tree ctx = DECL_CONTEXT (decl); |
| tree_node (ctx); |
| |
| tree name = NULL_TREE; |
| |
| if (TREE_CODE (decl) == USING_DECL) |
| ; |
| else |
| { |
| name = DECL_NAME (decl); |
| if (name && IDENTIFIER_ANON_P (name)) |
| name = NULL_TREE; |
| } |
| |
| tree_node (name); |
| if (!name && streaming_p ()) |
| { |
| unsigned ix = get_field_ident (ctx, decl); |
| u (ix); |
| } |
| |
| int tag = insert (decl); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote member:%d %C:%N", tag, TREE_CODE (decl), decl); |
| return false; |
| } |
| break; |
| |
| case VAR_DECL: |
| gcc_checking_assert (!DECL_LOCAL_DECL_P (decl)); |
| if (DECL_VTABLE_OR_VTT_P (decl)) |
| { |
| /* VTT or VTABLE, they are all on the vtables list. */ |
| tree ctx = CP_DECL_CONTEXT (decl); |
| tree vtable = CLASSTYPE_VTABLES (ctx); |
| for (unsigned ix = 0; ; vtable = DECL_CHAIN (vtable), ix++) |
| if (vtable == decl) |
| { |
| gcc_checking_assert (DECL_VIRTUAL_P (decl)); |
| if (streaming_p ()) |
| { |
| u (tt_vtable); |
| u (ix); |
| dump (dumper::TREE) |
| && dump ("Writing vtable %N[%u]", ctx, ix); |
| } |
| tree_node (ctx); |
| return false; |
| } |
| gcc_unreachable (); |
| } |
| |
| if (DECL_TINFO_P (decl)) |
| { |
| tinfo: |
| /* A typeinfo, tt_tinfo_typedef or tt_tinfo_var. */ |
| bool is_var = TREE_CODE (decl) == VAR_DECL; |
| tree type = TREE_TYPE (decl); |
| unsigned ix = get_pseudo_tinfo_index (type); |
| if (streaming_p ()) |
| { |
| i (is_var ? tt_tinfo_var : tt_tinfo_typedef); |
| u (ix); |
| } |
| |
| if (is_var) |
| { |
| /* We also need the type it is for and mangled name, so |
| the reader doesn't need to complete the type (which |
| would break section ordering). The type it is for is |
| stashed on the name's TREE_TYPE. */ |
| tree name = DECL_NAME (decl); |
| tree_node (name); |
| type = TREE_TYPE (name); |
| tree_node (type); |
| } |
| |
| int tag = insert (decl); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote tinfo_%s:%d %u %N", is_var ? "var" : "type", |
| tag, ix, type); |
| |
| if (!is_var) |
| { |
| tag = insert (type); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote tinfo_type:%d %u %N", tag, ix, type); |
| } |
| return false; |
| } |
| break; |
| |
| case TYPE_DECL: |
| if (DECL_TINFO_P (decl)) |
| goto tinfo; |
| break; |
| } |
| |
| if (DECL_THUNK_P (decl)) |
| { |
| /* Thunks are similar to binfos -- write the thunked-to decl and |
| then thunk-specific key info. */ |
| if (streaming_p ()) |
| { |
| i (tt_thunk); |
| i (THUNK_FIXED_OFFSET (decl)); |
| } |
| |
| tree target = decl; |
| while (DECL_THUNK_P (target)) |
| target = THUNK_TARGET (target); |
| tree_node (target); |
| tree_node (THUNK_VIRTUAL_OFFSET (decl)); |
| int tag = insert (decl); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote:%d thunk %N to %N", tag, DECL_NAME (decl), target); |
| return false; |
| } |
| |
| if (DECL_CLONED_FUNCTION_P (decl)) |
| { |
| tree target = get_clone_target (decl); |
| if (streaming_p ()) |
| i (tt_clone_ref); |
| |
| tree_node (target); |
| tree_node (DECL_NAME (decl)); |
| int tag = insert (decl); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Wrote:%d clone %N of %N", tag, DECL_NAME (decl), target); |
| return false; |
| } |
| |
| /* Everything left should be a thing that is in the entity table. |
| Mostly things that can be defined outside of their (original |
| declaration) context. */ |
| gcc_checking_assert (TREE_CODE (decl) == TEMPLATE_DECL |
| || TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == FUNCTION_DECL |
| || TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == USING_DECL |
| || TREE_CODE (decl) == CONCEPT_DECL |
| || TREE_CODE (decl) == NAMESPACE_DECL); |
| |
| int use_tpl = -1; |
| tree ti = node_template_info (decl, use_tpl); |
| tree tpl = NULL_TREE; |
| |
| /* If this is the TEMPLATE_DECL_RESULT of a TEMPLATE_DECL, get the |
| TEMPLATE_DECL. Note TI_TEMPLATE is not a TEMPLATE_DECL for |
| (some) friends, so we need to check that. */ |
| // FIXME: Should local friend template specializations be by value? |
| // They don't get idents so we'll never know they're imported, but I |
| // think we can only reach them from the TU that defines the |
| // befriending class? |
| if (ti && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL |
| && DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == decl) |
| { |
| tpl = TI_TEMPLATE (ti); |
| partial_template: |
| if (streaming_p ()) |
| { |
| i (tt_template); |
| dump (dumper::TREE) |
| && dump ("Writing implicit template %C:%N%S", |
| TREE_CODE (tpl), tpl, tpl); |
| } |
| tree_node (tpl); |
| |
| /* Streaming TPL caused us to visit DECL and maybe its type. */ |
| gcc_checking_assert (TREE_VISITED (decl)); |
| if (DECL_IMPLICIT_TYPEDEF_P (decl)) |
| gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl))); |
| return false; |
| } |
| |
| tree ctx = CP_DECL_CONTEXT (decl); |
| depset *dep = NULL; |
| if (streaming_p ()) |
| dep = dep_hash->find_dependency (decl); |
| else if (TREE_CODE (ctx) != FUNCTION_DECL |
| || TREE_CODE (decl) == TEMPLATE_DECL |
| || (dep_hash->sneakoscope && DECL_IMPLICIT_TYPEDEF_P (decl)) |
| || (DECL_LANG_SPECIFIC (decl) |
| && DECL_MODULE_IMPORT_P (decl))) |
| { |
| auto kind = (TREE_CODE (decl) == NAMESPACE_DECL |
| && !DECL_NAMESPACE_ALIAS (decl) |
| ? depset::EK_NAMESPACE : depset::EK_DECL); |
| dep = dep_hash->add_dependency (decl, kind); |
| } |
| |
| if (!dep) |
| { |
| /* Some internal entity of context. Do by value. */ |
| decl_value (decl, NULL); |
| return false; |
| } |
| |
| if (dep->get_entity_kind () == depset::EK_REDIRECT) |
| { |
| /* The DECL_TEMPLATE_RESULT of a partial specialization. |
| Write the partial specialization's template. */ |
| depset *redirect = dep->deps[0]; |
| gcc_checking_assert (redirect->get_entity_kind () == depset::EK_PARTIAL); |
| tpl = redirect->get_entity (); |
| goto partial_template; |
| } |
| |
| if (streaming_p ()) |
| { |
| /* Locate the entity. */ |
| unsigned index = dep->cluster; |
| unsigned import = 0; |
| |
| if (dep->is_import ()) |
| import = dep->section; |
| else if (CHECKING_P) |
| /* It should be what we put there. */ |
| gcc_checking_assert (index == ~import_entity_index (decl)); |
| |
| #if CHECKING_P |
| gcc_assert (!import || importedness >= 0); |
| #endif |
| i (tt_entity); |
| u (import); |
| u (index); |
| } |
| |
| int tag = insert (decl); |
| if (streaming_p () && dump (dumper::TREE)) |
| { |
| char const *kind = "import"; |
| module_state *from = (*modules)[0]; |
| if (dep->is_import ()) |
| /* Rediscover the unremapped index. */ |
| from = import_entity_module (import_entity_index (decl)); |
| else |
| { |
| tree o = get_originating_module_decl (decl); |
| o = STRIP_TEMPLATE (o); |
| kind = (DECL_LANG_SPECIFIC (o) && DECL_MODULE_PURVIEW_P (o) |
| ? "purview" : "GMF"); |
| } |
| dump ("Wrote %s:%d %C:%N@%M", kind, |
| tag, TREE_CODE (decl), decl, from); |
| } |
| |
| add_indirects (decl); |
| |
| return false; |
| } |
| |
| void |
| trees_out::type_node (tree type) |
| { |
| gcc_assert (TYPE_P (type)); |
| |
| tree root = (TYPE_NAME (type) |
| ? TREE_TYPE (TYPE_NAME (type)) : TYPE_MAIN_VARIANT (type)); |
| |
| if (type != root) |
| { |
| if (streaming_p ()) |
| i (tt_variant_type); |
| tree_node (root); |
| |
| int flags = -1; |
| |
| if (TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE) |
| { |
| int quals = type_memfn_quals (type); |
| int rquals = type_memfn_rqual (type); |
| tree raises = TYPE_RAISES_EXCEPTIONS (type); |
| bool late = TYPE_HAS_LATE_RETURN_TYPE (type); |
| |
| if (raises != TYPE_RAISES_EXCEPTIONS (root) |
| || rquals != type_memfn_rqual (root) |
| || quals != type_memfn_quals (root) |
| || late != TYPE_HAS_LATE_RETURN_TYPE (root)) |
| flags = rquals | (int (late) << 2) | (quals << 3); |
| } |
| else |
| { |
| if (TYPE_USER_ALIGN (type)) |
| flags = TYPE_ALIGN_RAW (type); |
| } |
| |
| if (streaming_p ()) |
| i (flags); |
| |
| if (flags < 0) |
| ; |
| else if (TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE) |
| { |
| tree raises = TYPE_RAISES_EXCEPTIONS (type); |
| if (raises == TYPE_RAISES_EXCEPTIONS (root)) |
| raises = error_mark_node; |
| tree_node (raises); |
| } |
| |
| tree_node (TYPE_ATTRIBUTES (type)); |
| |
| if (streaming_p ()) |
| { |
| /* Qualifiers. */ |
| int rquals = cp_type_quals (root); |
| int quals = cp_type_quals (type); |
| if (quals == rquals) |
| quals = -1; |
| i (quals); |
| } |
| |
| if (ref_node (type) != WK_none) |
| { |
| int tag = insert (type); |
| if (streaming_p ()) |
| { |
| i (0); |
| dump (dumper::TREE) |
| && dump ("Wrote:%d variant type %C", tag, TREE_CODE (type)); |
| } |
| } |
| return; |
| } |
| |
| if (tree name = TYPE_NAME (type)) |
| if ((TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name)) |
| || DECL_TEMPLATE_PARM_P (name) |
| || TREE_CODE (type) == RECORD_TYPE |
| || TREE_CODE (type) == UNION_TYPE |
| || TREE_CODE (type) == ENUMERAL_TYPE) |
| { |
| /* We can meet template parms that we didn't meet in the |
| tpl_parms walk, because we're referring to a derived type |
| that was previously constructed from equivalent template |
| parms. */ |
| if (streaming_p ()) |
| { |
| i (tt_typedef_type); |
| dump (dumper::TREE) |
| && dump ("Writing %stypedef %C:%N", |
| DECL_IMPLICIT_TYPEDEF_P (name) ? "implicit " : "", |
| TREE_CODE (name), name); |
| } |
| tree_node (name); |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Wrote typedef %C:%N%S", |
| TREE_CODE (name), name, name); |
| gcc_checking_assert (TREE_VISITED (type)); |
| return; |
| } |
| |
| if (TYPE_PTRMEMFUNC_P (type)) |
| { |
| /* This is a distinct type node, masquerading as a structure. */ |
| tree fn_type = TYPE_PTRMEMFUNC_FN_TYPE (type); |
| if (streaming_p ()) |
| i (tt_ptrmem_type); |
| tree_node (fn_type); |
| int tag = insert (type); |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Written:%d ptrmem type", tag); |
| return; |
| } |
| |
| if (streaming_p ()) |
| { |
| u (tt_derived_type); |
| u (TREE_CODE (type)); |
| } |
| |
| tree_node (TREE_TYPE (type)); |
| switch (TREE_CODE (type)) |
| { |
| default: |
| /* We should never meet a type here that is indescribable in |
| terms of other types. */ |
| gcc_unreachable (); |
| |
| case ARRAY_TYPE: |
| tree_node (TYPE_DOMAIN (type)); |
| if (streaming_p ()) |
| /* Dependent arrays are constructed with TYPE_DEPENENT_P |
| already set. */ |
| u (TYPE_DEPENDENT_P (type)); |
| break; |
| |
| case COMPLEX_TYPE: |
| /* No additional data. */ |
| break; |
| |
| case BOOLEAN_TYPE: |
| /* A non-standard boolean type. */ |
| if (streaming_p ()) |
| u (TYPE_PRECISION (type)); |
| break; |
| |
| case INTEGER_TYPE: |
| if (TREE_TYPE (type)) |
| { |
| /* A range type (representing an array domain). */ |
| tree_node (TYPE_MIN_VALUE (type)); |
| tree_node (TYPE_MAX_VALUE (type)); |
| } |
| else |
| { |
| /* A new integral type (representing a bitfield). */ |
| if (streaming_p ()) |
| { |
| unsigned prec = TYPE_PRECISION (type); |
| bool unsigned_p = TYPE_UNSIGNED (type); |
| |
| u ((prec << 1) | unsigned_p); |
| } |
| } |
| break; |
| |
| case METHOD_TYPE: |
| case FUNCTION_TYPE: |
| { |
| gcc_checking_assert (type_memfn_rqual (type) == REF_QUAL_NONE); |
| |
| tree arg_types = TYPE_ARG_TYPES (type); |
| if (TREE_CODE (type) == METHOD_TYPE) |
| { |
| tree_node (TREE_TYPE (TREE_VALUE (arg_types))); |
| arg_types = TREE_CHAIN (arg_types); |
| } |
| tree_node (arg_types); |
| } |
| break; |
| |
| case OFFSET_TYPE: |
| tree_node (TYPE_OFFSET_BASETYPE (type)); |
| break; |
| |
| case POINTER_TYPE: |
| /* No additional data. */ |
| break; |
| |
| case REFERENCE_TYPE: |
| if (streaming_p ()) |
| u (TYPE_REF_IS_RVALUE (type)); |
| break; |
| |
| case DECLTYPE_TYPE: |
| case TYPEOF_TYPE: |
| case UNDERLYING_TYPE: |
| tree_node (TYPE_VALUES_RAW (type)); |
| if (TREE_CODE (type) == DECLTYPE_TYPE) |
| /* We stash a whole bunch of things into decltype's |
| flags. */ |
| if (streaming_p ()) |
| tree_node_bools (type); |
| break; |
| |
| case TYPE_ARGUMENT_PACK: |
| /* No additional data. */ |
| break; |
| |
| case TYPE_PACK_EXPANSION: |
| if (streaming_p ()) |
| u (PACK_EXPANSION_LOCAL_P (type)); |
| tree_node (PACK_EXPANSION_PARAMETER_PACKS (type)); |
| break; |
| |
| case TYPENAME_TYPE: |
| { |
| tree_node (TYPE_CONTEXT (type)); |
| tree_node (DECL_NAME (TYPE_NAME (type))); |
| tree_node (TYPENAME_TYPE_FULLNAME (type)); |
| if (streaming_p ()) |
| { |
| enum tag_types tag_type = none_type; |
| if (TYPENAME_IS_ENUM_P (type)) |
| tag_type = enum_type; |
| else if (TYPENAME_IS_CLASS_P (type)) |
| tag_type = class_type; |
| u (int (tag_type)); |
| } |
| } |
| break; |
| |
| case UNBOUND_CLASS_TEMPLATE: |
| { |
| tree decl = TYPE_NAME (type); |
| tree_node (DECL_CONTEXT (decl)); |
| tree_node (DECL_NAME (decl)); |
| tree_node (DECL_TEMPLATE_PARMS (decl)); |
| } |
| break; |
| |
| case VECTOR_TYPE: |
| if (streaming_p ()) |
| { |
| poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (type); |
| /* to_constant asserts that only coeff[0] is of interest. */ |
| wu (static_cast<unsigned HOST_WIDE_INT> (nunits.to_constant ())); |
| } |
| break; |
| } |
| |
| /* We may have met the type during emitting the above. */ |
| if (ref_node (type) != WK_none) |
| { |
| int tag = insert (type); |
| if (streaming_p ()) |
| { |
| i (0); |
| dump (dumper::TREE) |
| && dump ("Wrote:%d derived type %C", tag, TREE_CODE (type)); |
| } |
| } |
| |
| return; |
| } |
| |
| /* T is (mostly*) a non-mergeable node that must be written by value. |
| The mergeable case is a BINFO, which are as-if DECLSs. */ |
| |
| void |
| trees_out::tree_value (tree t) |
| { |
| /* We should never be writing a type by value. tree_type should |
| have streamed it, or we're going via its TYPE_DECL. */ |
| gcc_checking_assert (!TYPE_P (t)); |
| |
| if (DECL_P (t)) |
| /* No template, type, var or function, except anonymous |
| non-context vars. */ |
| gcc_checking_assert ((TREE_CODE (t) != TEMPLATE_DECL |
| && TREE_CODE (t) != TYPE_DECL |
| && (TREE_CODE (t) != VAR_DECL |
| || (!DECL_NAME (t) && !DECL_CONTEXT (t))) |
| && TREE_CODE (t) != FUNCTION_DECL)); |
| |
| if (streaming_p ()) |
| { |
| /* A new node -> tt_node. */ |
| tree_val_count++; |
| i (tt_node); |
| start (t); |
| tree_node_bools (t); |
| } |
| |
| if (TREE_CODE (t) == TREE_BINFO) |
| /* Binfos are decl-like and need merging information. */ |
| binfo_mergeable (t); |
| |
| int tag = insert (t, WK_value); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Writing tree:%d %C:%N", tag, TREE_CODE (t), t); |
| |
| tree_node_vals (t); |
| |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Written tree:%d %C:%N", tag, TREE_CODE (t), t); |
| } |
| |
| tree |
| trees_in::tree_value () |
| { |
| tree t = start (); |
| if (!t || !tree_node_bools (t)) |
| return NULL_TREE; |
| |
| tree existing = t; |
| if (TREE_CODE (t) == TREE_BINFO) |
| { |
| tree type; |
| unsigned ix = binfo_mergeable (&type); |
| if (TYPE_BINFO (type)) |
| { |
| /* We already have a definition, this must be a duplicate. */ |
| dump (dumper::MERGE) |
| && dump ("Deduping binfo %N[%u]", type, ix); |
| existing = TYPE_BINFO (type); |
| while (existing && ix--) |
| existing = TREE_CHAIN (existing); |
| if (existing) |
| register_duplicate (t, existing); |
| else |
| /* Error, mismatch -- diagnose in read_class_def's |
| checking. */ |
| existing = t; |
| } |
| } |
| |
| /* Insert into map. */ |
| int tag = insert (existing); |
| dump (dumper::TREE) |
| && dump ("Reading tree:%d %C", tag, TREE_CODE (t)); |
| |
| if (!tree_node_vals (t)) |
| { |
| back_refs[~tag] = NULL_TREE; |
| set_overrun (); |
| /* Bail. */ |
| return NULL_TREE; |
| } |
| |
| dump (dumper::TREE) && dump ("Read tree:%d %C:%N", tag, TREE_CODE (t), t); |
| |
| if (TREE_CODE (existing) == INTEGER_CST && !TREE_OVERFLOW (existing)) |
| { |
| existing = cache_integer_cst (t, true); |
| back_refs[~tag] = existing; |
| } |
| |
| return existing; |
| } |
| |
| /* Stream out tree node T. We automatically create local back |
| references, which is essentially a single pass lisp |
| self-referential structure pretty-printer. */ |
| |
| void |
| trees_out::tree_node (tree t) |
| { |
| dump.indent (); |
| walk_kind ref = ref_node (t); |
| if (ref == WK_none) |
| goto done; |
| |
| if (ref != WK_normal) |
| goto skip_normal; |
| |
| if (TREE_CODE (t) == IDENTIFIER_NODE) |
| { |
| /* An identifier node -> tt_id, tt_conv_id, tt_anon_id, tt_lambda_id. */ |
| int code = tt_id; |
| if (IDENTIFIER_ANON_P (t)) |
| code = IDENTIFIER_LAMBDA_P (t) ? tt_lambda_id : tt_anon_id; |
| else if (IDENTIFIER_CONV_OP_P (t)) |
| code = tt_conv_id; |
| |
| if (streaming_p ()) |
| i (code); |
| |
| if (code == tt_conv_id) |
| { |
| tree type = TREE_TYPE (t); |
| gcc_checking_assert (type || t == conv_op_identifier); |
| tree_node (type); |
| } |
| else if (code == tt_id && streaming_p ()) |
| str (IDENTIFIER_POINTER (t), IDENTIFIER_LENGTH (t)); |
| |
| int tag = insert (t); |
| if (streaming_p ()) |
| { |
| /* We know the ordering of the 4 id tags. */ |
| static const char *const kinds[] = |
| {"", "conv_op ", "anon ", "lambda "}; |
| dump (dumper::TREE) |
| && dump ("Written:%d %sidentifier:%N", tag, |
| kinds[code - tt_id], |
| code == tt_conv_id ? TREE_TYPE (t) : t); |
| } |
| goto done; |
| } |
| |
| if (TREE_CODE (t) == TREE_BINFO) |
| { |
| /* A BINFO -> tt_binfo. |
| We must do this by reference. We stream the binfo tree |
| itself when streaming its owning RECORD_TYPE. That we got |
| here means the dominating type is not in this SCC. */ |
| if (streaming_p ()) |
| i (tt_binfo); |
| binfo_mergeable (t); |
| gcc_checking_assert (!TREE_VISITED (t)); |
| int tag = insert (t); |
| if (streaming_p ()) |
| dump (dumper::TREE) && dump ("Inserting binfo:%d %N", tag, t); |
| goto done; |
| } |
| |
| if (TREE_CODE (t) == INTEGER_CST |
| && !TREE_OVERFLOW (t) |
| && TREE_CODE (TREE_TYPE (t)) == ENUMERAL_TYPE) |
| { |
| /* An integral constant of enumeral type. See if it matches one |
| of the enumeration values. */ |
| for (tree values = TYPE_VALUES (TREE_TYPE (t)); |
| values; values = TREE_CHAIN (values)) |
| { |
| tree decl = TREE_VALUE (values); |
| if (tree_int_cst_equal (DECL_INITIAL (decl), t)) |
| { |
| if (streaming_p ()) |
| u (tt_enum_value); |
| tree_node (decl); |
| dump (dumper::TREE) && dump ("Written enum value %N", decl); |
| goto done; |
| } |
| } |
| /* It didn't match. We'll write it a an explicit INTEGER_CST |
| node. */ |
| } |
| |
| if (TYPE_P (t)) |
| { |
| type_node (t); |
| goto done; |
| } |
| |
| if (DECL_P (t)) |
| { |
| if (DECL_TEMPLATE_PARM_P (t)) |
| { |
| tpl_parm_value (t); |
| goto done; |
| } |
| |
| if (!DECL_CONTEXT (t)) |
| { |
| /* There are a few cases of decls with no context. We'll write |
| these by value, but first assert they are cases we expect. */ |
| gcc_checking_assert (ref == WK_normal); |
| switch (TREE_CODE (t)) |
| { |
| default: gcc_unreachable (); |
| |
| case LABEL_DECL: |
| /* CASE_LABEL_EXPRs contain uncontexted LABEL_DECLs. */ |
| gcc_checking_assert (!DECL_NAME (t)); |
| break; |
| |
| case VAR_DECL: |
| /* AGGR_INIT_EXPRs cons up anonymous uncontexted VAR_DECLs. */ |
| gcc_checking_assert (!DECL_NAME (t) |
| && DECL_ARTIFICIAL (t)); |
| break; |
| |
| case PARM_DECL: |
| /* REQUIRES_EXPRs have a tree list of uncontexted |
| PARM_DECLS. It'd be nice if they had a |
| distinguishing flag to double check. */ |
| break; |
| } |
| goto by_value; |
| } |
| } |
| |
| skip_normal: |
| if (DECL_P (t) && !decl_node (t, ref)) |
| goto done; |
| |
| /* Otherwise by value */ |
| by_value: |
| tree_value (t); |
| |
| done: |
| /* And, breath out. */ |
| dump.outdent (); |
| } |
| |
| /* Stream in a tree node. */ |
| |
| tree |
| trees_in::tree_node (bool is_use) |
| { |
| if (get_overrun ()) |
| return NULL_TREE; |
| |
| dump.indent (); |
| int tag = i (); |
| tree res = NULL_TREE; |
| switch (tag) |
| { |
| default: |
| /* backref, pull it out of the map. */ |
| res = back_ref (tag); |
| break; |
| |
| case tt_null: |
| /* NULL_TREE. */ |
| break; |
| |
| case tt_fixed: |
| /* A fixed ref, find it in the fixed_ref array. */ |
| { |
| unsigned fix = u (); |
| if (fix < (*fixed_trees).length ()) |
| { |
| res = (*fixed_trees)[fix]; |
| dump (dumper::TREE) && dump ("Read fixed:%u %C:%N%S", fix, |
| TREE_CODE (res), res, res); |
| } |
| |
| if (!res) |
| set_overrun (); |
| } |
| break; |
| |
| case tt_parm: |
| { |
| tree fn = tree_node (); |
| if (fn && TREE_CODE (fn) == FUNCTION_DECL) |
| res = tree_node (); |
| if (res) |
| dump (dumper::TREE) |
| && dump ("Read %s reference %N", |
| TREE_CODE (res) == PARM_DECL ? "parameter" : "result", |
| res); |
| } |
| break; |
| |
| case tt_node: |
| /* A new node. Stream it in. */ |
| res = tree_value (); |
| break; |
| |
| case tt_decl: |
| /* A new decl. Stream it in. */ |
| res = decl_value (); |
| break; |
| |
| case tt_tpl_parm: |
| /* A template parameter. Stream it in. */ |
| res = tpl_parm_value (); |
| break; |
| |
| case tt_id: |
| /* An identifier node. */ |
| { |
| size_t l; |
| const char *chars = str (&l); |
| res = get_identifier_with_length (chars, l); |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Read identifier:%d %N", tag, res); |
| } |
| break; |
| |
| case tt_conv_id: |
| /* A conversion operator. Get the type and recreate the |
| identifier. */ |
| { |
| tree type = tree_node (); |
| if (!get_overrun ()) |
| { |
| res = type ? make_conv_op_name (type) : conv_op_identifier; |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Created conv_op:%d %S for %N", tag, res, type); |
| } |
| } |
| break; |
| |
| case tt_anon_id: |
| case tt_lambda_id: |
| /* An anonymous or lambda id. */ |
| { |
| res = make_anon_name (); |
| if (tag == tt_lambda_id) |
| IDENTIFIER_LAMBDA_P (res) = true; |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Read %s identifier:%d %N", |
| IDENTIFIER_LAMBDA_P (res) ? "lambda" : "anon", tag, res); |
| } |
| break; |
| |
| case tt_typedef_type: |
| res = tree_node (); |
| if (res) |
| { |
| dump (dumper::TREE) |
| && dump ("Read %stypedef %C:%N", |
| DECL_IMPLICIT_TYPEDEF_P (res) ? "implicit " : "", |
| TREE_CODE (res), res); |
| res = TREE_TYPE (res); |
| } |
| break; |
| |
| case tt_derived_type: |
| /* A type derived from some other type. */ |
| { |
| enum tree_code code = tree_code (u ()); |
| res = tree_node (); |
| |
| switch (code) |
| { |
| default: |
| set_overrun (); |
| break; |
| |
| case ARRAY_TYPE: |
| { |
| tree domain = tree_node (); |
| int dep = u (); |
| if (!get_overrun ()) |
| res = build_cplus_array_type (res, domain, dep); |
| } |
| break; |
| |
| case COMPLEX_TYPE: |
| if (!get_overrun ()) |
| res = build_complex_type (res); |
| break; |
| |
| case BOOLEAN_TYPE: |
| { |
| unsigned precision = u (); |
| if (!get_overrun ()) |
| res = build_nonstandard_boolean_type (precision); |
| } |
| break; |
| |
| case INTEGER_TYPE: |
| if (res) |
| { |
| /* A range type (representing an array domain). */ |
| tree min = tree_node (); |
| tree max = tree_node (); |
| |
| if (!get_overrun ()) |
| res = build_range_type (res, min, max); |
| } |
| else |
| { |
| /* A new integral type (representing a bitfield). */ |
| unsigned enc = u (); |
| if (!get_overrun ()) |
| res = build_nonstandard_integer_type (enc >> 1, enc & 1); |
| } |
| break; |
| |
| case FUNCTION_TYPE: |
| case METHOD_TYPE: |
| { |
| tree klass = code == METHOD_TYPE ? tree_node () : NULL_TREE; |
| tree args = tree_node (); |
| if (!get_overrun ()) |
| { |
| if (klass) |
| res = build_method_type_directly (klass, res, args); |
| else |
| res = build_function_type (res, args); |
| } |
| } |
| break; |
| |
| case OFFSET_TYPE: |
| { |
| tree base = tree_node (); |
| if (!get_overrun ()) |
| res = build_offset_type (base, res); |
| } |
| break; |
| |
| case POINTER_TYPE: |
| if (!get_overrun ()) |
| res = build_pointer_type (res); |
| break; |
| |
| case REFERENCE_TYPE: |
| { |
| bool rval = bool (u ()); |
| if (!get_overrun ()) |
| res = cp_build_reference_type (res, rval); |
| } |
| break; |
| |
| case DECLTYPE_TYPE: |
| case TYPEOF_TYPE: |
| case UNDERLYING_TYPE: |
| { |
| tree expr = tree_node (); |
| if (!get_overrun ()) |
| { |
| res = cxx_make_type (code); |
| TYPE_VALUES_RAW (res) = expr; |
| if (code == DECLTYPE_TYPE) |
| tree_node_bools (res); |
| SET_TYPE_STRUCTURAL_EQUALITY (res); |
| } |
| } |
| break; |
| |
| case TYPE_ARGUMENT_PACK: |
| if (!get_overrun ()) |
| { |
| tree pack = cxx_make_type (TYPE_ARGUMENT_PACK); |
| SET_ARGUMENT_PACK_ARGS (pack, res); |
| res = pack; |
| } |
| break; |
| |
| case TYPE_PACK_EXPANSION: |
| { |
| bool local = u (); |
| tree param_packs = tree_node (); |
| if (!get_overrun ()) |
| { |
| tree expn = cxx_make_type (TYPE_PACK_EXPANSION); |
| SET_TYPE_STRUCTURAL_EQUALITY (expn); |
| SET_PACK_EXPANSION_PATTERN (expn, res); |
| PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs; |
| PACK_EXPANSION_LOCAL_P (expn) = local; |
| res = expn; |
| } |
| } |
| break; |
| |
| case TYPENAME_TYPE: |
| { |
| tree ctx = tree_node (); |
| tree name = tree_node (); |
| tree fullname = tree_node (); |
| enum tag_types tag_type = tag_types (u ()); |
| |
| if (!get_overrun ()) |
| res = build_typename_type (ctx, name, fullname, tag_type); |
| } |
| break; |
| |
| case UNBOUND_CLASS_TEMPLATE: |
| { |
| tree ctx = tree_node (); |
| tree name = tree_node (); |
| tree parms = tree_node (); |
| |
| if (!get_overrun ()) |
| res = make_unbound_class_template_raw (ctx, name, parms); |
| } |
| break; |
| |
| case VECTOR_TYPE: |
| { |
| unsigned HOST_WIDE_INT nunits = wu (); |
| if (!get_overrun ()) |
| res = build_vector_type (res, static_cast<poly_int64> (nunits)); |
| } |
| break; |
| } |
| |
| int tag = i (); |
| if (!tag) |
| { |
| tag = insert (res); |
| if (res) |
| dump (dumper::TREE) |
| && dump ("Created:%d derived type %C", tag, code); |
| } |
| else |
| res = back_ref (tag); |
| } |
| break; |
| |
| case tt_variant_type: |
| /* Variant of some type. */ |
| { |
| res = tree_node (); |
| int flags = i (); |
| if (get_overrun ()) |
| ; |
| else if (flags < 0) |
| /* No change. */; |
| else if (TREE_CODE (res) == FUNCTION_TYPE |
| || TREE_CODE (res) == METHOD_TYPE) |
| { |
| cp_ref_qualifier rqual = cp_ref_qualifier (flags & 3); |
| bool late = (flags >> 2) & 1; |
| cp_cv_quals quals = cp_cv_quals (flags >> 3); |
| |
| tree raises = tree_node (); |
| if (raises == error_mark_node) |
| raises = TYPE_RAISES_EXCEPTIONS (res); |
| |
| res = build_cp_fntype_variant (res, rqual, raises, late); |
| if (TREE_CODE (res) == FUNCTION_TYPE) |
| res = apply_memfn_quals (res, quals, rqual); |
| } |
| else |
| { |
| res = build_aligned_type (res, (1u << flags) >> 1); |
| TYPE_USER_ALIGN (res) = true; |
| } |
| |
| if (tree attribs = tree_node ()) |
| res = cp_build_type_attribute_variant (res, attribs); |
| |
| int quals = i (); |
| if (quals >= 0 && !get_overrun ()) |
| res = cp_build_qualified_type (res, quals); |
| |
| int tag = i (); |
| if (!tag) |
| { |
| tag = insert (res); |
| if (res) |
| dump (dumper::TREE) |
| && dump ("Created:%d variant type %C", tag, TREE_CODE (res)); |
| } |
| else |
| res = back_ref (tag); |
| } |
| break; |
| |
| case tt_tinfo_var: |
| case tt_tinfo_typedef: |
| /* A tinfo var or typedef. */ |
| { |
| bool is_var = tag == tt_tinfo_var; |
| unsigned ix = u (); |
| tree type = NULL_TREE; |
| |
| if (is_var) |
| { |
| tree name = tree_node (); |
| type = tree_node (); |
| |
| if (!get_overrun ()) |
| res = get_tinfo_decl_direct (type, name, int (ix)); |
| } |
| else |
| { |
| if (!get_overrun ()) |
| { |
| type = get_pseudo_tinfo_type (ix); |
| res = TYPE_NAME (type); |
| } |
| } |
| if (res) |
| { |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Created tinfo_%s:%d %S:%u for %N", |
| is_var ? "var" : "decl", tag, res, ix, type); |
| if (!is_var) |
| { |
| tag = insert (type); |
| dump (dumper::TREE) |
| && dump ("Created tinfo_type:%d %u %N", tag, ix, type); |
| } |
| } |
| } |
| break; |
| |
| case tt_ptrmem_type: |
| /* A pointer to member function. */ |
| { |
| tree type = tree_node (); |
| if (type && TREE_CODE (type) == POINTER_TYPE |
| && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE) |
| { |
| res = build_ptrmemfunc_type (type); |
| int tag = insert (res); |
| dump (dumper::TREE) && dump ("Created:%d ptrmem type", tag); |
| } |
| else |
| set_overrun (); |
| } |
| break; |
| |
| case tt_enum_value: |
| /* An enum const value. */ |
| { |
| if (tree decl = tree_node ()) |
| { |
| dump (dumper::TREE) && dump ("Read enum value %N", decl); |
| res = DECL_INITIAL (decl); |
| } |
| |
| if (!res) |
| set_overrun (); |
| } |
| break; |
| |
| case tt_enum_decl: |
| /* An enum decl. */ |
| { |
| tree ctx = tree_node (); |
| tree name = tree_node (); |
| |
| if (!get_overrun () |
| && TREE_CODE (ctx) == ENUMERAL_TYPE) |
| res = find_enum_member (ctx, name); |
| |
| if (!res) |
| set_overrun (); |
| else |
| { |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Read enum decl:%d %C:%N", tag, TREE_CODE (res), res); |
| } |
| } |
| break; |
| |
| case tt_data_member: |
| /* A data member. */ |
| { |
| tree ctx = tree_node (); |
| tree name = tree_node (); |
| |
| if (!get_overrun () |
| && RECORD_OR_UNION_TYPE_P (ctx)) |
| { |
| if (name) |
| res = lookup_class_binding (ctx, name); |
| else |
| res = lookup_field_ident (ctx, u ()); |
| |
| if (!res |
| || TREE_CODE (res) != FIELD_DECL |
| || DECL_CONTEXT (res) != ctx) |
| res = NULL_TREE; |
| } |
| |
| if (!res) |
| set_overrun (); |
| else |
| { |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("Read member:%d %C:%N", tag, TREE_CODE (res), res); |
| } |
| } |
| break; |
| |
| case tt_binfo: |
| /* A BINFO. Walk the tree of the dominating type. */ |
| { |
| tree type; |
| unsigned ix = binfo_mergeable (&type); |
| if (type) |
| { |
| res = TYPE_BINFO (type); |
| for (; ix && res; res = TREE_CHAIN (res)) |
| ix--; |
| if (!res) |
| set_overrun (); |
| } |
| |
| if (get_overrun ()) |
| break; |
| |
| /* Insert binfo into backreferences. */ |
| tag = insert (res); |
| dump (dumper::TREE) && dump ("Read binfo:%d %N", tag, res); |
| } |
| break; |
| |
| case tt_vtable: |
| { |
| unsigned ix = u (); |
| tree ctx = tree_node (); |
| dump (dumper::TREE) && dump ("Reading vtable %N[%u]", ctx, ix); |
| if (TREE_CODE (ctx) == RECORD_TYPE && TYPE_LANG_SPECIFIC (ctx)) |
| for (res = CLASSTYPE_VTABLES (ctx); res; res = DECL_CHAIN (res)) |
| if (!ix--) |
| break; |
| if (!res) |
| set_overrun (); |
| } |
| break; |
| |
| case tt_thunk: |
| { |
| int fixed = i (); |
| tree target = tree_node (); |
| tree virt = tree_node (); |
| |
| for (tree thunk = DECL_THUNKS (target); |
| thunk; thunk = DECL_CHAIN (thunk)) |
| if (THUNK_FIXED_OFFSET (thunk) == fixed |
| && !THUNK_VIRTUAL_OFFSET (thunk) == !virt |
| && (!virt |
| || tree_int_cst_equal (virt, THUNK_VIRTUAL_OFFSET (thunk)))) |
| { |
| res = thunk; |
| break; |
| } |
| |
| int tag = insert (res); |
| if (res) |
| dump (dumper::TREE) |
| && dump ("Read:%d thunk %N to %N", tag, DECL_NAME (res), target); |
| else |
| set_overrun (); |
| } |
| break; |
| |
| case tt_clone_ref: |
| { |
| tree target = tree_node (); |
| tree name = tree_node (); |
| |
| if (DECL_P (target) && DECL_MAYBE_IN_CHARGE_CDTOR_P (target)) |
| { |
| tree clone; |
| FOR_EVERY_CLONE (clone, target) |
| if (DECL_NAME (clone) == name) |
| { |
| res = clone; |
| break; |
| } |
| } |
| |
| if (!res) |
| set_overrun (); |
| int tag = insert (res); |
| if (res) |
| dump (dumper::TREE) |
| && dump ("Read:%d clone %N of %N", tag, DECL_NAME (res), target); |
| else |
| set_overrun (); |
| } |
| break; |
| |
| case tt_entity: |
| /* Index into the entity table. Perhaps not loaded yet! */ |
| { |
| unsigned origin = state->slurp->remap_module (u ()); |
| unsigned ident = u (); |
| module_state *from = (*modules)[origin]; |
| |
| if (!origin || ident >= from->entity_num) |
| set_overrun (); |
| if (!get_overrun ()) |
| { |
| binding_slot *slot = &(*entity_ary)[from->entity_lwm + ident]; |
| if (slot->is_lazy ()) |
| if (!from->lazy_load (ident, slot)) |
| set_overrun (); |
| res = *slot; |
| } |
| |
| if (res) |
| { |
| const char *kind = (origin != state->mod ? "Imported" : "Named"); |
| int tag = insert (res); |
| dump (dumper::TREE) |
| && dump ("%s:%d %C:%N@%M", kind, tag, TREE_CODE (res), |
| res, (*modules)[origin]); |
| |
| if (!add_indirects (res)) |
| { |
| set_overrun (); |
| res = NULL_TREE; |
| } |
| } |
| } |
| break; |
| |
| case tt_template: |
| /* A template. */ |
| if (tree tpl = tree_node ()) |
| { |
| res = DECL_TEMPLATE_RESULT (tpl); |
| dump (dumper::TREE) |
| && dump ("Read template %C:%N", TREE_CODE (res), res); |
| } |
| break; |
| } |
| |
| if (is_use && !unused && res && DECL_P (res) && !TREE_USED (res)) |
| { |
| /* Mark decl used as mark_used does -- we cannot call |
| mark_used in the middle of streaming, we only need a subset |
| of its functionality. */ |
| TREE_USED (res) = true; |
| |
| /* And for structured bindings also the underlying decl. */ |
| if (DECL_DECOMPOSITION_P (res) && DECL_DECOMP_BASE (res)) |
| TREE_USED (DECL_DECOMP_BASE (res)) = true; |
| |
| if (DECL_CLONED_FUNCTION_P (res)) |
| TREE_USED (DECL_CLONED_FUNCTION (res)) = true; |
| } |
| |
| dump.outdent (); |
| return res; |
| } |
| |
| void |
| trees_out::tpl_parms (tree parms, unsigned &tpl_levels) |
| { |
| if (!parms) |
| return; |
| |
| if (TREE_VISITED (parms)) |
| { |
| ref_node (parms); |
| return; |
| } |
| |
| tpl_parms (TREE_CHAIN (parms), tpl_levels); |
| |
| tree vec = TREE_VALUE (parms); |
| unsigned len = TREE_VEC_LENGTH (vec); |
| /* Depth. */ |
| int tag = insert (parms); |
| if (streaming_p ()) |
| { |
| i (len + 1); |
| dump (dumper::TREE) |
| && dump ("Writing template parms:%d level:%N length:%d", |
| tag, TREE_PURPOSE (parms), len); |
| } |
| tree_node (TREE_PURPOSE (parms)); |
| |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree parm = TREE_VEC_ELT (vec, ix); |
| tree decl = TREE_VALUE (parm); |
| |
| gcc_checking_assert (DECL_TEMPLATE_PARM_P (decl)); |
| if (CHECKING_P) |
| switch (TREE_CODE (decl)) |
| { |
| default: gcc_unreachable (); |
| |
| case TEMPLATE_DECL: |
| gcc_assert ((TREE_CODE (TREE_TYPE (decl)) == TEMPLATE_TEMPLATE_PARM) |
| && (TREE_CODE (DECL_TEMPLATE_RESULT (decl)) == TYPE_DECL) |
| && (TYPE_NAME (TREE_TYPE (decl)) == decl)); |
| break; |
| |
| case TYPE_DECL: |
| gcc_assert ((TREE_CODE (TREE_TYPE (decl)) == TEMPLATE_TYPE_PARM) |
| && (TYPE_NAME (TREE_TYPE (decl)) == decl)); |
| break; |
| |
| case PARM_DECL: |
| gcc_assert ((TREE_CODE (DECL_INITIAL (decl)) == TEMPLATE_PARM_INDEX) |
| && (TREE_CODE (TEMPLATE_PARM_DECL (DECL_INITIAL (decl))) |
| == CONST_DECL) |
| && (DECL_TEMPLATE_PARM_P |
| (TEMPLATE_PARM_DECL (DECL_INITIAL (decl))))); |
| break; |
| } |
| |
| tree_node (decl); |
| tree_node (TEMPLATE_PARM_CONSTRAINTS (parm)); |
| } |
| |
| tpl_levels++; |
| } |
| |
| tree |
| trees_in::tpl_parms (unsigned &tpl_levels) |
| { |
| tree parms = NULL_TREE; |
| |
| while (int len = i ()) |
| { |
| if (len < 0) |
| { |
| parms = back_ref (len); |
| continue; |
| } |
| |
| len -= 1; |
| parms = tree_cons (NULL_TREE, NULL_TREE, parms); |
| int tag = insert (parms); |
| TREE_PURPOSE (parms) = tree_node (); |
| |
| dump (dumper::TREE) |
| && dump ("Reading template parms:%d level:%N length:%d", |
| tag, TREE_PURPOSE (parms), len); |
| |
| tree vec = make_tree_vec (len); |
| for (int ix = 0; ix != len; ix++) |
| { |
| tree decl = tree_node (); |
| if (!decl) |
| return NULL_TREE; |
| |
| tree parm = build_tree_list (NULL, decl); |
| TEMPLATE_PARM_CONSTRAINTS (parm) = tree_node (); |
| |
| TREE_VEC_ELT (vec, ix) = parm; |
| } |
| |
| TREE_VALUE (parms) = vec; |
| tpl_levels++; |
| } |
| |
| return parms; |
| } |
| |
| void |
| trees_out::tpl_parms_fini (tree tmpl, unsigned tpl_levels) |
| { |
| for (tree parms = DECL_TEMPLATE_PARMS (tmpl); |
| tpl_levels--; parms = TREE_CHAIN (parms)) |
| { |
| tree vec = TREE_VALUE (parms); |
| |
| tree_node (TREE_TYPE (vec)); |
| tree dflt = error_mark_node; |
| for (unsigned ix = TREE_VEC_LENGTH (vec); ix--;) |
| { |
| tree parm = TREE_VEC_ELT (vec, ix); |
| if (dflt) |
| { |
| dflt = TREE_PURPOSE (parm); |
| tree_node (dflt); |
| } |
| |
| if (streaming_p ()) |
| { |
| tree decl = TREE_VALUE (parm); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| tree ctx = DECL_CONTEXT (decl); |
| tree inner = DECL_TEMPLATE_RESULT (decl); |
| tree tpi = (TREE_CODE (inner) == TYPE_DECL |
| ? TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (decl)) |
| : DECL_INITIAL (inner)); |
| bool original = (TEMPLATE_PARM_LEVEL (tpi) |
| == TEMPLATE_PARM_ORIG_LEVEL (tpi)); |
| /* Original template template parms have a context |
| of their owning template. Reduced ones do not. */ |
| gcc_checking_assert (original ? ctx == tmpl : !ctx); |
| } |
| } |
| } |
| } |
| } |
| |
| bool |
| trees_in::tpl_parms_fini (tree tmpl, unsigned tpl_levels) |
| { |
| for (tree parms = DECL_TEMPLATE_PARMS (tmpl); |
| tpl_levels--; parms = TREE_CHAIN (parms)) |
| { |
| tree vec = TREE_VALUE (parms); |
| tree dflt = error_mark_node; |
| |
| TREE_TYPE (vec) = tree_node (); |
| for (unsigned ix = TREE_VEC_LENGTH (vec); ix--;) |
| { |
| tree parm = TREE_VEC_ELT (vec, ix); |
| if (dflt) |
| { |
| dflt = tree_node (); |
| if (get_overrun ()) |
| return false; |
| TREE_PURPOSE (parm) = dflt; |
| } |
| |
| tree decl = TREE_VALUE (parm); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| tree inner = DECL_TEMPLATE_RESULT (decl); |
| tree tpi = (TREE_CODE (inner) == TYPE_DECL |
| ? TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (decl)) |
| : DECL_INITIAL (inner)); |
| bool original = (TEMPLATE_PARM_LEVEL (tpi) |
| == TEMPLATE_PARM_ORIG_LEVEL (tpi)); |
| /* Original template template parms have a context |
| of their owning template. Reduced ones do not. */ |
| if (original) |
| DECL_CONTEXT (decl) = tmpl; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /* PARMS is a LIST, one node per level. |
| TREE_VALUE is a TREE_VEC of parm info for that level. |
| each ELT is a TREE_LIST |
| TREE_VALUE is PARM_DECL, TYPE_DECL or TEMPLATE_DECL |
| TREE_PURPOSE is the default value. */ |
| |
| void |
| trees_out::tpl_header (tree tpl, unsigned *tpl_levels) |
| { |
| tree parms = DECL_TEMPLATE_PARMS (tpl); |
| tpl_parms (parms, *tpl_levels); |
| |
| /* Mark end. */ |
| if (streaming_p ()) |
| u (0); |
| |
| if (*tpl_levels) |
| tree_node (TEMPLATE_PARMS_CONSTRAINTS (parms)); |
| } |
| |
| bool |
| trees_in::tpl_header (tree tpl, unsigned *tpl_levels) |
| { |
| tree parms = tpl_parms (*tpl_levels); |
| if (!parms) |
| return false; |
| |
| DECL_TEMPLATE_PARMS (tpl) = parms; |
| |
| if (*tpl_levels) |
| TEMPLATE_PARMS_CONSTRAINTS (parms) = tree_node (); |
| |
| return true; |
| } |
| |
| /* Stream skeleton parm nodes, with their flags, type & parm indices. |
| All the parms will have consecutive tags. */ |
| |
| void |
| trees_out::fn_parms_init (tree fn) |
| { |
| /* First init them. */ |
| int base_tag = ref_num - 1; |
| int ix = 0; |
| for (tree parm = DECL_ARGUMENTS (fn); |
| parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| if (streaming_p ()) |
| { |
| start (parm); |
| tree_node_bools (parm); |
| } |
| int tag = insert (parm); |
| gcc_checking_assert (base_tag - ix == tag); |
| } |
| /* Mark the end. */ |
| if (streaming_p ()) |
| u (0); |
| |
| /* Now stream their contents. */ |
| ix = 0; |
| for (tree parm = DECL_ARGUMENTS (fn); |
| parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Writing parm:%d %u (%N) of %N", |
| base_tag - ix, ix, parm, fn); |
| tree_node_vals (parm); |
| } |
| } |
| |
| /* Build skeleton parm nodes, read their flags, type & parm indices. */ |
| |
| int |
| trees_in::fn_parms_init (tree fn) |
| { |
| int base_tag = ~(int)back_refs.length (); |
| |
| tree *parm_ptr = &DECL_ARGUMENTS (fn); |
| int ix = 0; |
| for (; int code = u (); ix++) |
| { |
| tree parm = start (code); |
| if (!tree_node_bools (parm)) |
| return 0; |
| |
| int tag = insert (parm); |
| gcc_checking_assert (base_tag - ix == tag); |
| *parm_ptr = parm; |
| parm_ptr = &DECL_CHAIN (parm); |
| } |
| |
| ix = 0; |
| for (tree parm = DECL_ARGUMENTS (fn); |
| parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| dump (dumper::TREE) |
| && dump ("Reading parm:%d %u (%N) of %N", |
| base_tag - ix, ix, parm, fn); |
| if (!tree_node_vals (parm)) |
| return 0; |
| } |
| |
| return base_tag; |
| } |
| |
| /* Read the remaining parm node data. Replace with existing (if |
| non-null) in the map. */ |
| |
| void |
| trees_in::fn_parms_fini (int tag, tree fn, tree existing, bool is_defn) |
| { |
| tree existing_parm = existing ? DECL_ARGUMENTS (existing) : NULL_TREE; |
| tree parms = DECL_ARGUMENTS (fn); |
| unsigned ix = 0; |
| for (tree parm = parms; parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| if (existing_parm) |
| { |
| if (is_defn && !DECL_SAVED_TREE (existing)) |
| { |
| /* If we're about to become the definition, set the |
| names of the parms from us. */ |
| DECL_NAME (existing_parm) = DECL_NAME (parm); |
| DECL_SOURCE_LOCATION (existing_parm) = DECL_SOURCE_LOCATION (parm); |
| } |
| |
| back_refs[~tag] = existing_parm; |
| existing_parm = DECL_CHAIN (existing_parm); |
| } |
| tag--; |
| } |
| } |
| |
| /* DEP is the depset of some decl we're streaming by value. Determine |
| the merging behaviour. */ |
| |
| merge_kind |
| trees_out::get_merge_kind (tree decl, depset *dep) |
| { |
| if (!dep) |
| { |
| if (VAR_OR_FUNCTION_DECL_P (decl)) |
| { |
| /* Any var or function with template info should have DEP. */ |
| gcc_checking_assert (!DECL_LANG_SPECIFIC (decl) |
| || !DECL_TEMPLATE_INFO (decl)); |
| if (DECL_LOCAL_DECL_P (decl)) |
| return MK_unique; |
| } |
| |
| /* Either unique, or some member of a class that cannot have an |
| out-of-class definition. For instance a FIELD_DECL. */ |
| tree ctx = CP_DECL_CONTEXT (decl); |
| if (TREE_CODE (ctx) == FUNCTION_DECL) |
| { |
| /* USING_DECLs cannot have DECL_TEMPLATE_INFO -- this isn't |
| permitting them to have one. */ |
| gcc_checking_assert (TREE_CODE (decl) == USING_DECL |
| || !DECL_LANG_SPECIFIC (decl) |
| || !DECL_TEMPLATE_INFO (decl)); |
| |
| return MK_unique; |
| } |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) |
| return MK_local_friend; |
| |
| gcc_checking_assert (TYPE_P (ctx)); |
| if (TREE_CODE (decl) == USING_DECL) |
| return MK_field; |
| |
| if (TREE_CODE (decl) == FIELD_DECL) |
| { |
| if (DECL_NAME (decl)) |
| { |
| /* Anonymous FIELD_DECLs have a NULL name. */ |
| gcc_checking_assert (!IDENTIFIER_ANON_P (DECL_NAME (decl))); |
| return MK_named; |
| } |
| |
| if (!DECL_NAME (decl) |
| && !RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) |
| && !DECL_BIT_FIELD_REPRESENTATIVE (decl)) |
| { |
| /* The underlying storage unit for a bitfield. We do not |
| need to dedup it, because it's only reachable through |
| the bitfields it represents. And those are deduped. */ |
| // FIXME: Is that assertion correct -- do we ever fish it |
| // out and put it in an expr? |
| gcc_checking_assert ((TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE |
| ? TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) |
| : TREE_CODE (TREE_TYPE (decl))) |
| == INTEGER_TYPE); |
| return MK_unique; |
| } |
| |
| return MK_field; |
| } |
| |
| if (TREE_CODE (decl) == CONST_DECL) |
| return MK_named; |
| |
| if (TREE_CODE (decl) == VAR_DECL |
| && DECL_VTABLE_OR_VTT_P (decl)) |
| return MK_vtable; |
| |
| if (DECL_THUNK_P (decl)) |
| /* Thunks are unique-enough, because they're only referenced |
| from the vtable. And that's either new (so we want the |
| thunks), or it's a duplicate (so it will be dropped). */ |
| return MK_unique; |
| |
| /* There should be no other cases. */ |
| gcc_unreachable (); |
| } |
| |
| gcc_checking_assert (TREE_CODE (decl) != FIELD_DECL |
| && TREE_CODE (decl) != USING_DECL |
| && TREE_CODE (decl) != CONST_DECL); |
| |
| if (is_key_order ()) |
| { |
| /* When doing the mergeablilty graph, there's an indirection to |
| the actual depset. */ |
| gcc_assert (dep->is_special ()); |
| dep = dep->deps[0]; |
| } |
| |
| gcc_checking_assert (decl == dep->get_entity ()); |
| |
| merge_kind mk = MK_named; |
| switch (dep->get_entity_kind ()) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case depset::EK_PARTIAL: |
| mk = MK_partial; |
| break; |
| |
| case depset::EK_DECL: |
| { |
| tree ctx = CP_DECL_CONTEXT (decl); |
| |
| switch (TREE_CODE (ctx)) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case FUNCTION_DECL: |
| // FIXME: This can occur for (a) voldemorty TYPE_DECLS |
| // (which are returned from a function), or (b) |
| // block-scope class definitions in template functions. |
| // These are as unique as the containing function. While |
| // on read-back we can discover if the CTX was a |
| // duplicate, we don't have a mechanism to get from the |
| // existing CTX to the existing version of this decl. |
| gcc_checking_assert |
| (DECL_IMPLICIT_TYPEDEF_P (STRIP_TEMPLATE (decl))); |
| |
| mk = MK_unique; |
| break; |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| if (DECL_NAME (decl) == as_base_identifier) |
| mk = MK_as_base; |
| else if (IDENTIFIER_ANON_P (DECL_NAME (decl))) |
| mk = MK_field; |
| break; |
| |
| case NAMESPACE_DECL: |
| if (DECL_IMPLICIT_TYPEDEF_P (STRIP_TEMPLATE (decl)) |
| && LAMBDA_TYPE_P (TREE_TYPE (decl))) |
| if (tree scope |
| = LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR |
| (TREE_TYPE (decl)))) |
| if (TREE_CODE (scope) == VAR_DECL |
| && DECL_MODULE_ATTACHMENTS_P (scope)) |
| { |
| mk = MK_attached; |
| break; |
| } |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) |
| mk = MK_local_friend; |
| else if (IDENTIFIER_ANON_P (DECL_NAME (decl))) |
| { |
| if (DECL_IMPLICIT_TYPEDEF_P (decl) |
| && UNSCOPED_ENUM_P (TREE_TYPE (decl)) |
| && TYPE_VALUES (TREE_TYPE (decl))) |
| /* Keyed by first enum value, and underlying type. */ |
| mk = MK_enum; |
| else |
| /* No way to merge it, it is an ODR land-mine. */ |
| mk = MK_unique; |
| } |
| } |
| } |
| break; |
| |
| case depset::EK_SPECIALIZATION: |
| { |
| gcc_checking_assert (dep->is_special ()); |
| |
| if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL) |
| /* An block-scope classes of templates are themselves |
| templates. */ |
| gcc_checking_assert (DECL_IMPLICIT_TYPEDEF_P (decl)); |
| |
| if (dep->is_friend_spec ()) |
| mk = MK_friend_spec; |
| else if (dep->is_type_spec ()) |
| mk = MK_type_spec; |
| else if (dep->is_alias ()) |
| mk = MK_alias_spec; |
| else |
| mk = MK_decl_spec; |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]); |
| if (TREE_CODE (entry->spec) != TEMPLATE_DECL) |
| mk = merge_kind (mk | MK_tmpl_tmpl_mask); |
| } |
| } |
| break; |
| } |
| |
| return mk; |
| } |
| |
| |
| /* The container of DECL -- not necessarily its context! */ |
| |
| tree |
| trees_out::decl_container (tree decl) |
| { |
| int use_tpl; |
| tree tpl = NULL_TREE; |
| if (tree template_info = node_template_info (decl, use_tpl)) |
| tpl = TI_TEMPLATE (template_info); |
| if (tpl == decl) |
| tpl = nullptr; |
| |
| /* Stream the template we're instantiated from. */ |
| tree_node (tpl); |
| |
| tree container = NULL_TREE; |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) |
| container = DECL_CHAIN (decl); |
| else |
| container = CP_DECL_CONTEXT (decl); |
| |
| if (TYPE_P (container)) |
| container = TYPE_NAME (container); |
| |
| tree_node (container); |
| |
| return container; |
| } |
| |
| tree |
| trees_in::decl_container () |
| { |
| /* The maybe-template. */ |
| (void)tree_node (); |
| |
| tree container = tree_node (); |
| |
| return container; |
| } |
| |
| /* Write out key information about a mergeable DEP. Does not write |
| the contents of DEP itself. The context has already been |
| written. The container has already been streamed. */ |
| |
| void |
| trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, |
| tree container, depset *dep) |
| { |
| if (dep && is_key_order ()) |
| { |
| gcc_checking_assert (dep->is_special ()); |
| dep = dep->deps[0]; |
| } |
| |
| if (streaming_p ()) |
| dump (dumper::MERGE) |
| && dump ("Writing:%d's %s merge key (%s) %C:%N", tag, merge_kind_name[mk], |
| dep ? dep->entity_kind_name () : "contained", |
| TREE_CODE (decl), decl); |
| |
| /* Now write the locating information. */ |
| if (mk & MK_template_mask) |
| { |
| /* Specializations are located via their originating template, |
| and the set of template args they specialize. */ |
| gcc_checking_assert (dep && dep->is_special ()); |
| spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]); |
| |
| tree_node (entry->tmpl); |
| tree_node (entry->args); |
| if (mk & MK_tmpl_decl_mask) |
| if (flag_concepts && TREE_CODE (inner) == VAR_DECL) |
| { |
| /* Variable template partial specializations might need |
| constraints (see spec_hasher::equal). It's simpler to |
| write NULL when we don't need them. */ |
| tree constraints = NULL_TREE; |
| |
| if (uses_template_parms (entry->args)) |
| constraints = get_constraints (inner); |
| tree_node (constraints); |
| } |
| |
| if (CHECKING_P) |
| { |
| /* Make sure we can locate the decl. */ |
| tree existing = match_mergeable_specialization |
| (bool (mk & MK_tmpl_decl_mask), entry); |
| |
| gcc_assert (existing); |
| if (mk & MK_tmpl_decl_mask) |
| { |
| if (mk & MK_tmpl_alias_mask) |
| /* It should be in both tables. */ |
| gcc_checking_assert |
| (same_type_p (match_mergeable_specialization (false, entry), |
| TREE_TYPE (existing))); |
| if (mk & MK_tmpl_tmpl_mask) |
| existing = DECL_TI_TEMPLATE (existing); |
| } |
| else |
| { |
| if (mk & MK_tmpl_tmpl_mask) |
| existing = CLASSTYPE_TI_TEMPLATE (existing); |
| else |
| existing = TYPE_NAME (existing); |
| } |
| |
| /* The walkabout should have found ourselves. */ |
| gcc_checking_assert (TREE_CODE (decl) == TYPE_DECL |
| ? same_type_p (TREE_TYPE (decl), |
| TREE_TYPE (existing)) |
| : existing == decl); |
| } |
| } |
| else if (mk != MK_unique) |
| { |
| merge_key key; |
| tree name = DECL_NAME (decl); |
| |
| switch (mk) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case MK_named: |
| case MK_friend_spec: |
| if (IDENTIFIER_CONV_OP_P (name)) |
| name = conv_op_identifier; |
| |
| if (TREE_CODE (inner) == FUNCTION_DECL) |
| { |
| /* Functions are distinguished by parameter types. */ |
| tree fn_type = TREE_TYPE (inner); |
| |
| key.ref_q = type_memfn_rqual (fn_type); |
| key.args = TYPE_ARG_TYPES (fn_type); |
| |
| if (tree reqs = get_constraints (inner)) |
| { |
| if (cxx_dialect < cxx20) |
| reqs = CI_ASSOCIATED_CONSTRAINTS (reqs); |
| else |
| reqs = CI_DECLARATOR_REQS (reqs); |
| key.constraints = reqs; |
| } |
| |
| if (IDENTIFIER_CONV_OP_P (name) |
| || (decl != inner |
| && !(name == fun_identifier |
| /* In case the user names something _FUN */ |
| && LAMBDA_TYPE_P (DECL_CONTEXT (inner))))) |
| /* And a function template, or conversion operator needs |
| the return type. Except for the _FUN thunk of a |
| generic lambda, which has a recursive decl_type'd |
| return type. */ |
| // FIXME: What if the return type is a voldemort? |
| key.ret = fndecl_declared_return_type (inner); |
| } |
| break; |
| |
| case MK_field: |
| { |
| unsigned ix = 0; |
| if (TREE_CODE (inner) != FIELD_DECL) |
| name = NULL_TREE; |
| else |
| gcc_checking_assert (!name || !IDENTIFIER_ANON_P (name)); |
| |
| for (tree field = TYPE_FIELDS (TREE_TYPE (container)); |
| ; field = DECL_CHAIN (field)) |
| { |
| tree finner = STRIP_TEMPLATE (field); |
| if (TREE_CODE (finner) == TREE_CODE (inner)) |
| { |
| if (finner == inner) |
| break; |
| ix++; |
| } |
| } |
| key.index = ix; |
| } |
| break; |
| |
| case MK_vtable: |
| { |
| tree vtable = CLASSTYPE_VTABLES (TREE_TYPE (container)); |
| for (unsigned ix = 0; ; vtable = DECL_CHAIN (vtable), ix++) |
| if (vtable == decl) |
| { |
| key.index = ix; |
| break; |
| } |
| name = NULL_TREE; |
| } |
| break; |
| |
| case MK_as_base: |
| gcc_checking_assert |
| (decl == TYPE_NAME (CLASSTYPE_AS_BASE (TREE_TYPE (container)))); |
| break; |
| |
| case MK_local_friend: |
| { |
| /* Find by index on the class's DECL_LIST */ |
| unsigned ix = 0; |
| for (tree decls = CLASSTYPE_DECL_LIST (TREE_CHAIN (decl)); |
| decls; decls = TREE_CHAIN (decls)) |
| if (!TREE_PURPOSE (decls)) |
| { |
| tree frnd = friend_from_decl_list (TREE_VALUE (decls)); |
| if (frnd == decl) |
| break; |
| ix++; |
| } |
| key.index = ix; |
| name = NULL_TREE; |
| } |
| break; |
| |
| case MK_enum: |
| { |
| /* Anonymous enums are located by their first identifier, |
| and underlying type. */ |
| tree type = TREE_TYPE (decl); |
| |
| gcc_checking_assert (UNSCOPED_ENUM_P (type)); |
| /* Using the type name drops the bit precision we might |
| have been using on the enum. */ |
| key.ret = TYPE_NAME (ENUM_UNDERLYING_TYPE (type)); |
| if (tree values = TYPE_VALUES (type)) |
| name = DECL_NAME (TREE_VALUE (values)); |
| } |
| break; |
| |
| case MK_attached: |
| { |
| gcc_checking_assert (LAMBDA_TYPE_P (TREE_TYPE (inner))); |
| tree scope = LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR |
| (TREE_TYPE (inner))); |
| gcc_checking_assert (TREE_CODE (scope) == VAR_DECL); |
| auto *root = attached_table->get (scope); |
| unsigned ix = root->length (); |
| /* If we don't find it, we'll write a really big number |
| that the reader will ignore. */ |
| while (ix--) |
| if ((*root)[ix] == inner) |
| break; |
| |
| /* Use the attached-to decl as the 'name'. */ |
| name = scope; |
| key.index = ix; |
| } |
| break; |
| |
| case MK_partial: |
| { |
| key.constraints = get_constraints (inner); |
| key.ret = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner)); |
| key.args = CLASSTYPE_TI_ARGS (TREE_TYPE (inner)); |
| } |
| break; |
| } |
| |
| tree_node (name); |
| if (streaming_p ()) |
| { |
| unsigned code = (key.ref_q << 0) | (key.index << 2); |
| u (code); |
| } |
| |
| if (mk == MK_enum) |
| tree_node (key.ret); |
| else if (mk == MK_partial |
| || (mk == MK_named && inner |
| && TREE_CODE (inner) == FUNCTION_DECL)) |
| { |
| tree_node (key.ret); |
| tree arg = key.args; |
| if (mk == MK_named) |
| while (arg && arg != void_list_node) |
| { |
| tree_node (TREE_VALUE (arg)); |
| arg = TREE_CHAIN (arg); |
| } |
| tree_node (arg); |
| tree_node (key.constraints); |
| } |
| } |
| } |
| |
| /* DECL is a new declaration that may be duplicated in OVL. Use RET & |
| ARGS to find its clone, or NULL. If DECL's DECL_NAME is NULL, this |
| has been found by a proxy. It will be an enum type located by it's |
| first member. |
| |
| We're conservative with matches, so ambiguous decls will be |
| registered as different, then lead to a lookup error if the two |
| modules are both visible. Perhaps we want to do something similar |
| to duplicate decls to get ODR errors on loading? We already have |
| some special casing for namespaces. */ |
| |
| static tree |
| check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key) |
| { |
| tree found = NULL_TREE; |
| for (ovl_iterator iter (ovl); !found && iter; ++iter) |
| { |
| tree match = *iter; |
| |
| tree d_inner = decl; |
| tree m_inner = match; |
| |
| again: |
| if (TREE_CODE (d_inner) != TREE_CODE (m_inner)) |
| { |
| if (TREE_CODE (match) == NAMESPACE_DECL |
| && !DECL_NAMESPACE_ALIAS (match)) |
| /* Namespaces are never overloaded. */ |
| found = match; |
| |
| continue; |
| } |
| |
| switch (TREE_CODE (d_inner)) |
| { |
| case TEMPLATE_DECL: |
| if (template_heads_equivalent_p (d_inner, m_inner)) |
| { |
| d_inner = DECL_TEMPLATE_RESULT (d_inner); |
| m_inner = DECL_TEMPLATE_RESULT (m_inner); |
| if (d_inner == error_mark_node |
| && TYPE_DECL_ALIAS_P (m_inner)) |
| { |
| found = match; |
| break; |
| } |
| goto again; |
| } |
| break; |
| |
| case FUNCTION_DECL: |
| if (tree m_type = TREE_TYPE (m_inner)) |
| if ((!key.ret |
| || same_type_p (key.ret, fndecl_declared_return_type (m_inner))) |
| && type_memfn_rqual (m_type) == key.ref_q |
| && compparms (key.args, TYPE_ARG_TYPES (m_type)) |
| /* Reject if old is a "C" builtin and new is not "C". |
| Matches decls_match behaviour. */ |
| && (!DECL_IS_UNDECLARED_BUILTIN (m_inner) |
| || !DECL_EXTERN_C_P (m_inner) |
| || DECL_EXTERN_C_P (d_inner))) |
| { |
| tree m_reqs = get_constraints (m_inner); |
| if (m_reqs) |
| { |
| if (cxx_dialect < cxx20) |
| m_reqs = CI_ASSOCIATED_CONSTRAINTS (m_reqs); |
| else |
| m_reqs = CI_DECLARATOR_REQS (m_reqs); |
| } |
| |
| if (cp_tree_equal (key.constraints, m_reqs)) |
| found = match; |
| } |
| break; |
| |
| case TYPE_DECL: |
| if (DECL_IMPLICIT_TYPEDEF_P (d_inner) |
| == DECL_IMPLICIT_TYPEDEF_P (m_inner)) |
| { |
| if (!IDENTIFIER_ANON_P (DECL_NAME (m_inner))) |
| return match; |
| else if (mk == MK_enum |
| && (TYPE_NAME (ENUM_UNDERLYING_TYPE (TREE_TYPE (m_inner))) |
| == key.ret)) |
| found = match; |
| } |
| break; |
| |
| default: |
| found = match; |
| break; |
| } |
| } |
| |
| return found; |
| } |
| |
| /* DECL, INNER & TYPE are a skeleton set of nodes for a decl. Only |
| the bools have been filled in. Read its merging key and merge it. |
| Returns the existing decl if there is one. */ |
| |
| tree |
| trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, |
| tree type, tree container, bool is_mod) |
| { |
| const char *kind = "new"; |
| tree existing = NULL_TREE; |
| |
| if (mk & MK_template_mask) |
| { |
| // FIXME: We could stream the specialization hash? |
| spec_entry spec; |
| spec.tmpl = tree_node (); |
| spec.args = tree_node (); |
| |
| if (get_overrun ()) |
| return error_mark_node; |
| |
| DECL_NAME (decl) = DECL_NAME (spec.tmpl); |
| DECL_CONTEXT (decl) = DECL_CONTEXT (spec.tmpl); |
| DECL_NAME (inner) = DECL_NAME (decl); |
| DECL_CONTEXT (inner) = DECL_CONTEXT (decl); |
| |
| tree constr = NULL_TREE; |
| bool is_decl = mk & MK_tmpl_decl_mask; |
| if (is_decl) |
| { |
| if (flag_concepts && TREE_CODE (inner) == VAR_DECL) |
| { |
| constr = tree_node (); |
| if (constr) |
| set_constraints (inner, constr); |
| } |
| spec.spec = (mk & MK_tmpl_tmpl_mask) ? inner : decl; |
| } |
| else |
| spec.spec = type; |
| existing = match_mergeable_specialization (is_decl, &spec); |
| if (constr) |
| /* We'll add these back later, if this is the new decl. */ |
| remove_constraints (inner); |
| |
| if (!existing) |
| ; /* We'll add to the table once read. */ |
| else if (mk & MK_tmpl_decl_mask) |
| { |
| /* A declaration specialization. */ |
| if (mk & MK_tmpl_tmpl_mask) |
| existing = DECL_TI_TEMPLATE (existing); |
| } |
| else |
| { |
| /* A type specialization. */ |
| if (mk & MK_tmpl_tmpl_mask) |
| existing = CLASSTYPE_TI_TEMPLATE (existing); |
| else |
| existing = TYPE_NAME (existing); |
| } |
| } |
| else if (mk == MK_unique) |
| kind = "unique"; |
| else |
| { |
| tree name = tree_node (); |
| |
| merge_key key; |
| unsigned code = u (); |
| key.ref_q = cp_ref_qualifier ((code >> 0) & 3); |
| key.index = code >> 2; |
| |
| if (mk == MK_enum) |
| key.ret = tree_node (); |
| else if (mk == MK_partial |
| || ((mk == MK_named || mk == MK_friend_spec) |
| && TREE_CODE (inner) == FUNCTION_DECL)) |
| { |
| key.ret = tree_node (); |
| tree arg, *arg_ptr = &key.args; |
| while ((arg = tree_node ()) |
| && arg != void_list_node |
| && mk != MK_partial) |
| { |
| *arg_ptr = tree_cons (NULL_TREE, arg, NULL_TREE); |
| arg_ptr = &TREE_CHAIN (*arg_ptr); |
| } |
| *arg_ptr = arg; |
| key.constraints = tree_node (); |
| } |
| |
| if (get_overrun ()) |
| return error_mark_node; |
| |
| if (mk < MK_indirect_lwm) |
| { |
| DECL_NAME (decl) = name; |
| DECL_CONTEXT (decl) = FROB_CONTEXT (container); |
| } |
| DECL_NAME (inner) = DECL_NAME (decl); |
| DECL_CONTEXT (inner) = DECL_CONTEXT (decl); |
| |
| if (mk == MK_partial) |
| { |
| for (tree spec = DECL_TEMPLATE_SPECIALIZATIONS (key.ret); |
| spec; spec = TREE_CHAIN (spec)) |
| { |
| tree tmpl = TREE_VALUE (spec); |
| if (template_args_equal (key.args, |
| CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))) |
| && cp_tree_equal (key.constraints, |
| get_constraints |
| (DECL_TEMPLATE_RESULT (tmpl)))) |
| { |
| existing = tmpl; |
| break; |
| } |
| } |
| } |
| else |
| switch (TREE_CODE (container)) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case NAMESPACE_DECL: |
| if (mk == MK_attached) |
| { |
| if (DECL_LANG_SPECIFIC (name) |
| && VAR_OR_FUNCTION_DECL_P (name) |
| && DECL_MODULE_ATTACHMENTS_P (name)) |
| if (auto *set = attached_table->get (name)) |
| if (key.index < set->length ()) |
| { |
| existing = (*set)[key.index]; |
| if (existing) |
| { |
| gcc_checking_assert |
| (DECL_IMPLICIT_TYPEDEF_P (existing)); |
| if (inner != decl) |
| existing |
| = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (existing)); |
| } |
| } |
| } |
| else if (is_mod && !(state->is_module () || state->is_partition ())) |
| kind = "unique"; |
| else |
| { |
| gcc_checking_assert (mk == MK_named || mk == MK_enum); |
| tree mvec; |
| tree *vslot = mergeable_namespace_slots (container, name, |
| !is_mod, &mvec); |
| existing = check_mergeable_decl (mk, decl, *vslot, key); |
| if (!existing) |
| add_mergeable_namespace_entity (vslot, decl); |
| else |
| { |
| /* Note that we now have duplicates to deal with in |
| name lookup. */ |
| if (is_mod) |
| BINDING_VECTOR_PARTITION_DUPS_P (mvec) = true; |
| else |
| BINDING_VECTOR_GLOBAL_DUPS_P (mvec) = true; |
| } |
| } |
| break; |
| |
| case FUNCTION_DECL: |
| // FIXME: What about a voldemort? how do we find what it |
| // duplicates? Do we have to number vmorts relative to |
| // their containing function? But how would that work |
| // when matching an in-TU declaration? |
| kind = "unique"; |
| break; |
| |
| case TYPE_DECL: |
| if (is_mod && !(state->is_module () || state->is_partition ()) |
| /* Implicit member functions can come from |
| anywhere. */ |
| && !(DECL_ARTIFICIAL (decl) |
| && TREE_CODE (decl) == FUNCTION_DECL |
| && !DECL_THUNK_P (decl))) |
| kind = "unique"; |
| else |
| { |
| tree ctx = TREE_TYPE (container); |
| |
| /* For some reason templated enumeral types are not marked |
| as COMPLETE_TYPE_P, even though they have members. |
| This may well be a bug elsewhere. */ |
| if (TREE_CODE (ctx) == ENUMERAL_TYPE) |
| existing = find_enum_member (ctx, name); |
| else if (COMPLETE_TYPE_P (ctx)) |
| { |
| switch (mk) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case MK_named: |
| existing = lookup_class_binding (ctx, name); |
| if (existing) |
| { |
| tree inner = decl; |
| if (TREE_CODE (inner) == TEMPLATE_DECL |
| && !DECL_MEMBER_TEMPLATE_P (inner)) |
| inner = DECL_TEMPLATE_RESULT (inner); |
| |
| existing = check_mergeable_decl |
| (mk, inner, existing, key); |
| |
| if (!existing && DECL_ALIAS_TEMPLATE_P (decl)) |
| {} // FIXME: Insert into specialization |
| // tables, we'll need the arguments for that! |
| } |
| break; |
| |
| case MK_field: |
| { |
| unsigned ix = key.index; |
| for (tree field = TYPE_FIELDS (ctx); |
| field; field = DECL_CHAIN (field)) |
| { |
| tree finner = STRIP_TEMPLATE (field); |
| if (TREE_CODE (finner) == TREE_CODE (inner)) |
| if (!ix--) |
| { |
| existing = field; |
| break; |
| } |
| } |
| } |
| break; |
| |
| case MK_vtable: |
| { |
| unsigned ix = key.index; |
| for (tree vtable = CLASSTYPE_VTABLES (ctx); |
| vtable; vtable = DECL_CHAIN (vtable)) |
| if (!ix--) |
| { |
| existing = vtable; |
| break; |
| } |
| } |
| break; |
| |
| case MK_as_base: |
| { |
| tree as_base = CLASSTYPE_AS_BASE (ctx); |
| if (as_base && as_base != ctx) |
| existing = TYPE_NAME (as_base); |
| } |
| break; |
| |
| case MK_local_friend: |
| { |
| unsigned ix = key.index; |
| for (tree decls = CLASSTYPE_DECL_LIST (ctx); |
| decls; decls = TREE_CHAIN (decls)) |
| if (!TREE_PURPOSE (decls) && !ix--) |
| { |
| existing |
| = friend_from_decl_list (TREE_VALUE (decls)); |
| break; |
| } |
| } |
| break; |
| } |
| |
| if (existing && mk < MK_indirect_lwm && mk != MK_partial |
| && TREE_CODE (decl) == TEMPLATE_DECL |
| && !DECL_MEMBER_TEMPLATE_P (decl)) |
| { |
| tree ti; |
| if (DECL_IMPLICIT_TYPEDEF_P (existing)) |
| ti = TYPE_TEMPLATE_INFO (TREE_TYPE (existing)); |
| else |
| ti = DECL_TEMPLATE_INFO (existing); |
| existing = TI_TEMPLATE (ti); |
| } |
| } |
| } |
| } |
| } |
| |
| dump (dumper::MERGE) |
| && dump ("Read:%d's %s merge key (%s) %C:%N", tag, merge_kind_name[mk], |
| existing ? "matched" : kind, TREE_CODE (decl), decl); |
| |
| return existing; |
| } |
| |
| void |
| trees_out::binfo_mergeable (tree binfo) |
| { |
| tree dom = binfo; |
| while (tree parent = BINFO_INHERITANCE_CHAIN (dom)) |
| dom = parent; |
| tree type = BINFO_TYPE (dom); |
| gcc_checking_assert (TYPE_BINFO (type) == dom); |
| tree_node (type); |
| if (streaming_p ()) |
| { |
| unsigned ix = 0; |
| for (; dom != binfo; dom = TREE_CHAIN (dom)) |
| ix++; |
| u (ix); |
| } |
| } |
| |
| unsigned |
| trees_in::binfo_mergeable (tree *type) |
| { |
| *type = tree_node (); |
| return u (); |
| } |
| |
| /* DECL is a just streamed mergeable decl that should match EXISTING. Check |
| it does and issue an appropriate diagnostic if not. Merge any |
| bits from DECL to EXISTING. This is stricter matching than |
| decls_match, because we can rely on ODR-sameness, and we cannot use |
| decls_match because it can cause instantiations of constraints. */ |
| |
| bool |
| trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) |
| { |
| // FIXME: We should probably do some duplicate decl-like stuff here |
| // (beware, default parms should be the same?) Can we just call |
| // duplicate_decls and teach it how to handle the module-specific |
| // permitted/required duplications? |
| |
| // We know at this point that the decls have matched by key, so we |
| // can elide some of the checking |
| gcc_checking_assert (TREE_CODE (existing) == TREE_CODE (decl)); |
| |
| tree d_inner = decl; |
| tree e_inner = existing; |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| d_inner = DECL_TEMPLATE_RESULT (d_inner); |
| e_inner = DECL_TEMPLATE_RESULT (e_inner); |
| gcc_checking_assert (TREE_CODE (e_inner) == TREE_CODE (d_inner)); |
| } |
| |
| if (TREE_CODE (d_inner) == FUNCTION_DECL) |
| { |
| tree e_ret = fndecl_declared_return_type (existing); |
| tree d_ret = fndecl_declared_return_type (decl); |
| |
| if (decl != d_inner && DECL_NAME (d_inner) == fun_identifier |
| && LAMBDA_TYPE_P (DECL_CONTEXT (d_inner))) |
| /* This has a recursive type that will compare different. */; |
| else if (!same_type_p (d_ret, e_ret)) |
| goto mismatch; |
| |
| tree e_type = TREE_TYPE (e_inner); |
| tree d_type = TREE_TYPE (d_inner); |
| |
| if (DECL_EXTERN_C_P (d_inner) != DECL_EXTERN_C_P (e_inner)) |
| goto mismatch; |
| |
| for (tree e_args = TYPE_ARG_TYPES (e_type), |
| d_args = TYPE_ARG_TYPES (d_type); |
| e_args != d_args && (e_args || d_args); |
| e_args = TREE_CHAIN (e_args), d_args = TREE_CHAIN (d_args)) |
| { |
| if (!(e_args && d_args)) |
| goto mismatch; |
| |
| if (!same_type_p (TREE_VALUE (d_args), TREE_VALUE (e_args))) |
| goto mismatch; |
| |
| // FIXME: Check default values |
| } |
| |
| /* If EXISTING has an undeduced or uninstantiated exception |
| specification, but DECL does not, propagate the exception |
| specification. Otherwise we end up asserting or trying to |
| instantiate it in the middle of loading. */ |
| tree e_spec = TYPE_RAISES_EXCEPTIONS (e_type); |
| tree d_spec = TYPE_RAISES_EXCEPTIONS (d_type); |
| if (DEFERRED_NOEXCEPT_SPEC_P (e_spec)) |
| { |
| if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec) |
| || (UNEVALUATED_NOEXCEPT_SPEC_P (e_spec) |
| && !UNEVALUATED_NOEXCEPT_SPEC_P (d_spec))) |
| { |
| dump (dumper::MERGE) |
| && dump ("Propagating instantiated noexcept to %N", existing); |
| TREE_TYPE (existing) = d_type; |
| |
| /* Propagate to existing clones. */ |
| tree clone; |
| FOR_EACH_CLONE (clone, existing) |
| { |
| if (TREE_TYPE (clone) == e_type) |
| TREE_TYPE (clone) = d_type; |
| else |
| TREE_TYPE (clone) |
| = build_exception_variant (TREE_TYPE (clone), d_spec); |
| } |
| } |
| } |
| else if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec) |
| && !comp_except_specs (d_spec, e_spec, ce_type)) |
| goto mismatch; |
| } |
| else if (is_typedef) |
| { |
| if (!DECL_ORIGINAL_TYPE (e_inner) |
| || !same_type_p (DECL_ORIGINAL_TYPE (d_inner), |
| DECL_ORIGINAL_TYPE (e_inner))) |
| goto mismatch; |
| } |
| /* Using cp_tree_equal because we can meet TYPE_ARGUMENT_PACKs |
| here. I suspect the entities that directly do that are things |
| that shouldn't go to duplicate_decls (FIELD_DECLs etc). */ |
| else if (!cp_tree_equal (TREE_TYPE (decl), TREE_TYPE (existing))) |
| { |
| mismatch: |
| if (DECL_IS_UNDECLARED_BUILTIN (existing)) |
| /* Just like duplicate_decls, presum the user knows what |
| they're doing in overriding a builtin. */ |
| TREE_TYPE (existing) = TREE_TYPE (decl); |
| else |
| { |
| // FIXME:QOI Might be template specialization from a module, |
| // not necessarily global module |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "conflicting global module declaration %#qD", decl); |
| inform (DECL_SOURCE_LOCATION (existing), |
| "existing declaration %#qD", existing); |
| return false; |
| } |
| } |
| |
| if (DECL_IS_UNDECLARED_BUILTIN (existing) |
| && !DECL_IS_UNDECLARED_BUILTIN (decl)) |
| { |
| /* We're matching a builtin that the user has yet to declare. |
| We are the one! This is very much duplicate-decl |
| shenanigans. */ |
| DECL_SOURCE_LOCATION (existing) = DECL_SOURCE_LOCATION (decl); |
| if (TREE_CODE (decl) != TYPE_DECL) |
| { |
| /* Propagate exceptions etc. */ |
| TREE_TYPE (existing) = TREE_TYPE (decl); |
| TREE_NOTHROW (existing) = TREE_NOTHROW (decl); |
| } |
| /* This is actually an import! */ |
| DECL_MODULE_IMPORT_P (existing) = true; |
| |
| /* Yay, sliced! */ |
| existing->base = decl->base; |
| |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| /* Ew :( */ |
| memcpy (&existing->decl_common.size, |
| &decl->decl_common.size, |
| (offsetof (tree_decl_common, pt_uid) |
| - offsetof (tree_decl_common, size))); |
| auto bltin_class = DECL_BUILT_IN_CLASS (decl); |
| existing->function_decl.built_in_class = bltin_class; |
| auto fncode = DECL_UNCHECKED_FUNCTION_CODE (decl); |
| DECL_UNCHECKED_FUNCTION_CODE (existing) = fncode; |
| if (existing->function_decl.built_in_class == BUILT_IN_NORMAL) |
| { |
| if (builtin_decl_explicit_p (built_in_function (fncode))) |
| switch (fncode) |
| { |
| case BUILT_IN_STPCPY: |
| set_builtin_decl_implicit_p |
| (built_in_function (fncode), true); |
| break; |
| default: |
| set_builtin_decl_declared_p |
| (built_in_function (fncode), true); |
| break; |
| } |
| copy_attributes_to_builtin (decl); |
| } |
| } |
| } |
| |
| if (VAR_OR_FUNCTION_DECL_P (decl) |
| && DECL_TEMPLATE_INSTANTIATED (decl)) |
| /* Don't instantiate again! */ |
| DECL_TEMPLATE_INSTANTIATED (existing) = true; |
| |
| if (TREE_CODE (d_inner) == FUNCTION_DECL |
| && DECL_DECLARED_INLINE_P (d_inner)) |
| DECL_DECLARED_INLINE_P (e_inner) = true; |
| if (!DECL_EXTERNAL (d_inner)) |
| DECL_EXTERNAL (e_inner) = false; |
| |
| // FIXME: Check default tmpl and fn parms here |
| |
| return true; |
| } |
| |
| /* FN is an implicit member function that we've discovered is new to |
| the class. Add it to the TYPE_FIELDS chain and the method vector. |
| Reset the appropriate classtype lazy flag. */ |
| |
| bool |
| trees_in::install_implicit_member (tree fn) |
| { |
| tree ctx = DECL_CONTEXT (fn); |
| tree name = DECL_NAME (fn); |
| /* We know these are synthesized, so the set of expected prototypes |
| is quite restricted. We're not validating correctness, just |
| distinguishing beteeen the small set of possibilities. */ |
| tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (fn)); |
| if (IDENTIFIER_CTOR_P (name)) |
| { |
| if (CLASSTYPE_LAZY_DEFAULT_CTOR (ctx) |
| && VOID_TYPE_P (parm_type)) |
| CLASSTYPE_LAZY_DEFAULT_CTOR (ctx) = false; |
| else if (!TYPE_REF_P (parm_type)) |
| return false; |
| else if (CLASSTYPE_LAZY_COPY_CTOR (ctx) |
| && !TYPE_REF_IS_RVALUE (parm_type)) |
| CLASSTYPE_LAZY_COPY_CTOR (ctx) = false; |
| else if (CLASSTYPE_LAZY_MOVE_CTOR (ctx)) |
| CLASSTYPE_LAZY_MOVE_CTOR (ctx) = false; |
| else |
| return false; |
| } |
| else if (IDENTIFIER_DTOR_P (name)) |
| { |
| if (CLASSTYPE_LAZY_DESTRUCTOR (ctx)) |
| CLASSTYPE_LAZY_DESTRUCTOR (ctx) = false; |
| else |
| return false; |
| if (DECL_VIRTUAL_P (fn)) |
| /* A virtual dtor should have been created when the class |
| became complete. */ |
| return false; |
| } |
| else if (name == assign_op_identifier) |
| { |
| if (!TYPE_REF_P (parm_type)) |
| return false; |
| else if (CLASSTYPE_LAZY_COPY_ASSIGN (ctx) |
| && !TYPE_REF_IS_RVALUE (parm_type)) |
| CLASSTYPE_LAZY_COPY_ASSIGN (ctx) = false; |
| else if (CLASSTYPE_LAZY_MOVE_ASSIGN (ctx)) |
| CLASSTYPE_LAZY_MOVE_ASSIGN (ctx) = false; |
| else |
| return false; |
| } |
| else |
| return false; |
| |
| dump (dumper::MERGE) && dump ("Adding implicit member %N", fn); |
| |
| DECL_CHAIN (fn) = TYPE_FIELDS (ctx); |
| TYPE_FIELDS (ctx) = fn; |
| |
| add_method (ctx, fn, false); |
| |
| /* Propagate TYPE_FIELDS. */ |
| fixup_type_variants (ctx); |
| |
| return true; |
| } |
| |
| /* Return non-zero if DECL has a definition that would be interesting to |
| write out. */ |
| |
| static bool |
| has_definition (tree decl) |
| { |
| bool is_tmpl = TREE_CODE (decl) == TEMPLATE_DECL; |
| if (is_tmpl) |
| decl = DECL_TEMPLATE_RESULT (decl); |
| |
| switch (TREE_CODE (decl)) |
| { |
| default: |
| break; |
| |
| case FUNCTION_DECL: |
| if (!DECL_SAVED_TREE (decl)) |
| /* Not defined. */ |
| break; |
| |
| if (DECL_DECLARED_INLINE_P (decl)) |
| return true; |
| |
| if (DECL_THIS_STATIC (decl) |
| && (header_module_p () |
| || (!DECL_LANG_SPECIFIC (decl) || !DECL_MODULE_PURVIEW_P (decl)))) |
| /* GM static function. */ |
| return true; |
| |
| if (DECL_TEMPLATE_INFO (decl)) |
| { |
| int use_tpl = DECL_USE_TEMPLATE (decl); |
| |
| // FIXME: Partial specializations have definitions too. |
| if (use_tpl < 2) |
| return true; |
| } |
| break; |
| |
| case TYPE_DECL: |
| { |
| tree type = TREE_TYPE (decl); |
| if (type == TYPE_MAIN_VARIANT (type) |
| && decl == TYPE_NAME (type) |
| && (TREE_CODE (type) == ENUMERAL_TYPE |
| ? TYPE_VALUES (type) : TYPE_FIELDS (type))) |
| return true; |
| } |
| break; |
| |
| case VAR_DECL: |
| if (DECL_LANG_SPECIFIC (decl) |
| && DECL_TEMPLATE_INFO (decl) |
| && DECL_USE_TEMPLATE (decl) < 2) |
| return DECL_INITIAL (decl); |
| else |
| { |
| if (!DECL_INITIALIZED_P (decl)) |
| return false; |
| |
| if (header_module_p () |
| || (!DECL_LANG_SPECIFIC (decl) || !DECL_MODULE_PURVIEW_P (decl))) |
| /* GM static variable. */ |
| return true; |
| |
| if (!TREE_CONSTANT (decl)) |
| return false; |
| |
| return true; |
| } |
| break; |
| |
| case CONCEPT_DECL: |
| if (DECL_INITIAL (decl)) |
| return true; |
| |
| break; |
| } |
| |
| return false; |
| } |
| |
| uintptr_t * |
| trees_in::find_duplicate (tree existing) |
| { |
| if (!duplicates) |
| return NULL; |
| |
| return duplicates->get (existing); |
| } |
| |
| /* We're starting to read a duplicate DECL. EXISTING is the already |
| known node. */ |
| |
| void |
| trees_in::register_duplicate (tree decl, tree existing) |
| { |
| if (!duplicates) |
| duplicates = new duplicate_hash_map (40); |
| |
| bool existed; |
| uintptr_t &slot = duplicates->get_or_insert (existing, &existed); |
| gcc_checking_assert (!existed); |
| slot = reinterpret_cast<uintptr_t> (decl); |
| } |
| |
| /* We've read a definition of MAYBE_EXISTING. If not a duplicate, |
| return MAYBE_EXISTING (into which the definition should be |
| installed). Otherwise return NULL if already known bad, or the |
| duplicate we read (for ODR checking, or extracting additional merge |
| information). */ |
| |
| tree |
| trees_in::odr_duplicate (tree maybe_existing, bool has_defn) |
| { |
| tree res = NULL_TREE; |
| |
| if (uintptr_t *dup = find_duplicate (maybe_existing)) |
| { |
| if (!(*dup & 1)) |
| res = reinterpret_cast<tree> (*dup); |
| } |
| else |
| res = maybe_existing; |
| |
| assert_definition (maybe_existing, res && !has_defn); |
| |
| // FIXME: We probably need to return the template, so that the |
| // template header can be checked? |
| return res ? STRIP_TEMPLATE (res) : NULL_TREE; |
| } |
| |
| /* The following writer functions rely on the current behaviour of |
| depset::hash::add_dependency making the decl and defn depset nodes |
| depend on eachother. That way we don't have to worry about seeding |
| the tree map with named decls that cannot be looked up by name (I.e |
| template and function parms). We know the decl and definition will |
| be in the same cluster, which is what we want. */ |
| |
| void |
| trees_out::write_function_def (tree decl) |
| { |
| tree_node (DECL_RESULT (decl)); |
| tree_node (DECL_INITIAL (decl)); |
| tree_node (DECL_SAVED_TREE (decl)); |
| tree_node (DECL_FRIEND_CONTEXT (decl)); |
| |
| constexpr_fundef *cexpr = retrieve_constexpr_fundef (decl); |
| int tag = 0; |
| if (cexpr) |
| { |
| if (cexpr->result == error_mark_node) |
| /* We'll stream the RESULT_DECL naturally during the |
| serialization. We never need to fish it back again, so |
| that's ok. */ |
| tag = 0; |
| else |
| tag = insert (cexpr->result); |
| } |
| if (streaming_p ()) |
| { |
| i (tag); |
| if (tag) |
| dump (dumper::TREE) |
| && dump ("Constexpr:%d result %N", tag, cexpr->result); |
| } |
| if (tag) |
| { |
| unsigned ix = 0; |
| for (tree parm = cexpr->parms; parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| tag = insert (parm); |
| if (streaming_p ()) |
| dump (dumper::TREE) |
| && dump ("Constexpr:%d parm:%u %N", tag, ix, parm); |
| } |
| tree_node (cexpr->body); |
| } |
| |
| if (streaming_p ()) |
| { |
| unsigned flags = 0; |
| |
| if (DECL_NOT_REALLY_EXTERN (decl)) |
| flags |= 1; |
| |
| u (flags); |
| } |
| } |
| |
| void |
| trees_out::mark_function_def (tree) |
| { |
| } |
| |
| bool |
| trees_in::read_function_def (tree decl, tree maybe_template) |
| { |
| dump () && dump ("Reading function definition %N", decl); |
| tree result = tree_node (); |
| tree initial = tree_node (); |
| tree saved = tree_node (); |
| tree context = tree_node (); |
| constexpr_fundef cexpr; |
| |
| tree maybe_dup = odr_duplicate (maybe_template, DECL_SAVED_TREE (decl)); |
| bool installing = maybe_dup && !DECL_SAVED_TREE (decl); |
| |
| if (int wtag = i ()) |
| { |
| int tag = 1; |
| cexpr.result = error_mark_node; |
| |
| cexpr.result = copy_decl (result); |
| tag = insert (cexpr.result); |
| |
| if (wtag != tag) |
| set_overrun (); |
| dump (dumper::TREE) |
| && dump ("Constexpr:%d result %N", tag, cexpr.result); |
| |
| cexpr.parms = NULL_TREE; |
| tree *chain = &cexpr.parms; |
| unsigned ix = 0; |
| for (tree parm = DECL_ARGUMENTS (maybe_dup ? maybe_dup : decl); |
| parm; parm = DECL_CHAIN (parm), ix++) |
| { |
| tree p = copy_decl (parm); |
| tag = insert (p); |
| dump (dumper::TREE) |
| && dump ("Constexpr:%d parm:%u %N", tag, ix, p); |
| *chain = p; |
| chain = &DECL_CHAIN (p); |
| } |
| cexpr.body = tree_node (); |
| cexpr.decl = decl; |
| } |
| else |
| cexpr.decl = NULL_TREE; |
| |
| unsigned flags = u (); |
| |
| if (get_overrun ()) |
| return NULL_TREE; |
| |
| if (installing) |
| { |
| DECL_NOT_REALLY_EXTERN (decl) = flags & 1; |
| DECL_RESULT (decl) = result; |
| DECL_INITIAL (decl) = initial; |
| DECL_SAVED_TREE (decl) = saved; |
| if (maybe_dup) |
| DECL_ARGUMENTS (decl) = DECL_ARGUMENTS (maybe_dup); |
| |
| if (context) |
| SET_DECL_FRIEND_CONTEXT (decl, context); |
| if (cexpr.decl) |
| register_constexpr_fundef (cexpr); |
| post_process (maybe_template); |
| } |
| else if (maybe_dup) |
| { |
| // FIXME:QOI Check matching defn |
| } |
| |
| return true; |
| } |
| |
| /* Also for CONCEPT_DECLs. */ |
| |
| void |
| trees_out::write_var_def (tree decl) |
| { |
| tree init = DECL_INITIAL (decl); |
| tree_node (init); |
| if (!init) |
| { |
| tree dyn_init = NULL_TREE; |
| |
| if (DECL_NONTRIVIALLY_INITIALIZED_P (decl)) |
| { |
| dyn_init = value_member (decl, |
| CP_DECL_THREAD_LOCAL_P (decl) |
| ? tls_aggregates : static_aggregates); |
| gcc_checking_assert (dyn_init); |
| /* Mark it so write_inits knows this is needed. */ |
| TREE_LANG_FLAG_0 (dyn_init) = true; |
| dyn_init = TREE_PURPOSE (dyn_init); |
| } |
| tree_node (dyn_init); |
| } |
| } |
| |
| void |
| trees_out::mark_var_def (tree) |
| { |
| } |
| |
| bool |
| trees_in::read_var_def (tree decl, tree maybe_template) |
| { |
| /* Do not mark the virtual table entries as used. */ |
| bool vtable = TREE_CODE (decl) == VAR_DECL && DECL_VTABLE_OR_VTT_P (decl); |
| unused += vtable; |
| tree init = tree_node (); |
| tree dyn_init = init ? NULL_TREE : tree_node (); |
| unused -= vtable; |
| |
| if (get_overrun ()) |
| return false; |
| |
| bool initialized = (VAR_P (decl) ? bool (DECL_INITIALIZED_P (decl)) |
| : bool (DECL_INITIAL (decl))); |
| tree maybe_dup = odr_duplicate (maybe_template, initialized); |
| bool installing = maybe_dup && !initialized; |
| if (installing) |
| { |
| if (DECL_EXTERNAL (decl)) |
| DECL_NOT_REALLY_EXTERN (decl) = true; |
| if (VAR_P (decl)) |
| { |
| DECL_INITIALIZED_P (decl) = true; |
| if (maybe_dup && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (maybe_dup)) |
| DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; |
| } |
| DECL_INITIAL (decl) = init; |
| if (!dyn_init) |
| ; |
| else if (CP_DECL_THREAD_LOCAL_P (decl)) |
| tls_aggregates = tree_cons (dyn_init, decl, tls_aggregates); |
| else |
| static_aggregates = tree_cons (dyn_init, decl, static_aggregates); |
| } |
| else if (maybe_dup) |
| { |
| // FIXME:QOI Check matching defn |
| } |
| |
| return true; |
| } |
| |
| /* If MEMBER doesn't have an independent life outside the class, |
| return it (or it's TEMPLATE_DECL). Otherwise NULL. */ |
| |
| static tree |
| member_owned_by_class (tree member) |
| { |
| gcc_assert (DECL_P (member)); |
| |
| /* Clones are owned by their origin. */ |
| if (DECL_CLONED_FUNCTION_P (member)) |
| return NULL; |
| |
| if (TREE_CODE (member) == FIELD_DECL) |
| /* FIELD_DECLS can have template info in some cases. We always |
| want the FIELD_DECL though, as there's never a TEMPLATE_DECL |
| wrapping them. */ |
| return member; |
| |
| int use_tpl = -1; |
| if (tree ti = node_template_info (member, use_tpl)) |
| { |
| // FIXME: Don't bail on things that CANNOT have their own |
| // template header. No, make sure they're in the same cluster. |
| if (use_tpl > 0) |
| return NULL_TREE; |
| |
| if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == member) |
| member = TI_TEMPLATE (ti); |
| } |
| return member; |
| } |
| |
| void |
| trees_out::write_class_def (tree defn) |
| { |
| gcc_assert (DECL_P (defn)); |
| if (streaming_p ()) |
| dump () && dump ("Writing class definition %N", defn); |
| |
| tree type = TREE_TYPE (defn); |
| tree_node (TYPE_SIZE (type)); |
| tree_node (TYPE_SIZE_UNIT (type)); |
| tree_node (TYPE_VFIELD (type)); |
| tree_node (TYPE_BINFO (type)); |
| |
| vec_chained_decls (TYPE_FIELDS (type)); |
| |
| /* Every class but __as_base has a type-specific. */ |
| gcc_checking_assert (!TYPE_LANG_SPECIFIC (type) == IS_FAKE_BASE_TYPE (type)); |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| { |
| vec<tree, va_gc> *v = CLASSTYPE_MEMBER_VEC (type); |
| if (!v) |
| { |
| gcc_checking_assert (!streaming_p ()); |
| /* Force a class vector. */ |
| v = set_class_bindings (type, -1); |
| gcc_checking_assert (v); |
| } |
| |
| unsigned len = v->length (); |
| if (streaming_p ()) |
| u (len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree m = (*v)[ix]; |
| if (TREE_CODE (m) == TYPE_DECL |
| && DECL_ARTIFICIAL (m) |
| && TYPE_STUB_DECL (TREE_TYPE (m)) == m) |
| /* This is a using-decl for a type, or an anonymous |
| struct (maybe with a typedef name). Write the type. */ |
| m = TREE_TYPE (m); |
| tree_node (m); |
| } |
| } |
| tree_node (CLASSTYPE_LAMBDA_EXPR (type)); |
| |
| /* TYPE_CONTAINS_VPTR_P looks at the vbase vector, which the |
| reader won't know at this point. */ |
| int has_vptr = TYPE_CONTAINS_VPTR_P (type); |
| |
| if (streaming_p ()) |
| { |
| unsigned nvbases = vec_safe_length (CLASSTYPE_VBASECLASSES (type)); |
| u (nvbases); |
| i (has_vptr); |
| } |
| |
| if (has_vptr) |
| { |
| tree_vec (CLASSTYPE_PURE_VIRTUALS (type)); |
| tree_pair_vec (CLASSTYPE_VCALL_INDICES (type)); |
| tree_node (CLASSTYPE_KEY_METHOD (type)); |
| } |
| } |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| tree_node (CLASSTYPE_PRIMARY_BINFO (type)); |
| |
| tree as_base = CLASSTYPE_AS_BASE (type); |
| if (as_base) |
| as_base = TYPE_NAME (as_base); |
| tree_node (as_base); |
| |
| /* Write the vtables. */ |
| tree vtables = CLASSTYPE_VTABLES (type); |
| vec_chained_decls (vtables); |
| for (; vtables; vtables = TREE_CHAIN (vtables)) |
| write_definition (vtables); |
| |
| /* Write the friend classes. */ |
| tree_list (CLASSTYPE_FRIEND_CLASSES (type), false); |
| |
| /* Write the friend functions. */ |
| for (tree friends = DECL_FRIENDLIST (defn); |
| friends; friends = TREE_CHAIN (friends)) |
| { |
| /* Name of these friends. */ |
| tree_node (TREE_PURPOSE (friends)); |
| tree_list (TREE_VALUE (friends), false); |
| } |
| /* End of friend fns. */ |
| tree_node (NULL_TREE); |
| |
| /* Write the decl list. */ |
| tree_list (CLASSTYPE_DECL_LIST (type), true); |
| |
| if (TYPE_CONTAINS_VPTR_P (type)) |
| { |
| /* Write the thunks. */ |
| for (tree decls = TYPE_FIELDS (type); |
| decls; decls = DECL_CHAIN (decls)) |
| if (TREE_CODE (decls) == FUNCTION_DECL |
| && DECL_VIRTUAL_P (decls) |
| && DECL_THUNKS (decls)) |
| { |
| tree_node (decls); |
| /* Thunks are always unique, so chaining is ok. */ |
| chained_decls (DECL_THUNKS (decls)); |
| } |
| tree_node (NULL_TREE); |
| } |
| } |
| } |
| |
| void |
| trees_out::mark_class_member (tree member, bool do_defn) |
| { |
| gcc_assert (DECL_P (member)); |
| |
| member = member_owned_by_class (member); |
| if (member) |
| mark_declaration (member, do_defn && has_definition (member)); |
| } |
| |
| void |
| trees_out::mark_class_def (tree defn) |
| { |
| gcc_assert (DECL_P (defn)); |
| tree type = TREE_TYPE (defn); |
| /* Mark the class members that are not type-decls and cannot have |
| independent definitions. */ |
| for (tree member = TYPE_FIELDS (type); member; member = DECL_CHAIN (member)) |
| if (TREE_CODE (member) == FIELD_DECL |
| || TREE_CODE (member) == USING_DECL |
| /* A cloned enum-decl from 'using enum unrelated;' */ |
| || (TREE_CODE (member) == CONST_DECL |
| && DECL_CONTEXT (member) == type)) |
| { |
| mark_class_member (member); |
| if (TREE_CODE (member) == FIELD_DECL) |
| if (tree repr = DECL_BIT_FIELD_REPRESENTATIVE (member)) |
| mark_declaration (repr, false); |
| } |
| |
| /* Mark the binfo hierarchy. */ |
| for (tree child = TYPE_BINFO (type); child; child = TREE_CHAIN (child)) |
| mark_by_value (child); |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| for (tree vtable = CLASSTYPE_VTABLES (type); |
| vtable; vtable = TREE_CHAIN (vtable)) |
| mark_declaration (vtable, true); |
| |
| if (TYPE_CONTAINS_VPTR_P (type)) |
| /* Mark the thunks, they belong to the class definition, |
| /not/ the thunked-to function. */ |
| for (tree decls = TYPE_FIELDS (type); |
| decls; decls = DECL_CHAIN (decls)) |
| if (TREE_CODE (decls) == FUNCTION_DECL) |
| for (tree thunks = DECL_THUNKS (decls); |
| thunks; thunks = DECL_CHAIN (thunks)) |
| mark_declaration (thunks, false); |
| } |
| } |
| |
| /* Nop sorting, needed for resorting the member vec. */ |
| |
| static void |
| nop (void *, void *) |
| { |
| } |
| |
| bool |
| trees_in::read_class_def (tree defn, tree maybe_template) |
| { |
| gcc_assert (DECL_P (defn)); |
| dump () && dump ("Reading class definition %N", defn); |
| tree type = TREE_TYPE (defn); |
| tree size = tree_node (); |
| tree size_unit = tree_node (); |
| tree vfield = tree_node (); |
| tree binfo = tree_node (); |
| vec<tree, va_gc> *vbase_vec = NULL; |
| vec<tree, va_gc> *member_vec = NULL; |
| vec<tree, va_gc> *pure_virts = NULL; |
| vec<tree_pair_s, va_gc> *vcall_indices = NULL; |
| tree key_method = NULL_TREE; |
| tree lambda = NULL_TREE; |
| |
| /* Read the fields. */ |
| vec<tree, va_heap> *fields = vec_chained_decls (); |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| if (unsigned len = u ()) |
| { |
| vec_alloc (member_vec, len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree m = tree_node (); |
| if (get_overrun ()) |
| break; |
| if (TYPE_P (m)) |
| m = TYPE_STUB_DECL (m); |
| member_vec->quick_push (m); |
| } |
| } |
| lambda = tree_node (); |
| |
| if (!get_overrun ()) |
| { |
| unsigned nvbases = u (); |
| if (nvbases) |
| { |
| vec_alloc (vbase_vec, nvbases); |
| for (tree child = binfo; child; child = TREE_CHAIN (child)) |
| if (BINFO_VIRTUAL_P (child)) |
| vbase_vec->quick_push (child); |
| } |
| } |
| |
| if (!get_overrun ()) |
| { |
| int has_vptr = i (); |
| if (has_vptr) |
| { |
| pure_virts = tree_vec (); |
| vcall_indices = tree_pair_vec (); |
| key_method = tree_node (); |
| } |
| } |
| } |
| |
| tree maybe_dup = odr_duplicate (maybe_template, TYPE_SIZE (type)); |
| bool installing = maybe_dup && !TYPE_SIZE (type); |
| if (installing) |
| { |
| if (DECL_EXTERNAL (defn) && TYPE_LANG_SPECIFIC (type)) |
| { |
| /* We don't deal with not-really-extern, because, for a |
| module you want the import to be the interface, and for a |
| header-unit, you're doing it wrong. */ |
| CLASSTYPE_INTERFACE_UNKNOWN (type) = false; |
| CLASSTYPE_INTERFACE_ONLY (type) = true; |
| } |
| |
| if (maybe_dup != defn) |
| { |
| // FIXME: This is needed on other defns too, almost |
| // duplicate-decl like? See is_matching_decl too. |
| /* Copy flags from the duplicate. */ |
| tree type_dup = TREE_TYPE (maybe_dup); |
| |
| /* Core pieces. */ |
| TYPE_MODE_RAW (type) = TYPE_MODE_RAW (type_dup); |
| SET_DECL_MODE (defn, DECL_MODE (maybe_dup)); |
| TREE_ADDRESSABLE (type) = TREE_ADDRESSABLE (type_dup); |
| DECL_SIZE (defn) = DECL_SIZE (maybe_dup); |
| DECL_SIZE_UNIT (defn) = DECL_SIZE_UNIT (maybe_dup); |
| DECL_ALIGN_RAW (defn) = DECL_ALIGN_RAW (maybe_dup); |
| DECL_WARN_IF_NOT_ALIGN_RAW (defn) |
| = DECL_WARN_IF_NOT_ALIGN_RAW (maybe_dup); |
| DECL_USER_ALIGN (defn) = DECL_USER_ALIGN (maybe_dup); |
| |
| /* C++ pieces. */ |
| TYPE_POLYMORPHIC_P (type) = TYPE_POLYMORPHIC_P (type_dup); |
| TYPE_HAS_USER_CONSTRUCTOR (type) |
| = TYPE_HAS_USER_CONSTRUCTOR (type_dup); |
| TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) |
| = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type_dup); |
| |
| if (auto ls = TYPE_LANG_SPECIFIC (type_dup)) |
| { |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| CLASSTYPE_BEFRIENDING_CLASSES (type_dup) |
| = CLASSTYPE_BEFRIENDING_CLASSES (type); |
| if (!ANON_AGGR_TYPE_P (type)) |
| CLASSTYPE_TYPEINFO_VAR (type_dup) |
| = CLASSTYPE_TYPEINFO_VAR (type); |
| } |
| for (tree v = type; v; v = TYPE_NEXT_VARIANT (v)) |
| TYPE_LANG_SPECIFIC (v) = ls; |
| } |
| } |
| |
| TYPE_SIZE (type) = size; |
| TYPE_SIZE_UNIT (type) = size_unit; |
| |
| if (fields) |
| { |
| tree *chain = &TYPE_FIELDS (type); |
| unsigned len = fields->length (); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree decl = (*fields)[ix]; |
| |
| if (!decl) |
| { |
| /* An anonymous struct with typedef name. */ |
| tree tdef = (*fields)[ix+1]; |
| decl = TYPE_STUB_DECL (TREE_TYPE (tdef)); |
| gcc_checking_assert (IDENTIFIER_ANON_P (DECL_NAME (decl)) |
| && decl != tdef); |
| } |
| |
| gcc_checking_assert (!*chain == !DECL_CLONED_FUNCTION_P (decl)); |
| *chain = decl; |
| chain = &DECL_CHAIN (decl); |
| |
| if (TREE_CODE (decl) == FIELD_DECL |
| && ANON_AGGR_TYPE_P (TREE_TYPE (decl))) |
| ANON_AGGR_TYPE_FIELD |
| (TYPE_MAIN_VARIANT (TREE_TYPE (decl))) = decl; |
| |
| if (TREE_CODE (decl) == USING_DECL |
| && TREE_CODE (USING_DECL_SCOPE (decl)) == RECORD_TYPE) |
| { |
| /* Reconstruct DECL_ACCESS. */ |
| tree decls = USING_DECL_DECLS (decl); |
| tree access = declared_access (decl); |
| |
| for (ovl_iterator iter (decls); iter; ++iter) |
| { |
| tree d = *iter; |
| |
| retrofit_lang_decl (d); |
| tree list = DECL_ACCESS (d); |
| |
| if (!purpose_member (type, list)) |
| DECL_ACCESS (d) = tree_cons (type, access, list); |
| } |
| } |
| } |
| } |
| |
| TYPE_VFIELD (type) = vfield; |
| TYPE_BINFO (type) = binfo; |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| CLASSTYPE_LAMBDA_EXPR (type) = lambda; |
| |
| CLASSTYPE_MEMBER_VEC (type) = member_vec; |
| CLASSTYPE_PURE_VIRTUALS (type) = pure_virts; |
| CLASSTYPE_VCALL_INDICES (type) = vcall_indices; |
| |
| CLASSTYPE_KEY_METHOD (type) = key_method; |
| |
| CLASSTYPE_VBASECLASSES (type) = vbase_vec; |
| |
| /* Resort the member vector. */ |
| resort_type_member_vec (member_vec, NULL, nop, NULL); |
| } |
| } |
| else if (maybe_dup) |
| { |
| // FIXME:QOI Check matching defn |
| } |
| |
| if (TYPE_LANG_SPECIFIC (type)) |
| { |
| tree primary = tree_node (); |
| tree as_base = tree_node (); |
| |
| if (as_base) |
| as_base = TREE_TYPE (as_base); |
| |
| /* Read the vtables. */ |
| vec<tree, va_heap> *vtables = vec_chained_decls (); |
| if (vtables) |
| { |
| unsigned len = vtables->length (); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree vtable = (*vtables)[ix]; |
| read_var_def (vtable, vtable); |
| } |
| } |
| |
| tree friend_classes = tree_list (false); |
| tree friend_functions = NULL_TREE; |
| for (tree *chain = &friend_functions; |
| tree name = tree_node (); chain = &TREE_CHAIN (*chain)) |
| { |
| tree val = tree_list (false); |
| *chain = build_tree_list (name, val); |
| } |
| tree decl_list = tree_list (true); |
| |
| if (installing) |
| { |
| CLASSTYPE_PRIMARY_BINFO (type) = primary; |
| CLASSTYPE_AS_BASE (type) = as_base; |
| |
| if (vtables) |
| { |
| if (!CLASSTYPE_KEY_METHOD (type) |
| /* Sneaky user may have defined it inline |
| out-of-class. */ |
| || DECL_DECLARED_INLINE_P (CLASSTYPE_KEY_METHOD (type))) |
| vec_safe_push (keyed_classes, type); |
| unsigned len = vtables->length (); |
| tree *chain = &CLASSTYPE_VTABLES (type); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| tree vtable = (*vtables)[ix]; |
| gcc_checking_assert (!*chain); |
| *chain = vtable; |
| chain = &DECL_CHAIN (vtable); |
| } |
| } |
| CLASSTYPE_FRIEND_CLASSES (type) = friend_classes; |
| DECL_FRIENDLIST (defn) = friend_functions; |
| CLASSTYPE_DECL_LIST (type) = decl_list; |
| |
| for (; friend_classes; friend_classes = TREE_CHAIN (friend_classes)) |
| { |
| tree f = TREE_VALUE (friend_classes); |
| |
| if (TYPE_P (f)) |
| { |
| CLASSTYPE_BEFRIENDING_CLASSES (f) |
| = tree_cons (NULL_TREE, type, |
| CLASSTYPE_BEFRIENDING_CLASSES (f)); |
| dump () && dump ("Class %N befriending %C:%N", |
| type, TREE_CODE (f), f); |
| } |
| } |
| |
| for (; friend_functions; |
| friend_functions = TREE_CHAIN (friend_functions)) |
| for (tree friend_decls = TREE_VALUE (friend_functions); |
| friend_decls; friend_decls = TREE_CHAIN (friend_decls)) |
| { |
| tree f = TREE_VALUE (friend_decls); |
| |
| DECL_BEFRIENDING_CLASSES (f) |
| = tree_cons (NULL_TREE, type, DECL_BEFRIENDING_CLASSES (f)); |
| dump () && dump ("Class %N befriending %C:%N", |
| type, TREE_CODE (f), f); |
| } |
| } |
| |
| if (TYPE_CONTAINS_VPTR_P (type)) |
| /* Read and install the thunks. */ |
| while (tree vfunc = tree_node ()) |
| { |
| tree thunks = chained_decls (); |
| if (installing) |
| SET_DECL_THUNKS (vfunc, thunks); |
| } |
| |
| vec_free (vtables); |
| } |
| |
| /* Propagate to all variants. */ |
| if (installing) |
| fixup_type_variants (type); |
| |
| /* IS_FAKE_BASE_TYPE is inaccurate at this point, because if this is |
| the fake base, we've not hooked it into the containing class's |
| data structure yet. Fortunately it has a unique name. */ |
| if (installing |
| && DECL_NAME (defn) != as_base_identifier |
| && (!CLASSTYPE_TEMPLATE_INFO (type) |
| || !uses_template_parms (TI_ARGS (CLASSTYPE_TEMPLATE_INFO (type))))) |
| /* Emit debug info. It'd be nice to know if the interface TU |
| already emitted this. */ |
| rest_of_type_compilation (type, !LOCAL_CLASS_P (type)); |
| |
| vec_free (fields); |
| |
| return !get_overrun (); |
| } |
| |
| void |
| trees_out::write_enum_def (tree decl) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| tree_node (TYPE_VALUES (type)); |
| tree_node (TYPE_MIN_VALUE (type)); |
| tree_node (TYPE_MAX_VALUE (type)); |
| } |
| |
| void |
| trees_out::mark_enum_def (tree decl) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| for (tree values = TYPE_VALUES (type); values; values = TREE_CHAIN (values)) |
| { |
| tree cst = TREE_VALUE (values); |
| mark_by_value (cst); |
| /* We must mark the init to avoid circularity in tt_enum_int. */ |
| if (tree init = DECL_INITIAL (cst)) |
| if (TREE_CODE (init) == INTEGER_CST) |
| mark_by_value (init); |
| } |
| } |
| |
| bool |
| trees_in::read_enum_def (tree defn, tree maybe_template) |
| { |
| tree type = TREE_TYPE (defn); |
| tree values = tree_node (); |
| tree min = tree_node (); |
| tree max = tree_node (); |
| |
| if (get_overrun ()) |
| return false; |
| |
| tree maybe_dup = odr_duplicate (maybe_template, TYPE_VALUES (type)); |
| bool installing = maybe_dup && !TYPE_VALUES (type); |
| |
| if (installing) |
| { |
| TYPE_VALUES (type) = values; |
| TYPE_MIN_VALUE (type) = min; |
| TYPE_MAX_VALUE (type) = max; |
| |
| rest_of_type_compilation (type, DECL_NAMESPACE_SCOPE_P (defn)); |
| } |
| else if (maybe_dup) |
| { |
| tree known = TYPE_VALUES (type); |
| for (; known && values; |
| known = TREE_CHAIN (known), values = TREE_CHAIN (values)) |
| { |
| tree known_decl = TREE_VALUE (known); |
| tree new_decl = TREE_VALUE (values); |
| |
| if (DECL_NAME (known_decl) != DECL_NAME (new_decl)) |
| goto bad; |
| |
| new_decl = maybe_duplicate (new_decl); |
| |
| if (!cp_tree_equal (DECL_INITIAL (known_decl), |
| DECL_INITIAL (new_decl))) |
| goto bad; |
| } |
| |
| if (known || values) |
| goto bad; |
| |
| if (!cp_tree_equal (TYPE_MIN_VALUE (type), min) |
| || !cp_tree_equal (TYPE_MAX_VALUE (type), max)) |
| { |
| bad:; |
| error_at (DECL_SOURCE_LOCATION (maybe_dup), |
| "definition of %qD does not match", maybe_dup); |
| inform (DECL_SOURCE_LOCATION (defn), |
| "existing definition %qD", defn); |
| |
| tree known_decl = NULL_TREE, new_decl = NULL_TREE; |
| |
| if (known) |
| known_decl = TREE_VALUE (known); |
| if (values) |
| new_decl = maybe_duplicate (TREE_VALUE (values)); |
| |
| if (known_decl && new_decl) |
| { |
| inform (DECL_SOURCE_LOCATION (new_decl), |
| "... this enumerator %qD", new_decl); |
| inform (DECL_SOURCE_LOCATION (known_decl), |
| "enumerator %qD does not match ...", known_decl); |
| } |
| else if (known_decl || new_decl) |
| { |
| tree extra = known_decl ? known_decl : new_decl; |
| inform (DECL_SOURCE_LOCATION (extra), |
| "additional enumerators beginning with %qD", extra); |
| } |
| else |
| inform (DECL_SOURCE_LOCATION (maybe_dup), |
| "enumeration range differs"); |
| |
| /* Mark it bad. */ |
| unmatched_duplicate (maybe_template); |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Write out the body of DECL. See above circularity note. */ |
| |
| void |
| trees_out::write_definition (tree decl) |
| { |
| if (streaming_p ()) |
| { |
| assert_definition (decl); |
| dump () |
| && dump ("Writing definition %C:%N", TREE_CODE (decl), decl); |
| } |
| else |
| dump (dumper::DEPEND) |
| && dump ("Depending definition %C:%N", TREE_CODE (decl), decl); |
| |
| again: |
| switch (TREE_CODE (decl)) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case TEMPLATE_DECL: |
| decl = DECL_TEMPLATE_RESULT (decl); |
| goto again; |
| |
| case FUNCTION_DECL: |
| write_function_def (decl); |
| break; |
| |
| case TYPE_DECL: |
| { |
| tree type = TREE_TYPE (decl); |
| gcc_assert (TYPE_MAIN_VARIANT (type) == type |
| && TYPE_NAME (type) == decl); |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| write_enum_def (decl); |
| else |
| write_class_def (decl); |
| } |
| break; |
| |
| case VAR_DECL: |
| case CONCEPT_DECL: |
| write_var_def (decl); |
| break; |
| } |
| } |
| |
| /* Mark a declaration for by-value walking. If DO_DEFN is true, mark |
| its body too. */ |
| |
| void |
| trees_out::mark_declaration (tree decl, bool do_defn) |
| { |
| mark_by_value (decl); |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| decl = DECL_TEMPLATE_RESULT (decl); |
| |
| if (!do_defn) |
| return; |
| |
| switch (TREE_CODE (decl)) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case FUNCTION_DECL: |
| mark_function_def (decl); |
| break; |
| |
| case TYPE_DECL: |
| { |
| tree type = TREE_TYPE (decl); |
| gcc_assert (TYPE_MAIN_VARIANT (type) == type |
| && TYPE_NAME (type) == decl); |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| mark_enum_def (decl); |
| else |
| mark_class_def (decl); |
| } |
| break; |
| |
| case VAR_DECL: |
| case CONCEPT_DECL: |
| mark_var_def (decl); |
| break; |
| } |
| } |
| |
| /* Read in the body of DECL. See above circularity note. */ |
| |
| bool |
| trees_in::read_definition (tree decl) |
| { |
| dump () && dump ("Reading definition %C %N", TREE_CODE (decl), decl); |
| |
| tree maybe_template = decl; |
| |
| again: |
| switch (TREE_CODE (decl)) |
| { |
| default: |
| break; |
| |
| case TEMPLATE_DECL: |
| decl = DECL_TEMPLATE_RESULT (decl); |
| goto again; |
| |
| case FUNCTION_DECL: |
| return read_function_def (decl, maybe_template); |
| |
| case TYPE_DECL: |
| { |
| tree type = TREE_TYPE (decl); |
| gcc_assert (TYPE_MAIN_VARIANT (type) == type |
| && TYPE_NAME (type) == decl); |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| return read_enum_def (decl, maybe_template); |
| else |
| return read_class_def (decl, maybe_template); |
| } |
| break; |
| |
| case VAR_DECL: |
| case CONCEPT_DECL: |
| return read_var_def (decl, maybe_template); |
| } |
| |
| return false; |
| } |
| |
| /* Lookup an maybe insert a slot for depset for KEY. */ |
| |
| depset ** |
| depset::hash::entity_slot (tree entity, bool insert) |
| { |
| traits::compare_type key (entity, NULL); |
| depset **slot = find_slot_with_hash (key, traits::hash (key), |
| insert ? INSERT : NO_INSERT); |
| |
| return slot; |
| } |
| |
| depset ** |
| depset::hash::binding_slot (tree ctx, tree name, bool insert) |
| { |
| traits::compare_type key (ctx, name); |
| depset **slot = find_slot_with_hash (key, traits::hash (key), |
| insert ? INSERT : NO_INSERT); |
| |
| return slot; |
| } |
| |
| depset * |
| depset::hash::find_dependency (tree decl) |
| { |
| depset **slot = entity_slot (decl, false); |
| |
| return slot ? *slot : NULL; |
| } |
| |
| depset * |
| depset::hash::find_binding (tree ctx, tree name) |
| { |
| depset **slot = binding_slot (ctx, name, false); |
| |
| return slot ? *slot : NULL; |
| } |
| |
| /* DECL is a newly discovered dependency. Create the depset, if it |
| doesn't already exist. Add it to the worklist if so. |
| |
| DECL will be an OVL_USING_P OVERLOAD, if it's from a binding that's |
| a using decl. |
| |
| We do not have to worry about adding the same dependency more than |
| once. First it's harmless, but secondly the TREE_VISITED marking |
| prevents us wanting to do it anyway. */ |
| |
| depset * |
| depset::hash::make_dependency (tree decl, entity_kind ek) |
| { |
| /* Make sure we're being told consistent information. */ |
| gcc_checking_assert ((ek == EK_NAMESPACE) |
| == (TREE_CODE (decl) == NAMESPACE_DECL |
| && !DECL_NAMESPACE_ALIAS (decl))); |
| gcc_checking_assert (ek != EK_BINDING && ek != EK_REDIRECT); |
| gcc_checking_assert (TREE_CODE (decl) != FIELD_DECL |
| && (TREE_CODE (decl) != USING_DECL |
| || TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)); |
| gcc_checking_assert (!is_key_order ()); |
| if (ek == EK_USING) |
| gcc_checking_assert (TREE_CODE (decl) == OVERLOAD); |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| /* The template should have copied these from its result decl. */ |
| gcc_checking_assert (DECL_MODULE_EXPORT_P (decl) |
| == DECL_MODULE_EXPORT_P (DECL_TEMPLATE_RESULT (decl))); |
| |
| depset **slot = entity_slot (decl, true); |
| depset *dep = *slot; |
| bool for_binding = ek == EK_FOR_BINDING; |
| |
| if (!dep) |
| { |
| if (DECL_IMPLICIT_TYPEDEF_P (decl) |
| /* ... not an enum, for instance. */ |
| && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) |
| && TYPE_LANG_SPECIFIC (TREE_TYPE (decl)) |
| && CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl)) == 2) |
| { |
| /* A partial or explicit specialization. Partial |
| specializations might not be in the hash table, because |
| there can be multiple differently-constrained variants. |
| |
| template<typename T> class silly; |
| template<typename T> requires true class silly {}; |
| |
| We need to find them, insert their TEMPLATE_DECL in the |
| dep_hash, and then convert the dep we just found into a |
| redirect. */ |
| |
| tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)); |
| tree tmpl = TI_TEMPLATE (ti); |
| tree partial = NULL_TREE; |
| for (tree spec = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); |
| spec; spec = TREE_CHAIN (spec)) |
| if (DECL_TEMPLATE_RESULT (TREE_VALUE (spec)) == decl) |
| { |
| partial = TREE_VALUE (spec); |
| break; |
| } |
| |
| if (partial) |
| { |
| /* Eagerly create an empty redirect. The following |
| make_dependency call could cause hash reallocation, |
| and invalidate slot's value. */ |
| depset *redirect = make_entity (decl, EK_REDIRECT); |
| |
| /* Redirects are never reached -- always snap to their target. */ |
| redirect->set_flag_bit<DB_UNREACHED_BIT> (); |
| |
| *slot = redirect; |
| |
| depset *tmpl_dep = make_dependency (partial, EK_PARTIAL); |
| gcc_checking_assert (tmpl_dep->get_entity_kind () == EK_PARTIAL); |
| |
| redirect->deps.safe_push (tmpl_dep); |
| |
| return redirect; |
| } |
| } |
| |
| bool has_def = ek != EK_USING && has_definition (decl); |
| if (ek > EK_BINDING) |
| ek = EK_DECL; |
| |
| /* The only OVERLOADS we should see are USING decls from |
| bindings. */ |
| *slot = dep = make_entity (decl, ek, has_def); |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| if (DECL_ALIAS_TEMPLATE_P (decl) && DECL_TEMPLATE_INFO (decl)) |
| dep->set_flag_bit<DB_ALIAS_TMPL_INST_BIT> (); |
| else if (CHECKING_P) |
| /* The template_result should otherwise not be in the |
| table, or be an empty redirect (created above). */ |
| if (auto *eslot = entity_slot (DECL_TEMPLATE_RESULT (decl), false)) |
| gcc_checking_assert ((*eslot)->get_entity_kind () == EK_REDIRECT |
| && !(*eslot)->deps.length ()); |
| } |
| |
| if (ek != EK_USING) |
| { |
| tree not_tmpl = STRIP_TEMPLATE (decl); |
| |
| if (DECL_LANG_SPECIFIC (not_tmpl) |
| && DECL_MODULE_IMPORT_P (not_tmpl)) |
| { |
| /* Store the module number and index in cluster/section, |
| so we don't have to look them up again. */ |
| unsigned index = import_entity_index (decl); |
| module_state *from = import_entity_module (index); |
| /* Remap will be zero for imports from partitions, which |
| we want to treat as-if declared in this TU. */ |
| if (from->remap) |
| { |
| dep->cluster = index - from->entity_lwm; |
| dep->section = from->remap; |
| dep->set_flag_bit<DB_IMPORTED_BIT> (); |
| } |
| } |
| |
| if (ek == EK_DECL |
| && !dep->is_import () |
| && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL |
| && !(TREE_CODE (decl) == TEMPLATE_DECL |
| && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))) |
| { |
| tree ctx = CP_DECL_CONTEXT (decl); |
| |
| if (!TREE_PUBLIC (ctx)) |
| /* Member of internal namespace. */ |
| dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); |
| else if (VAR_OR_FUNCTION_DECL_P (not_tmpl) |
| && DECL_THIS_STATIC (not_tmpl)) |
| { |
| /* An internal decl. This is ok in a GM entity. */ |
| if (!(header_module_p () |
| || !DECL_LANG_SPECIFIC (not_tmpl) |
| || !DECL_MODULE_PURVIEW_P (not_tmpl))) |
| dep->set_flag_bit<DB_IS_INTERNAL_BIT> (); |
| } |
| } |
| } |
| |
| if (!dep->is_import ()) |
| worklist.safe_push (dep); |
| } |
| |
| dump (dumper::DEPEND) |
| && dump ("%s on %s %C:%N found", |
| ek == EK_REDIRECT ? "Redirect" |
| : for_binding ? "Binding" : "Dependency", |
| dep->entity_kind_name (), TREE_CODE (decl), decl); |
| |
| return dep; |
| } |
| |
| /* DEP is a newly discovered dependency. Append it to current's |
| depset. */ |
| |
| void |
| depset::hash::add_dependency (depset *dep) |
| { |
| gcc_checking_assert (current && !is_key_order ()); |
| current->deps.safe_push (dep); |
| |
| if (dep->is_internal () && !current->is_internal ()) |
| current->set_flag_bit<DB_REFS_INTERNAL_BIT> (); |
| |
| if (current->get_entity_kind () == EK_USING |
| && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) |
| && TREE_CODE (TREE_TYPE (dep->get_entity ())) == ENUMERAL_TYPE) |
| { |
| /* CURRENT is an unwrapped using-decl and DECL is an enum's |
| implicit typedef. Is CURRENT a member of the enum? */ |
| tree c_decl = OVL_FUNCTION (current->get_entity ()); |
| |
| if (TREE_CODE (c_decl) == CONST_DECL |
| && (current->deps[0]->get_entity () |
| == CP_DECL_CONTEXT (dep->get_entity ()))) |
| /* Make DECL depend on CURRENT. */ |
| dep->deps.safe_push (current); |
| } |
| |
| if (dep->is_unreached ()) |
| { |
| /* The dependency is reachable now. */ |
| reached_unreached = true; |
| dep->clear_flag_bit<DB_UNREACHED_BIT> (); |
| dump (dumper::DEPEND) |
| && dump ("Reaching unreached %s %C:%N", dep->entity_kind_name (), |
| TREE_CODE (dep->get_entity ()), dep->get_entity ()); |
| } |
| } |
| |
| depset * |
| depset::hash::add_dependency (tree decl, entity_kind ek) |
| { |
| depset *dep; |
| |
| if (is_key_order ()) |
| { |
| dep = find_dependency (decl); |
| if (dep) |
| { |
| current->deps.safe_push (dep); |
| dump (dumper::MERGE) |
| && dump ("Key dependency on %s %C:%N found", |
| dep->entity_kind_name (), TREE_CODE (decl), decl); |
| } |
| else |
| { |
| /* It's not a mergeable decl, look for it in the original |
| table. */ |
| dep = chain->find_dependency (decl); |
| gcc_checking_assert (dep); |
| } |
| } |
| else |
| { |
| dep = make_dependency (decl, ek); |
| if (dep->get_entity_kind () != EK_REDIRECT) |
| add_dependency (dep); |
| } |
| |
| return dep; |
| } |
| |
| void |
| depset::hash::add_namespace_context (depset *dep, tree ns) |
| { |
| depset *ns_dep = make_dependency (ns, depset::EK_NAMESPACE); |
| dep->deps.safe_push (ns_dep); |
| |
| /* Mark it as special if imported so we don't walk connect when |
| SCCing. */ |
| if (!dep->is_binding () && ns_dep->is_import ()) |
| dep->set_special (); |
| } |
| |
| struct add_binding_data |
| { |
| tree ns; |
| bitmap partitions; |
| depset *binding; |
| depset::hash *hash; |
| bool met_namespace; |
| }; |
| |
| /* Return true if we are, or contain something that is exported. */ |
| |
| bool |
| depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_) |
| { |
| auto data = static_cast <add_binding_data *> (data_); |
| |
| if (!(TREE_CODE (decl) == NAMESPACE_DECL && !DECL_NAMESPACE_ALIAS (decl))) |
| { |
| tree inner = decl; |
| |
| if (TREE_CODE (inner) == CONST_DECL |
| && TREE_CODE (DECL_CONTEXT (inner)) == ENUMERAL_TYPE) |
| inner = TYPE_NAME (DECL_CONTEXT (inner)); |
| else if (TREE_CODE (inner) == TEMPLATE_DECL) |
| inner = DECL_TEMPLATE_RESULT (inner); |
| |
| if (!DECL_LANG_SPECIFIC (inner) || !DECL_MODULE_PURVIEW_P (inner)) |
| /* Ignore global module fragment entities. */ |
| return false; |
| |
| if (VAR_OR_FUNCTION_DECL_P (inner) |
| && DECL_THIS_STATIC (inner)) |
| { |
| if (!header_module_p ()) |
| /* Ignore internal-linkage entitites. */ |
| return false; |
| } |
| |
| if ((TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == TYPE_DECL) |
| && DECL_TINFO_P (decl)) |
| /* Ignore TINFO things. */ |
| return false; |
| |
| if (!(flags & WMB_Using) && CP_DECL_CONTEXT (decl) != data->ns) |
| { |
| /* A using that lost its wrapper or an unscoped enum |
| constant. */ |
| flags = WMB_Flags (flags | WMB_Using); |
| if (DECL_MODULE_EXPORT_P (TREE_CODE (decl) == CONST_DECL |
| ? TYPE_NAME (TREE_TYPE (decl)) |
| : STRIP_TEMPLATE (decl))) |
| flags = WMB_Flags (flags | WMB_Export); |
| } |
| |
| if (!data->binding) |
| /* No binding to check. */; |
| else if (flags & WMB_Using) |
| { |
| /* Look in the binding to see if we already have this |
| using. */ |
| for (unsigned ix = data->binding->deps.length (); --ix;) |
| { |
| depset *d = data->binding->deps[ix]; |
| if (d->get_entity_kind () == EK_USING |
| && OVL_FUNCTION (d->get_entity ()) == decl) |
| { |
| if (!(flags & WMB_Hidden)) |
| d->clear_hidden_binding (); |
| if (flags & WMB_Export) |
| OVL_EXPORT_P (d->get_entity ()) = true; |
| return bool (flags & WMB_Export); |
| } |
| } |
| } |
| else if (flags & WMB_Dups) |
| { |
| /* Look in the binding to see if we already have this decl. */ |
| for (unsigned ix = data->binding->deps.length (); --ix;) |
| { |
| depset *d = data->binding->deps[ix]; |
| if (d->get_entity () == decl) |
| { |
| if (!(flags & WMB_Hidden)) |
| d->clear_hidden_binding (); |
| return false; |
| } |
| } |
| } |
| |
| /* We're adding something. */ |
| if (!data->binding) |
| { |
| data->binding = make_binding (data->ns, DECL_NAME (decl)); |
| data->hash->add_namespace_context (data->binding, data->ns); |
| |
| depset **slot = data->hash->binding_slot (data->ns, |
| DECL_NAME (decl), true); |
| gcc_checking_assert (!*slot); |
| *slot = data->binding; |
| } |
| |
| /* Make sure nobody left a tree visited lying about. */ |
| gcc_checking_assert (!TREE_VISITED (decl)); |
| |
| if (flags & WMB_Using) |
| { |
| decl = ovl_make (decl, NULL_TREE); |
| if (flags & WMB_Export) |
| OVL_EXPORT_P (decl) = true; |
| } |
| |
| depset *dep = data->hash->make_dependency |
| (decl, flags & WMB_Using ? EK_USING : EK_FOR_BINDING); |
| if (flags & WMB_Hidden) |
| dep->set_hidden_binding (); |
| data->binding->deps.safe_push (dep); |
| /* Binding and contents are mutually dependent. */ |
| dep->deps.safe_push (data->binding); |
| |
| return (flags & WMB_Using |
| ? flags & WMB_Export : DECL_MODULE_EXPORT_P (decl)); |
| } |
| else if (DECL_NAME (decl) && !data->met_namespace) |
| { |
| /* Namespace, walk exactly once. */ |
| gcc_checking_assert (TREE_PUBLIC (decl)); |
| data->met_namespace = true; |
| if (data->hash->add_namespace_entities (decl, data->partitions)) |
| { |
| /* It contains an exported thing, so it is exported. */ |
| gcc_checking_assert (DECL_MODULE_PURVIEW_P (decl)); |
| DECL_MODULE_EXPORT_P (decl) = true; |
| } |
| |
| if (DECL_MODULE_PURVIEW_P (decl)) |
| { |
| data->hash->make_dependency (decl, depset::EK_NAMESPACE); |
| |
| return DECL_MODULE_EXPORT_P (decl); |
| } |
| } |
| |
| return false; |
| } |
| |
| /* Recursively find all the namespace bindings of NS. Add a depset |
| for every binding that contains an export or module-linkage entity. |
| Add a defining depset for every such decl that we need to write a |
| definition. Such defining depsets depend on the binding depset. |
| Returns true if we contain something exported. */ |
| |
| bool |
| depset::hash::add_namespace_entities (tree ns, bitmap partitions) |
| { |
| dump () && dump ("Looking for writables in %N", ns); |
| dump.indent (); |
| |
| unsigned count = 0; |
| add_binding_data data; |
| data.ns = ns; |
| data.partitions = partitions; |
| data.hash = this; |
| |
| hash_table<named_decl_hash>::iterator end |
| (DECL_NAMESPACE_BINDINGS (ns)->end ()); |
| for (hash_table<named_decl_hash>::iterator iter |
| (DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter) |
| { |
| data.binding = nullptr; |
| data.met_namespace = false; |
| if (walk_module_binding (*iter, partitions, add_binding_entity, &data)) |
| count++; |
| } |
| |
| if (count) |
| dump () && dump ("Found %u entries", count); |
| dump.outdent (); |
| |
| return count != 0; |
| } |
| |
| void |
| depset::hash::add_partial_entities (vec<tree, va_gc> *partial_classes) |
| { |
| for (unsigned ix = 0; ix != partial_classes->length (); ix++) |
| { |
| tree inner = (*partial_classes)[ix]; |
| |
| depset *dep = make_dependency (inner, depset::EK_DECL); |
| |
| if (dep->get_entity_kind () == depset::EK_REDIRECT) |
| /* We should have recorded the template as a partial |
| specialization. */ |
| gcc_checking_assert (dep->deps[0]->get_entity_kind () |
| == depset::EK_PARTIAL); |
| else |
| /* It was an explicit specialization, not a partial one. */ |
| gcc_checking_assert (dep->get_entity_kind () |
| == depset::EK_SPECIALIZATION); |
| } |
| } |
| |
| /* Add the members of imported classes that we defined in this TU. |
| This will also include lazily created implicit member function |
| declarations. (All others will be definitions.) */ |
| |
| void |
| depset::hash::add_class_entities (vec<tree, va_gc> *class_members) |
| { |
| for (unsigned ix = 0; ix != class_members->length (); ix++) |
| { |
| tree defn = (*class_members)[ix]; |
| depset *dep = make_dependency (defn, EK_INNER_DECL); |
| |
| if (dep->get_entity_kind () == EK_REDIRECT) |
| dep = dep->deps[0]; |
| |
| /* Only non-instantiations need marking as members. */ |
| if (dep->get_entity_kind () == EK_DECL) |
| dep->set_flag_bit <DB_IS_MEMBER_BIT> (); |
| } |
| } |
| |
| /* We add the partial & explicit specializations, and the explicit |
| instantiations. */ |
| |
| static void |
| specialization_add (bool decl_p, spec_entry *entry, void *data_) |
| { |
| vec<spec_entry *> *data = reinterpret_cast <vec<spec_entry *> *> (data_); |
| |
| if (!decl_p) |
| { |
| /* We exclusively use decls to locate things. Make sure there's |
| no mismatch between the two specialization tables we keep. |
| pt.c optimizes instantiation lookup using a complicated |
| heuristic. We don't attempt to replicate that algorithm, but |
| observe its behaviour and reproduce it upon read back. */ |
| |
| gcc_checking_assert (DECL_ALIAS_TEMPLATE_P (entry->tmpl) |
| || TREE_CODE (entry->spec) == ENUMERAL_TYPE |
| || DECL_CLASS_TEMPLATE_P (entry->tmpl)); |
| |
| /* Only alias templates can appear in both tables (and |
| if they're in the type table they must also be in the decl |
| table). */ |
| gcc_checking_assert |
| (!match_mergeable_specialization (true, entry) |
| == !DECL_ALIAS_TEMPLATE_P (entry->tmpl)); |
| } |
| else if (VAR_OR_FUNCTION_DECL_P (entry->spec)) |
| gcc_checking_assert (!DECL_LOCAL_DECL_P (entry->spec)); |
| |
| data->safe_push (entry); |
| } |
| |
| /* Arbitrary stable comparison. */ |
| |
| static int |
| specialization_cmp (const void *a_, const void *b_) |
| { |
| const spec_entry *ea = *reinterpret_cast<const spec_entry *const *> (a_); |
| const spec_entry *eb = *reinterpret_cast<const spec_entry *const *> (b_); |
| |
| if (ea == eb) |
| return 0; |
| |
| tree a = ea->spec; |
| tree b = eb->spec; |
| if (TYPE_P (a)) |
| { |
| a = TYPE_NAME (a); |
| b = TYPE_NAME (b); |
| } |
| |
| if (a == b) |
| /* This can happen with friend specializations. Just order by |
| entry address. See note in depset_cmp. */ |
| return ea < eb ? -1 : +1; |
| |
| return DECL_UID (a) < DECL_UID (b) ? -1 : +1; |
| } |
| |
| /* We add all kinds of specialializations. Implicit specializations |
| should only streamed and walked if they are reachable from |
| elsewhere. Hence the UNREACHED flag. This is making the |
| assumption that it is cheaper to reinstantiate them on demand |
| elsewhere, rather than stream them in when we instantiate their |
| general template. Also, if we do stream them, we can only do that |
| if they are not internal (which they can become if they themselves |
| touch an internal entity?). */ |
| |
| void |
| depset::hash::add_specializations (bool decl_p) |
| { |
| vec<spec_entry *> data; |
| data.create (100); |
| walk_specializations (decl_p, specialization_add, &data); |
| data.qsort (specialization_cmp); |
| while (data.length ()) |
| { |
| spec_entry *entry = data.pop (); |
| tree spec = entry->spec; |
| int use_tpl = 0; |
| bool is_alias = false; |
| bool is_friend = false; |
| |
| if (decl_p && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl)) |
| /* A friend of a template. This is keyed to the |
| instantiation. */ |
| is_friend = true; |
| |
| if (!decl_p && DECL_ALIAS_TEMPLATE_P (entry->tmpl)) |
| { |
| spec = TYPE_NAME (spec); |
| is_alias = true; |
| } |
| |
| if (decl_p || is_alias) |
| { |
| if (tree ti = DECL_TEMPLATE_INFO (spec)) |
| { |
| tree tmpl = TI_TEMPLATE (ti); |
| |
| use_tpl = DECL_USE_TEMPLATE (spec); |
| if (spec == DECL_TEMPLATE_RESULT (tmpl)) |
| { |
| spec = tmpl; |
| gcc_checking_assert (DECL_USE_TEMPLATE (spec) == use_tpl); |
| } |
| else if (is_friend) |
| { |
| if (TI_TEMPLATE (ti) != entry->tmpl |
| || !template_args_equal (TI_ARGS (ti), entry->tmpl)) |
| goto template_friend; |
| } |
| } |
| else |
| { |
| template_friend:; |
| gcc_checking_assert (is_friend); |
| /* This is a friend of a template class, but not the one |
| that generated entry->spec itself (i.e. it's an |
| equivalent clone). We do not need to record |
| this. */ |
| continue; |
| } |
| } |
| else |
| { |
| if (TREE_CODE (spec) == ENUMERAL_TYPE) |
| { |
| tree ctx = DECL_CONTEXT (TYPE_NAME (spec)); |
| |
| if (TYPE_P (ctx)) |
| use_tpl = CLASSTYPE_USE_TEMPLATE (ctx); |
| else |
| use_tpl = DECL_USE_TEMPLATE (ctx); |
| } |
| else |
| use_tpl = CLASSTYPE_USE_TEMPLATE (spec); |
| |
| tree ti = TYPE_TEMPLATE_INFO (spec); |
| tree tmpl = TI_TEMPLATE (ti); |
| |
| spec = TYPE_NAME (spec); |
| if (spec == DECL_TEMPLATE_RESULT (tmpl)) |
| { |
| spec = tmpl; |
| use_tpl = DECL_USE_TEMPLATE (spec); |
| } |
| } |
| |
| bool needs_reaching = false; |
| if (use_tpl == 1) |
| /* Implicit instantiations only walked if we reach them. */ |
| needs_reaching = true; |
| else if (!DECL_LANG_SPECIFIC (spec) |
| || !DECL_MODULE_PURVIEW_P (spec)) |
| /* Likewise, GMF explicit or partial specializations. */ |
| needs_reaching = true; |
| |
| #if false && CHECKING_P |
| /* The instantiation isn't always on |
| DECL_TEMPLATE_INSTANTIATIONS, */ |
| // FIXME: we probably need to remember this information? |
| /* Verify the specialization is on the |
| DECL_TEMPLATE_INSTANTIATIONS of the template. */ |
| for (tree cons = DECL_TEMPLATE_INSTANTIATIONS (entry->tmpl); |
| cons; cons = TREE_CHAIN (cons)) |
| if (TREE_VALUE (cons) == entry->spec) |
| { |
| gcc_assert (entry->args == TREE_PURPOSE (cons)); |
| goto have_spec; |
| } |
| gcc_unreachable (); |
| have_spec:; |
| #endif |
| |
| /* Make sure nobody left a tree visited lying about. */ |
| gcc_checking_assert (!TREE_VISITED (spec)); |
| depset *dep = make_dependency (spec, depset::EK_SPECIALIZATION); |
| if (dep->is_special ()) |
| { |
| /* An already located specialization, this must be the TYPE |
| corresponding to an alias_decl we found in the decl |
| table. */ |
| spec_entry *other = reinterpret_cast <spec_entry *> (dep->deps[0]); |
| gcc_checking_assert (!decl_p && is_alias && !dep->is_type_spec ()); |
| gcc_checking_assert (other->tmpl == entry->tmpl |
| && template_args_equal (other->args, entry->args) |
| && TREE_TYPE (other->spec) == entry->spec); |
| dep->set_flag_bit<DB_ALIAS_SPEC_BIT> (); |
| } |
| else |
| { |
| gcc_checking_assert (decl_p || !is_alias); |
| if (dep->get_entity_kind () == depset::EK_REDIRECT) |
| dep = dep->deps[0]; |
| else if (dep->get_entity_kind () == depset::EK_SPECIALIZATION) |
| { |
| dep->set_special (); |
| dep->deps.safe_push (reinterpret_cast<depset *> (entry)); |
| if (!decl_p) |
| dep->set_flag_bit<DB_TYPE_SPEC_BIT> (); |
| } |
| |
| if (needs_reaching) |
| dep->set_flag_bit<DB_UNREACHED_BIT> (); |
| if (is_friend) |
| dep->set_flag_bit<DB_FRIEND_SPEC_BIT> (); |
| } |
| } |
| data.release (); |
| } |
| |
| /* Add a depset into the mergeable hash. */ |
| |
| void |
| depset::hash::add_mergeable (depset *mergeable) |
| { |
| gcc_checking_assert (is_key_order ()); |
| entity_kind ek = mergeable->get_entity_kind (); |
| tree decl = mergeable->get_entity (); |
| gcc_checking_assert (ek < EK_DIRECT_HWM); |
| |
| depset **slot = entity_slot (decl, true); |
| gcc_checking_assert (!*slot); |
| depset *dep = make_entity (decl, ek); |
| *slot = dep; |
| |
| worklist.safe_push (dep); |
| |
| /* So we can locate the mergeable depset this depset refers to, |
| mark the first dep. */ |
| dep->set_special (); |
| dep->deps.safe_push (mergeable); |
| } |
| |
| /* Find the innermost-namespace scope of DECL, and that |
| namespace-scope decl. */ |
| |
| tree |
| find_pending_key (tree decl, tree *decl_p = nullptr) |
| { |
| tree ns = decl; |
| do |
| { |
| decl = ns; |
| ns = CP_DECL_CONTEXT (ns); |
| if (TYPE_P (ns)) |
| ns = TYPE_NAME (ns); |
| } |
| while (TREE_CODE (ns) != NAMESPACE_DECL); |
| |
| if (decl_p) |
| *decl_p = decl; |
| |
| return ns; |
| } |
| |
| /* Iteratively find dependencies. During the walk we may find more |
| entries on the same binding that need walking. */ |
| |
| void |
| depset::hash::find_dependencies (module_state *module) |
| { |
| trees_out walker (NULL, module, *this); |
| vec<depset *> unreached; |
| unreached.create (worklist.length ()); |
| |
| for (;;) |
| { |
| reached_unreached = false; |
| while (worklist.length ()) |
| { |
| depset *item = worklist.pop (); |
| |
| gcc_checking_assert (!item->is_binding ()); |
| if (item->is_unreached ()) |
| unreached.quick_push (item); |
| else |
| { |
| current = item; |
| tree decl = current->get_entity (); |
| dump (is_key_order () ? dumper::MERGE : dumper::DEPEND) |
| && dump ("Dependencies of %s %C:%N", |
| is_key_order () ? "key-order" |
| : current->entity_kind_name (), TREE_CODE (decl), decl); |
| dump.indent (); |
| walker.begin (); |
| if (current->get_entity_kind () == EK_USING) |
| walker.tree_node (OVL_FUNCTION (decl)); |
| else if (TREE_VISITED (decl)) |
| /* A global tree. */; |
| else if (item->get_entity_kind () == EK_NAMESPACE) |
| add_namespace_context (current, CP_DECL_CONTEXT (decl)); |
| else |
| { |
| walker.mark_declaration (decl, current->has_defn ()); |
| |
| if (!walker.is_key_order () |
| && (item->get_entity_kind () == EK_SPECIALIZATION |
| || item->get_entity_kind () == EK_PARTIAL |
| || (item->get_entity_kind () == EK_DECL |
| && item->is_member ()))) |
| { |
| tree ns = find_pending_key (decl, nullptr); |
| add_namespace_context (item, ns); |
| } |
| |
| // FIXME: Perhaps p1815 makes this redundant? Or at |
| // least simplifies it. Voldemort types are only |
| // ever emissable when containing (inline) function |
| // definition is emitted? |
| /* Turn the Sneakoscope on when depending the decl. */ |
| sneakoscope = true; |
| walker.decl_value (decl, current); |
| sneakoscope = false; |
| if (current->has_defn ()) |
| walker.write_definition (decl); |
| } |
| walker.end (); |
| |
| if (!walker.is_key_order () |
| && TREE_CODE (decl) == TEMPLATE_DECL |
| && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) |
| /* Mark all the explicit & partial specializations as |
| reachable. */ |
| for (tree cons = DECL_TEMPLATE_INSTANTIATIONS (decl); |
| cons; cons = TREE_CHAIN (cons)) |
| { |
| tree spec = TREE_VALUE (cons); |
| if (TYPE_P (spec)) |
| spec = TYPE_NAME (spec); |
| int use_tpl; |
| node_template_info (spec, use_tpl); |
| if (use_tpl & 2) |
| { |
| depset *spec_dep = find_dependency (spec); |
| if (spec_dep->get_entity_kind () == EK_REDIRECT) |
| spec_dep = spec_dep->deps[0]; |
| if (spec_dep->is_unreached ()) |
| { |
| reached_unreached = true; |
| spec_dep->clear_flag_bit<DB_UNREACHED_BIT> (); |
| dump (dumper::DEPEND) |
| && dump ("Reaching unreached specialization" |
| " %C:%N", TREE_CODE (spec), spec); |
| } |
| } |
| } |
| |
| dump.outdent (); |
| current = NULL; |
| } |
| } |
| |
| if (!reached_unreached) |
| break; |
| |
| /* It's possible the we reached the unreached before we |
| processed it in the above loop, so we'll be doing this an |
| extra time. However, to avoid that we have to do some |
| bit shuffling that also involves a scan of the list. |
| Swings & roundabouts I guess. */ |
| std::swap (worklist, unreached); |
| } |
| |
| unreached.release (); |
| } |
| |
| /* Compare two entries of a single binding. TYPE_DECL before |
| non-exported before exported. */ |
| |
| static int |
| binding_cmp (const void *a_, const void *b_) |
| { |
| depset *a = *(depset *const *)a_; |
| depset *b = *(depset *const *)b_; |
| |
| tree a_ent = a->get_entity (); |
| tree b_ent = b->get_entity (); |
| gcc_checking_assert (a_ent != b_ent |
| && !a->is_binding () |
| && !b->is_binding ()); |
| |
| /* Implicit typedefs come first. */ |
| bool a_implicit = DECL_IMPLICIT_TYPEDEF_P (a_ent); |
| bool b_implicit = DECL_IMPLICIT_TYPEDEF_P (b_ent); |
| if (a_implicit || b_implicit) |
| { |
| /* A binding with two implicit type decls? That's unpossible! */ |
| gcc_checking_assert (!(a_implicit && b_implicit)); |
| return a_implicit ? -1 : +1; /* Implicit first. */ |
| } |
| |
| /* Hidden before non-hidden. */ |
| bool a_hidden = a->is_hidden (); |
| bool b_hidden = b->is_hidden (); |
| if (a_hidden != b_hidden) |
| return a_hidden ? -1 : +1; |
| |
| bool a_using = a->get_entity_kind () == depset::EK_USING; |
| bool a_export; |
| if (a_using) |
| { |
| a_export = OVL_EXPORT_P (a_ent); |
| a_ent = OVL_FUNCTION (a_ent); |
| } |
| else |
| a_export = DECL_MODULE_EXPORT_P (TREE_CODE (a_ent) == CONST_DECL |
| ? TYPE_NAME (TREE_TYPE (a_ent)) |
| : STRIP_TEMPLATE (a_ent)); |
| |
| bool b_using = b->get_entity_kind () == depset::EK_USING; |
| bool b_export; |
| if (b_using) |
| { |
| b_export = OVL_EXPORT_P (b_ent); |
| b_ent = OVL_FUNCTION (b_ent); |
| } |
| else |
| b_export = DECL_MODULE_EXPORT_P (TREE_CODE (b_ent) == CONST_DECL |
| ? TYPE_NAME (TREE_TYPE (b_ent)) |
| : STRIP_TEMPLATE (b_ent)); |
| |
| /* Non-exports before exports. */ |
| if (a_export != b_export) |
| return a_export ? +1 : -1; |
| |
| /* At this point we don't care, but want a stable sort. */ |
| |
| if (a_using != b_using) |
| /* using first. */ |
| return a_using? -1 : +1; |
| |
| return DECL_UID (a_ent) < DECL_UID (b_ent) ? -1 : +1; |
| } |
| |
| /* Sort the bindings, issue errors about bad internal refs. */ |
| |
| bool |
| depset::hash::finalize_dependencies () |
| { |
| bool ok = true; |
| depset::hash::iterator end (this->end ()); |
| for (depset::hash::iterator iter (begin ()); iter != end; ++iter) |
| { |
| depset *dep = *iter; |
| if (dep->is_binding ()) |
| { |
| /* Keep the containing namespace dep first. */ |
| gcc_checking_assert (dep->deps.length () > 1 |
| && (dep->deps[0]->get_entity_kind () |
| == EK_NAMESPACE) |
| && (dep->deps[0]->get_entity () |
| == dep->get_entity ())); |
| if (dep->deps.length () > 2) |
| gcc_qsort (&dep->deps[1], dep->deps.length () - 1, |
| sizeof (dep->deps[1]), binding_cmp); |
| } |
| else if (dep->refs_internal ()) |
| { |
| for (unsigned ix = dep->deps.length (); ix--;) |
| { |
| depset *rdep = dep->deps[ix]; |
| if (rdep->is_internal ()) |
| { |
| // FIXME:QOI Better location information? We're |
| // losing, so it doesn't matter about efficiency |
| tree decl = dep->get_entity (); |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "%q#D references internal linkage entity %q#D", |
| decl, rdep->get_entity ()); |
| break; |
| } |
| } |
| ok = false; |
| } |
| } |
| |
| return ok; |
| } |
| |
| /* Core of TARJAN's algorithm to find Strongly Connected Components |
| within a graph. See https://en.wikipedia.org/wiki/ |
| Tarjan%27s_strongly_connected_components_algorithm for details. |
| |
| We use depset::section as lowlink. Completed nodes have |
| depset::cluster containing the cluster number, with the top |
| bit set. |
| |
| A useful property is that the output vector is a reverse |
| topological sort of the resulting DAG. In our case that means |
| dependent SCCs are found before their dependers. We make use of |
| that property. */ |
| |
| void |
| depset::tarjan::connect (depset *v) |
| { |
| gcc_checking_assert (v->is_binding () |
| || !(v->is_unreached () || v->is_import ())); |
| |
| v->cluster = v->section = ++index; |
| stack.safe_push (v); |
| |
| /* Walk all our dependencies, ignore a first marked slot */ |
| for (unsigned ix = v->is_special (); ix != v->deps.length (); ix++) |
| { |
| depset *dep = v->deps[ix]; |
| |
| if (dep->is_binding () || !dep->is_import ()) |
| { |
| unsigned lwm = dep->cluster; |
| |
| if (!dep->cluster) |
| { |
| /* A new node. Connect it. */ |
| connect (dep); |
| lwm = dep->section; |
| } |
| |
| if (dep->section && v->section > lwm) |
| v->section = lwm; |
| } |
| } |
| |
| if (v->section == v->cluster) |
| { |
| /* Root of a new SCC. Push all the members onto the result list. */ |
| unsigned num = v->cluster; |
| depset *p; |
| do |
| { |
| p = stack.pop (); |
| p->cluster = num; |
| p->section = 0; |
| result.quick_push (p); |
| } |
| while (p != v); |
| } |
| } |
| |
| /* Compare two depsets. The specific ordering is unimportant, we're |
| just trying to get consistency. */ |
| |
| static int |
| depset_cmp (const void *a_, const void *b_) |
| { |
| depset *a = *(depset *const *)a_; |
| depset *b = *(depset *const *)b_; |
| |
| depset::entity_kind a_kind = a->get_entity_kind (); |
| depset::entity_kind b_kind = b->get_entity_kind (); |
| |
| if (a_kind != b_kind) |
| /* Different entity kinds, order by that. */ |
| return a_kind < b_kind ? -1 : +1; |
| |
| tree a_decl = a->get_entity (); |
| tree b_decl = b->get_entity (); |
| if (a_kind == depset::EK_USING) |
| { |
| /* If one is a using, the other must be too. */ |
| a_decl = OVL_FUNCTION (a_decl); |
| b_decl = OVL_FUNCTION (b_decl); |
| } |
| |
| if (a_decl != b_decl) |
| /* Different entities, order by their UID. */ |
| return DECL_UID (a_decl) < DECL_UID (b_decl) ? -1 : +1; |
| |
| if (a_kind == depset::EK_BINDING) |
| { |
| /* Both are bindings. Order by identifier hash. */ |
| gcc_checking_assert (a->get_name () != b->get_name ()); |
| return (IDENTIFIER_HASH_VALUE (a->get_name ()) |
| < IDENTIFIER_HASH_VALUE (b->get_name ()) |
| ? -1 : +1); |
| } |
| |
| /* They are the same decl. This can happen with two using decls |
| pointing to the same target. The best we can aim for is |
| consistently telling qsort how to order them. Hopefully we'll |
| never have to debug a case that depends on this. Oh, who am I |
| kidding? Good luck. */ |
| gcc_checking_assert (a_kind == depset::EK_USING); |
| |
| /* Order by depset address. Not the best, but it is something. */ |
| return a < b ? -1 : +1; |
| } |
| |
| /* Sort the clusters in SCC such that those that depend on one another |
| are placed later. */ |
| |
| // FIXME: I am not convinced this is needed and, if needed, |
| // sufficient. We emit the decls in this order but that emission |
| // could walk into later decls (from the body of the decl, or default |
| // arg-like things). Why doesn't that walk do the right thing? And |
| // if it DTRT why do we need to sort here -- won't things naturally |
| // work? I think part of the issue is that when we're going to refer |
| // to an entity by name, and that entity is in the same cluster as us, |
| // we need to actually walk that entity, if we've not already walked |
| // it. |
| static void |
| sort_cluster (depset::hash *original, depset *scc[], unsigned size) |
| { |
| depset::hash table (size, original); |
| |
| dump.indent (); |
| |
| /* Place bindings last, usings before that. It's not strictly |
| necessary, but it does make things neater. Says Mr OCD. */ |
| unsigned bind_lwm = size; |
| unsigned use_lwm = size; |
| for (unsigned ix = 0; ix != use_lwm;) |
| { |
| depset *dep = scc[ix]; |
| switch (dep->get_entity_kind ()) |
| { |
| case depset::EK_BINDING: |
| /* Move to end. No increment. Notice this could be moving |
| a using decl, which we'll then move again. */ |
| if (--bind_lwm != ix) |
| { |
| scc[ix] = scc[bind_lwm]; |
| scc[bind_lwm] = dep; |
| } |
| if (use_lwm > bind_lwm) |
| { |
| use_lwm--; |
| break; |
| } |
| /* We must have copied a using, so move it too. */ |
| dep = scc[ix]; |
| gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING); |
| /* FALLTHROUGH */ |
| |
| case depset::EK_USING: |
| if (--use_lwm != ix) |
| { |
| scc[ix] = scc[use_lwm]; |
| scc[use_lwm] = dep; |
| } |
| break; |
| |
| case depset::EK_DECL: |
| case depset::EK_SPECIALIZATION: |
| case depset::EK_PARTIAL: |
| table.add_mergeable (dep); |
| ix++; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| gcc_checking_assert (use_lwm <= bind_lwm); |
| dump (dumper::MERGE) && dump ("Ordering %u/%u depsets", use_lwm, size); |
| |
| table.find_dependencies (nullptr); |
| |
| vec<depset *> order = table.connect (); |
| gcc_checking_assert (order.length () == use_lwm); |
| |
| /* Now rewrite entries [0,lwm), in the dependency order we |
| discovered. Usually each entity is in its own cluster. Rarely, |
| we can get multi-entity clusters, in which case all but one must |
| only be reached from within the cluster. This happens for |
| something like: |
| |
| template<typename T> |
| auto Foo (const T &arg) -> TPL<decltype (arg)>; |
| |
| The instantiation of TPL will be in the specialization table, and |
| refer to Foo via arg. But we can only get to that specialization |
| from Foo's declaration, so we only need to treat Foo as mergable |
| (We'll do structural comparison of TPL<decltype (arg)>). |
| |
| Finding the single cluster entry dep is very tricky and |
| expensive. Let's just not do that. It's harmless in this case |
| anyway. */ |
| unsigned pos = 0; |
| unsigned cluster = ~0u; |
| for (unsigned ix = 0; ix != order.length (); ix++) |
| { |
| gcc_checking_assert (order[ix]->is_special ()); |
| depset *dep = order[ix]->deps[0]; |
| scc[pos++] = dep; |
| dump (dumper::MERGE) |
| && dump ("Mergeable %u is %N%s", ix, dep->get_entity (), |
| order[ix]->cluster == cluster ? " (tight)" : ""); |
| cluster = order[ix]->cluster; |
| } |
| |
| gcc_checking_assert (pos == use_lwm); |
| |
| order.release (); |
| dump (dumper::MERGE) && dump ("Ordered %u keys", pos); |
| dump.outdent (); |
| } |
| |
| /* Reduce graph to SCCS clusters. SCCS will be populated with the |
| depsets in dependency order. Each depset's CLUSTER field contains |
| its cluster number. Each SCC has a unique cluster number, and are |
| contiguous in SCCS. Cluster numbers are otherwise arbitrary. */ |
| |
| vec<depset *> |
| depset::hash::connect () |
| { |
| tarjan connector (size ()); |
| vec<depset *> deps; |
| deps.create (size ()); |
| iterator end (this->end ()); |
| for (iterator iter (begin ()); iter != end; ++iter) |
| { |
| depset *item = *iter; |
| |
| entity_kind kind = item->get_entity_kind (); |
| if (kind == EK_BINDING |
| || !(kind == EK_REDIRECT |
| || item->is_unreached () |
| || item->is_import ())) |
| deps.quick_push (item); |
| } |
| |
| /* Iteration over the hash table is an unspecified ordering. While |
| that has advantages, it causes 2 problems. Firstly repeatable |
| builds are tricky. Secondly creating testcases that check |
| dependencies are correct by making sure a bad ordering would |
| happen if that was wrong. */ |
| deps.qsort (depset_cmp); |
| |
| while (deps.length ()) |
| { |
| depset *v = deps.pop (); |
| dump (dumper::CLUSTER) && |
| (v->is_binding () |
| ? dump ("Connecting binding %P", v->get_entity (), v->get_name ()) |
| : dump ("Connecting %s %s %C:%N", |
| is_key_order () ? "key-order" |
| : !v->has_defn () ? "declaration" : "definition", |
| v->entity_kind_name (), TREE_CODE (v->get_entity ()), |
| v->get_entity ())); |
| if (!v->cluster) |
| connector.connect (v); |
| } |
| |
| deps.release (); |
| return connector.result; |
| } |
| |
| /* Initialize location spans. */ |
| |
| void |
| loc_spans::init (const line_maps *lmaps, const line_map_ordinary *map) |
| { |
| gcc_checking_assert (!init_p ()); |
| spans = new vec<span> (); |
| spans->reserve (20); |
| |
| span interval; |
| interval.ordinary.first = 0; |
| interval.macro.second = MAX_LOCATION_T + 1; |
| interval.ordinary_delta = interval.macro_delta = 0; |
| |
| /* A span for reserved fixed locs. */ |
| interval.ordinary.second |
| = MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0)); |
| interval.macro.first = interval.macro.second; |
| dump (dumper::LOCATION) |
| && dump ("Fixed span %u ordinary:[%u,%u) macro:[%u,%u)", spans->length (), |
| interval.ordinary.first, interval.ordinary.second, |
| interval.macro.first, interval.macro.second); |
| spans->quick_push (interval); |
| |
| /* A span for command line & forced headers. */ |
| interval.ordinary.first = interval.ordinary.second; |
| interval.macro.second = interval.macro.first; |
| if (map) |
| { |
| interval.ordinary.second = map->start_location; |
| interval.macro.first = LINEMAPS_MACRO_LOWEST_LOCATION (lmaps); |
| } |
| dump (dumper::LOCATION) |
| && dump ("Pre span %u ordinary:[%u,%u) macro:[%u,%u)", spans->length (), |
| interval.ordinary.first, interval.ordinary.second, |
| interval.macro.first, interval.macro.second); |
| spans->quick_push (interval); |
| |
| /* Start an interval for the main file. */ |
| interval.ordinary.first = interval.ordinary.second; |
| interval.macro.second = interval.macro.first; |
| dump (dumper::LOCATION) |
| && dump ("Main span %u ordinary:[%u,*) macro:[*,%u)", spans->length (), |
| interval.ordinary.first, interval.macro.second); |
| spans->quick_push (interval); |
| } |
| |
| /* Reopen the span, if we want the about-to-be-inserted set of maps to |
| be propagated in our own location table. I.e. we are the primary |
| interface and we're importing a partition. */ |
| |
| bool |
| loc_spans::maybe_propagate (module_state *import, location_t hwm) |
| { |
| bool opened = (module_interface_p () && !module_partition_p () |
| && import->is_partition ()); |
| if (opened) |
| open (hwm); |
| return opened; |
| } |
| |
| /* Open a new linemap interval. The just-created ordinary map is the |
| first map of the interval. */ |
| |
| void |
| loc_spans::open (location_t hwm) |
| { |
| span interval; |
| interval.ordinary.first = interval.ordinary.second = hwm; |
| interval.macro.first = interval.macro.second |
| = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); |
| interval.ordinary_delta = interval.macro_delta = 0; |
| dump (dumper::LOCATION) |
| && dump ("Opening span %u ordinary:[%u,... macro:...,%u)", |
| spans->length (), interval.ordinary.first, |
| interval.macro.second); |
| if (spans->length ()) |
| { |
| /* No overlapping! */ |
| auto &last = spans->last (); |
| gcc_checking_assert (interval.ordinary.first >= last.ordinary.second); |
| gcc_checking_assert (interval.macro.second <= last.macro.first); |
| } |
| spans->safe_push (interval); |
| } |
| |
| /* Close out the current linemap interval. The last maps are within |
| the interval. */ |
| |
| void |
| loc_spans::close () |
| { |
| span &interval = spans->last (); |
| |
| interval.ordinary.second |
| = ((line_table->highest_location + (1 << line_table->default_range_bits)) |
| & ~((1u << line_table->default_range_bits) - 1)); |
| interval.macro.first = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); |
| dump (dumper::LOCATION) |
| && dump ("Closing span %u ordinary:[%u,%u) macro:[%u,%u)", |
| spans->length () - 1, |
| interval.ordinary.first,interval.ordinary.second, |
| interval.macro.first, interval.macro.second); |
| } |
| |
| /* Given an ordinary location LOC, return the lmap_interval it resides |
| in. NULL if it is not in an interval. */ |
| |
| const loc_spans::span * |
| loc_spans::ordinary (location_t loc) |
| { |
| unsigned len = spans->length (); |
| unsigned pos = 0; |
| while (len) |
| { |
| unsigned half = len / 2; |
| const span &probe = (*spans)[pos + half]; |
| if (loc < probe.ordinary.first) |
| len = half; |
| else if (loc < probe.ordinary.second) |
| return &probe; |
| else |
| { |
| pos += half + 1; |
| len = len - (half + 1); |
| } |
| } |
| return NULL; |
| } |
| |
| /* Likewise, given a macro location LOC, return the lmap interval it |
| resides in. */ |
| |
| const loc_spans::span * |
| loc_spans::macro (location_t loc) |
| { |
| unsigned len = spans->length (); |
| unsigned pos = 0; |
| while (len) |
| { |
| unsigned half = len / 2; |
| const span &probe = (*spans)[pos + half]; |
| if (loc >= probe.macro.second) |
| len = half; |
| else if (loc >= probe.macro.first) |
| return &probe; |
| else |
| { |
| pos += half + 1; |
| len = len - (half + 1); |
| } |
| } |
| return NULL; |
| } |
| |
| /* Return the ordinary location closest to FROM. */ |
| |
| static location_t |
| ordinary_loc_of (line_maps *lmaps, location_t from) |
| { |
| while (!IS_ORDINARY_LOC (from)) |
| { |
| if (IS_ADHOC_LOC (from)) |
| from = get_location_from_adhoc_loc (lmaps, from); |
| if (from >= LINEMAPS_MACRO_LOWEST_LOCATION (lmaps)) |
| { |
| /* Find the ordinary location nearest FROM. */ |
| const line_map *map = linemap_lookup (lmaps, from); |
| const line_map_macro *mac_map = linemap_check_macro (map); |
| from = MACRO_MAP_EXPANSION_POINT_LOCATION (mac_map); |
| } |
| } |
| return from; |
| } |
| |
| static module_state ** |
| get_module_slot (tree name, module_state *parent, bool partition, bool insert) |
| { |
| module_state_hash::compare_type ct (name, uintptr_t (parent) | partition); |
| hashval_t hv = module_state_hash::hash (ct); |
| |
| return modules_hash->find_slot_with_hash (ct, hv, insert ? INSERT : NO_INSERT); |
| } |
| |
| static module_state * |
| get_primary (module_state *parent) |
| { |
| while (parent->is_partition ()) |
| parent = parent->parent; |
| |
| if (!parent->name) |
| // Implementation unit has null name |
| parent = parent->parent; |
| |
| return parent; |
| } |
| |
| /* Find or create module NAME & PARENT in the hash table. */ |
| |
| module_state * |
| get_module (tree name, module_state *parent, bool partition) |
| { |
| if (partition) |
| { |
| if (!parent) |
| parent = get_primary ((*modules)[0]); |
| |
| if (!parent->is_partition () && !parent->flatname) |
| parent->set_flatname (); |
| } |
| |
| module_state **slot = get_module_slot (name, parent, partition, true); |
| module_state *state = *slot; |
| if (!state) |
| { |
| state = (new (ggc_alloc<module_state> ()) |
| module_state (name, parent, partition)); |
| *slot = state; |
| } |
| return state; |
| } |
| |
| /* Process string name PTR into a module_state. */ |
| |
| static module_state * |
| get_module (const char *ptr) |
| { |
| if (ptr[0] == '.' ? IS_DIR_SEPARATOR (ptr[1]) : IS_ABSOLUTE_PATH (ptr)) |
| /* A header name. */ |
| return get_module (build_string (strlen (ptr), ptr)); |
| |
| bool partition = false; |
| module_state *mod = NULL; |
| |
| for (const char *probe = ptr;; probe++) |
| if (!*probe || *probe == '.' || *probe == ':') |
| { |
| if (probe == ptr) |
| return NULL; |
| |
| mod = get_module (get_identifier_with_length (ptr, probe - ptr), |
| mod, partition); |
| ptr = probe; |
| if (*ptr == ':') |
| { |
| if (partition) |
| return NULL; |
| partition = true; |
| } |
| |
| if (!*ptr++) |
| break; |
| } |
| else if (!(ISALPHA (*probe) || *probe == '_' |
| || (probe != ptr && ISDIGIT (*probe)))) |
| return NULL; |
| |
| return mod; |
| } |
| |
| /* Create a new mapper connecting to OPTION. */ |
| |
| module_client * |
| make_mapper (location_t loc) |
| { |
| timevar_start (TV_MODULE_MAPPER); |
| const char *option = module_mapper_name; |
| if (!option) |
| option = getenv ("CXX_MODULE_MAPPER"); |
| |
| mapper = module_client::open_module_client |
| (loc, option, &set_cmi_repo, |
| (save_decoded_options[0].opt_index == OPT_SPECIAL_program_name) |
| && save_decoded_options[0].arg != progname |
| ? save_decoded_options[0].arg : nullptr); |
| |
| timevar_stop (TV_MODULE_MAPPER); |
| |
| return mapper; |
| } |
| |
| static unsigned lazy_snum; |
| |
| static bool |
| recursive_lazy (unsigned snum = ~0u) |
| { |
| if (lazy_snum) |
| { |
| error_at (input_location, "recursive lazy load"); |
| return true; |
| } |
| |
| lazy_snum = snum; |
| return false; |
| } |
| |
| /* If THIS is the current purview, issue an import error and return false. */ |
| |
| bool |
| module_state::check_not_purview (location_t from) |
| { |
| module_state *imp = (*modules)[0]; |
| if (imp && !imp->name) |
| imp = imp->parent; |
| if (imp == this) |
| { |
| /* Cannot import the current module. */ |
| error_at (from, "cannot import module in its own purview"); |
| inform (loc, "module %qs declared here", get_flatname ()); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Module name substitutions. */ |
| static vec<module_state *,va_heap> substs; |
| |
| void |
| module_state::mangle (bool include_partition) |
| { |
| if (subst) |
| mangle_module_substitution (subst - 1); |
| else |
| { |
| if (parent) |
| parent->mangle (include_partition); |
| if (include_partition || !is_partition ()) |
| { |
| char p = 0; |
| // Partitions are significant for global initializer functions |
| if (is_partition () && !parent->is_partition ()) |
| p = 'P'; |
| substs.safe_push (this); |
| subst = substs.length (); |
| mangle_identifier (p, name); |
| } |
| } |
| } |
| |
| void |
| mangle_module (int mod, bool include_partition) |
| { |
| module_state *imp = (*modules)[mod]; |
| |
| if (!imp->name) |
| /* Set when importing the primary module interface. */ |
| imp = imp->parent; |
| |
| imp->mangle (include_partition); |
| } |
| |
| /* Clean up substitutions. */ |
| void |
| mangle_module_fini () |
| { |
| while (substs.length ()) |
| substs.pop ()->subst = 0; |
| } |
| |
| /* Announce WHAT about the module. */ |
| |
| void |
| module_state::announce (const char *what) const |
| { |
| if (noisy_p ()) |
| { |
| fprintf (stderr, " %s:%s", what, get_flatname ()); |
| fflush (stderr); |
| } |
| } |
| |
| /* A human-readable README section. The contents of this section to |
| not contribute to the CRC, so the contents can change per |
| compilation. That allows us to embed CWD, hostname, build time and |
| what not. It is a STRTAB that may be extracted with: |
| readelf -pgnu.c++.README $(module).gcm */ |
| |
| void |
| module_state::write_readme (elf_out *to, cpp_reader *reader, |
| const char *dialect, unsigned extensions) |
| { |
| bytes_out readme (to); |
| |
| readme.begin (false); |
| |
| readme.printf ("GNU C++ %smodule%s%s", |
| is_header () ? "header " : is_partition () ? "" : "primary ", |
| is_header () ? "" |
| : is_interface () ? " interface" : " implementation", |
| is_partition () ? " partition" : ""); |
| |
| /* Compiler's version. */ |
| readme.printf ("compiler: %s", version_string); |
| |
| /* Module format version. */ |
| verstr_t string; |
| version2string (MODULE_VERSION, string); |
| readme.printf ("version: %s", string); |
| |
| /* Module information. */ |
| readme.printf ("module: %s", get_flatname ()); |
| readme.printf ("source: %s", main_input_filename); |
| readme.printf ("dialect: %s", dialect); |
| if (extensions) |
| readme.printf ("extensions: %s", |
| extensions & SE_OPENMP ? "-fopenmp" : ""); |
| |
| /* The following fields could be expected to change between |
| otherwise identical compilations. Consider a distributed build |
| system. We should have a way of overriding that. */ |
| if (char *cwd = getcwd (NULL, 0)) |
| { |
| readme.printf ("cwd: %s", cwd); |
| free (cwd); |
| } |
| readme.printf ("repository: %s", cmi_repo ? cmi_repo : "."); |
| #if NETWORKING |
| { |
| char hostname[64]; |
| if (!gethostname (hostname, sizeof (hostname))) |
| readme.printf ("host: %s", hostname); |
| } |
| #endif |
| { |
| /* This of course will change! */ |
| time_t stampy; |
| auto kind = cpp_get_date (reader, &stampy); |
| if (kind != CPP_time_kind::UNKNOWN) |
| { |
| struct tm *time; |
| |
| time = gmtime (&stampy); |
| readme.print_time ("build", time, "UTC"); |
| |
| if (kind == CPP_time_kind::DYNAMIC) |
| { |
| time = localtime (&stampy); |
| readme.print_time ("local", time, |
| #if defined (__USE_MISC) || defined (__USE_BSD) /* Is there a better way? */ |
| time->tm_zone |
| #else |
| "" |
| #endif |
| ); |
| } |
| } |
| } |
| |
| /* Its direct imports. */ |
| for (unsigned ix = 1; ix < modules->length (); ix++) |
| { |
| module_state *state = (*modules)[ix]; |
| |
| if (state->is_direct ()) |
| readme.printf ("%s: %s %s", state->exported_p ? "export" : "import", |
| state->get_flatname (), state->filename); |
| } |
| |
| readme.end (to, to->name (MOD_SNAME_PFX ".README"), NULL); |
| } |
| |
| /* Sort environment var names in reverse order. */ |
| |
| static int |
| env_var_cmp (const void *a_, const void *b_) |
| { |
| const unsigned char *a = *(const unsigned char *const *)a_; |
| const unsigned char *b = *(const unsigned char *const *)b_; |
| |
| for (unsigned ix = 0; ; ix++) |
| { |
| bool a_end = !a[ix] || a[ix] == '='; |
| if (a[ix] == b[ix]) |
| { |
| if (a_end) |
| break; |
| } |
| else |
| { |
| bool b_end = !b[ix] || b[ix] == '='; |
| |
| if (!a_end && !b_end) |
| return a[ix] < b[ix] ? +1 : -1; |
| if (a_end && b_end) |
| break; |
| return a_end ? +1 : -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Write the environment. It is a STRTAB that may be extracted with: |
| readelf -pgnu.c++.ENV $(module).gcm */ |
| |
| void |
| module_state::write_env (elf_out *to) |
| { |
| vec<const char *> vars; |
| vars.create (20); |
| |
| extern char **environ; |
| while (const char *var = environ[vars.length ()]) |
| vars.safe_push (var); |
| vars.qsort (env_var_cmp); |
| |
| bytes_out env (to); |
| env.begin (false); |
| while (vars.length ()) |
| env.printf ("%s", vars.pop ()); |
| env.end (to, to->name (MOD_SNAME_PFX ".ENV"), NULL); |
| |
| vars.release (); |
| } |
| |
| /* Write the direct or indirect imports. |
| u:N |
| { |
| u:index |
| s:name |
| u32:crc |
| s:filename (direct) |
| u:exported (direct) |
| } imports[N] |
| */ |
| |
| void |
| module_state::write_imports (bytes_out &sec, bool direct) |
| { |
| unsigned count = 0; |
| |
| for (unsigned ix = 1; ix < modules->length (); ix++) |
| { |
| module_state *imp = (*modules)[ix]; |
| |
| if (imp->remap && imp->is_direct () == direct) |
| count++; |
| } |
| |
| gcc_assert (!direct || count); |
| |
| sec.u (count); |
| for (unsigned ix = 1; ix < modules->length (); ix++) |
| { |
| module_state *imp = (*modules)[ix]; |
| |
| if (imp->remap && imp->is_direct () == direct) |
| { |
| dump () && dump ("Writing %simport:%u->%u %M (crc=%x)", |
| !direct ? "indirect " |
| : imp->exported_p ? "exported " : "", |
| ix, imp->remap, imp, imp->crc); |
| sec.u (imp->remap); |
| sec.str (imp->get_flatname ()); |
| sec.u32 (imp->crc); |
| if (direct) |
| { |
| write_location (sec, imp->imported_from ()); |
| sec.str (imp->filename); |
| int exportedness = 0; |
| if (imp->exported_p) |
| exportedness = +1; |
| else if (!imp->is_purview_direct ()) |
| exportedness = -1; |
| sec.i (exportedness); |
| } |
| } |
| } |
| } |
| |
| /* READER, LMAPS != NULL == direct imports, |
| == NUL == indirect imports. */ |
| |
| unsigned |
| module_state::read_imports (bytes_in &sec, cpp_reader *reader, line_maps *lmaps) |
| { |
| unsigned count = sec.u (); |
| unsigned loaded = 0; |
| |
| while (count--) |
| { |
| unsigned ix = sec.u (); |
| if (ix >= slurp->remap->length () || !ix || (*slurp->remap)[ix]) |
| { |
| sec.set_overrun (); |
| break; |
| } |
| |
| const char *name = sec.str (NULL); |
| module_state *imp = get_module (name); |
| unsigned crc = sec.u32 (); |
| int exportedness = 0; |
| |
| /* If the import is a partition, it must be the same primary |
| module as this TU. */ |
| if (imp && imp->is_partition () && |
| (!named_module_p () |
| || (get_primary ((*modules)[0]) != get_primary (imp)))) |
| imp = NULL; |
| |
| if (!imp) |
| sec.set_overrun (); |
| if (sec.get_overrun ()) |
| break; |
| |
| if (lmaps) |
| { |
| /* A direct import, maybe load it. */ |
| location_t floc = read_location (sec); |
| const char *fname = sec.str (NULL); |
| exportedness = sec.i (); |
| |
| if (sec.get_overrun ()) |
| break; |
| |
| if (!imp->check_not_purview (loc)) |
| continue; |
| |
| if (imp->loadedness == ML_NONE) |
| { |
| imp->loc = floc; |
| imp->crc = crc; |
| if (!imp->get_flatname ()) |
| imp->set_flatname (); |
| |
| unsigned n = dump.push (imp); |
| |
| if (!imp->filename && fname) |
| imp->filename = xstrdup (fname); |
| |
| if (imp->is_partition ()) |
| dump () && dump ("Importing elided partition %M", imp); |
| |
| if (!imp->do_import (reader, false)) |
| imp = NULL; |
| dump.pop (n); |
| if (!imp) |
| continue; |
| } |
| |
| if (is_partition ()) |
| { |
| if (!imp->is_direct ()) |
| imp->directness = MD_PARTITION_DIRECT; |
| if (exportedness > 0) |
| imp->exported_p = true; |
| } |
| } |
| else |
| { |
| /* An indirect import, find it, it should already be here. */ |
| if (imp->loadedness == ML_NONE) |
| { |
| error_at (loc, "indirect import %qs is not already loaded", name); |
| continue; |
| } |
| } |
| |
| if (imp->crc != crc) |
| error_at (loc, "import %qs has CRC mismatch", imp->get_flatname ()); |
| |
| (*slurp->remap)[ix] = (imp->mod << 1) | (lmaps != NULL); |
| |
| if (lmaps && exportedness >= 0) |
| set_import (imp, bool (exportedness)); |
| dump () && dump ("Found %simport:%u %M->%u", !lmaps ? "indirect " |
| : exportedness > 0 ? "exported " |
| : exportedness < 0 ? "gmf" : "", ix, imp, |
| imp->mod); |
| loaded++; |
| } |
| |
| return loaded; |
| } |
| |
| /* Write the import table to MOD_SNAME_PFX.imp. */ |
| |
| void |
| module_state::write_imports (elf_out *to, unsigned *crc_ptr) |
| { |
| dump () && dump ("Writing imports"); |
| dump.indent (); |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| write_imports (sec, true); |
| write_imports (sec, false); |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".imp"), crc_ptr); |
| dump.outdent (); |
| } |
| |
| bool |
| module_state::read_imports (cpp_reader *reader, line_maps *lmaps) |
| { |
| bytes_in sec; |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".imp")) |
| return false; |
| |
| dump () && dump ("Reading %u imports", slurp->remap->length () - 1); |
| dump.indent (); |
| |
| /* Read the imports. */ |
| unsigned direct = read_imports (sec, reader, lmaps); |
| unsigned indirect = read_imports (sec, NULL, NULL); |
| if (direct + indirect + 1 != slurp->remap->length ()) |
| from ()->set_error (elf::E_BAD_IMPORT); |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* We're the primary module interface, but have partitions. Document |
| them so that non-partition module implementation units know which |
| have already been loaded. */ |
| |
| void |
| module_state::write_partitions (elf_out *to, unsigned count, unsigned *crc_ptr) |
| { |
| dump () && dump ("Writing %u elided partitions", count); |
| dump.indent (); |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| for (unsigned ix = 1; ix != modules->length (); ix++) |
| { |
| module_state *imp = (*modules)[ix]; |
| if (imp->is_partition ()) |
| { |
| dump () && dump ("Writing elided partition %M (crc=%x)", |
| imp, imp->crc); |
| sec.str (imp->get_flatname ()); |
| sec.u32 (imp->crc); |
| write_location (sec, imp->is_direct () |
| ? imp->imported_from () : UNKNOWN_LOCATION); |
| sec.str (imp->filename); |
| } |
| } |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".prt"), crc_ptr); |
| dump.outdent (); |
| } |
| |
| bool |
| module_state::read_partitions (unsigned count) |
| { |
| bytes_in sec; |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".prt")) |
| return false; |
| |
| dump () && dump ("Reading %u elided partitions", count); |
| dump.indent (); |
| |
| while (count--) |
| { |
| const char *name = sec.str (NULL); |
| unsigned crc = sec.u32 (); |
| location_t floc = read_location (sec); |
| const char *fname = sec.str (NULL); |
| |
| if (sec.get_overrun ()) |
| break; |
| |
| dump () && dump ("Reading elided partition %s (crc=%x)", name, crc); |
| |
| module_state *imp = get_module (name); |
| if (!imp /* Partition should be ... */ |
| || !imp->is_partition () /* a partition ... */ |
| || imp->loadedness != ML_NONE /* that is not yet loaded ... */ |
| || get_primary (imp) != this) /* whose primary is this. */ |
| { |
| sec.set_overrun (); |
| break; |
| } |
| |
| if (!imp->has_location ()) |
| imp->loc = floc; |
| imp->crc = crc; |
| if (!imp->filename && fname[0]) |
| imp->filename = xstrdup (fname); |
| } |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* Counter indices. */ |
| enum module_state_counts |
| { |
| MSC_sec_lwm, |
| MSC_sec_hwm, |
| MSC_pendings, |
| MSC_entities, |
| MSC_namespaces, |
| MSC_bindings, |
| MSC_macros, |
| MSC_inits, |
| MSC_HWM |
| }; |
| |
| /* Data for config reading and writing. */ |
| struct module_state_config { |
| const char *dialect_str; |
| unsigned num_imports; |
| unsigned num_partitions; |
| unsigned num_entities; |
| unsigned ordinary_locs; |
| unsigned macro_locs; |
| unsigned ordinary_loc_align; |
| |
| public: |
| module_state_config () |
| :dialect_str (get_dialect ()), |
| num_imports (0), num_partitions (0), num_entities (0), |
| ordinary_locs (0), macro_locs (0), ordinary_loc_align (0) |
| { |
| } |
| |
| static void release () |
| { |
| XDELETEVEC (dialect); |
| dialect = NULL; |
| } |
| |
| private: |
| static const char *get_dialect (); |
| static char *dialect; |
| }; |
| |
| char *module_state_config::dialect; |
| |
| /* Generate a string of the significant compilation options. |
| Generally assume the user knows what they're doing, in the same way |
| that object files can be mixed. */ |
| |
| const char * |
| module_state_config::get_dialect () |
| { |
| if (!dialect) |
| dialect = concat (get_cxx_dialect_name (cxx_dialect), |
| /* C++ implies these, only show if disabled. */ |
| flag_exceptions ? "" : "/no-exceptions", |
| flag_rtti ? "" : "/no-rtti", |
| flag_new_inheriting_ctors ? "" : "/old-inheriting-ctors", |
| /* C++ 20 implies concepts. */ |
| cxx_dialect < cxx20 && flag_concepts ? "/concepts" : "", |
| flag_coroutines ? "/coroutines" : "", |
| flag_module_implicit_inline ? "/implicit-inline" : "", |
| NULL); |
| |
| return dialect; |
| } |
| |
| /* Contents of a cluster. */ |
| enum cluster_tag { |
| ct_decl, /* A decl. */ |
| ct_defn, /* A definition. */ |
| ct_bind, /* A binding. */ |
| ct_hwm |
| }; |
| |
| /* Binding modifiers. */ |
| enum ct_bind_flags |
| { |
| cbf_export = 0x1, /* An exported decl. */ |
| cbf_hidden = 0x2, /* A hidden (friend) decl. */ |
| cbf_using = 0x4, /* A using decl. */ |
| cbf_wrapped = 0x8, /* ... that is wrapped. */ |
| }; |
| |
| /* DEP belongs to a different cluster, seed it to prevent |
| unfortunately timed duplicate import. */ |
| // FIXME: QOI For inter-cluster references we could just only pick |
| // one entity from an earlier cluster. Even better track |
| // dependencies between earlier clusters |
| |
| void |
| module_state::intercluster_seed (trees_out &sec, unsigned index_hwm, depset *dep) |
| { |
| if (dep->is_import () |
| || dep->cluster < index_hwm) |
| { |
| tree ent = dep->get_entity (); |
| if (!TREE_VISITED (ent)) |
| { |
| sec.tree_node (ent); |
| dump (dumper::CLUSTER) |
| && dump ("Seeded %s %N", |
| dep->is_import () ? "import" : "intercluster", ent); |
| } |
| } |
| } |
| |
| /* Write the cluster of depsets in SCC[0-SIZE). |
| dep->section -> section number |
| dep->cluster -> entity number |
| */ |
| |
| unsigned |
| module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, |
| depset::hash &table, unsigned *counts, |
| unsigned *crc_ptr) |
| { |
| dump () && dump ("Writing section:%u %u depsets", table.section, size); |
| dump.indent (); |
| |
| trees_out sec (to, this, table, table.section); |
| sec.begin (); |
| unsigned index_lwm = counts[MSC_entities]; |
| |
| /* Determine entity numbers, mark for writing. */ |
| dump (dumper::CLUSTER) && dump ("Cluster members:") && (dump.indent (), true); |
| for (unsigned ix = 0; ix != size; ix++) |
| { |
| depset *b = scc[ix]; |
| |
| switch (b->get_entity_kind ()) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case depset::EK_BINDING: |
| { |
| dump (dumper::CLUSTER) |
| && dump ("[%u]=%s %P", ix, b->entity_kind_name (), |
| b->get_entity (), b->get_name ()); |
| depset *ns_dep = b->deps[0]; |
| gcc_checking_assert (ns_dep->get_entity_kind () |
| == depset::EK_NAMESPACE |
| && ns_dep->get_entity () == b->get_entity ()); |
| for (unsigned jx = b->deps.length (); --jx;) |
| { |
| depset *dep = b->deps[jx]; |
| // We could be declaring something that is also a |
| // (merged) import |
| gcc_checking_assert (dep->is_import () |
| || TREE_VISITED (dep->get_entity ()) |
| || (dep->get_entity_kind () |
| == depset::EK_USING)); |
| } |
| } |
| break; |
| |
| case depset::EK_DECL: |
| case depset::EK_SPECIALIZATION: |
| case depset::EK_PARTIAL: |
| b->cluster = counts[MSC_entities]++; |
| sec.mark_declaration (b->get_entity (), b->has_defn ()); |
| /* FALLTHROUGH */ |
| |
| case depset::EK_USING: |
| gcc_checking_assert (!b->is_import () |
| && !b->is_unreached ()); |
| dump (dumper::CLUSTER) |
| && dump ("[%u]=%s %s %N", ix, b->entity_kind_name (), |
| b->has_defn () ? "definition" : "declaration", |
| b->get_entity ()); |
| break; |
| } |
| } |
| dump (dumper::CLUSTER) && (dump.outdent (), true); |
| |
| /* Ensure every out-of-cluster decl is referenced before we start |
| streaming. We must do both imports *and* earlier clusters, |
| because the latter could reach into the former and cause a |
| duplicate loop. */ |
| sec.set_importing (+1); |
| for (unsigned ix = 0; ix != size; ix++) |
| { |
| depset *b = scc[ix]; |
| for (unsigned jx = (b->get_entity_kind () == depset::EK_BINDING |
| || b->is_special ()) ? 1 : 0; |
| jx != b->deps.length (); jx++) |
| { |
| depset *dep = b->deps[jx]; |
| |
| if (dep->is_binding ()) |
| { |
| for (unsigned ix = dep->deps.length (); --ix;) |
| { |
| depset *bind = dep->deps[ix]; |
| if (bind->get_entity_kind () == depset::EK_USING) |
| bind = bind->deps[1]; |
| |
| intercluster_seed (sec, index_lwm, bind); |
| } |
| /* Also check the namespace itself. */ |
| dep = dep->deps[0]; |
| } |
| |
| intercluster_seed (sec, index_lwm, dep); |
| } |
| } |
| sec.tree_node (NULL_TREE); |
| /* We're done importing now. */ |
| sec.set_importing (-1); |
| |
| /* Write non-definitions. */ |
| for (unsigned ix = 0; ix != size; ix++) |
| { |
| depset *b = scc[ix]; |
| tree decl = b->get_entity (); |
| switch (b->get_entity_kind ()) |
| { |
| default: |
| gcc_unreachable (); |
| break; |
| |
| case depset::EK_BINDING: |
| { |
| gcc_assert (TREE_CODE (decl) == NAMESPACE_DECL); |
| dump () && dump ("Depset:%u binding %C:%P", ix, TREE_CODE (decl), |
| decl, b->get_name ()); |
| sec.u (ct_bind); |
| sec.tree_node (decl); |
| sec.tree_node (b->get_name ()); |
| |
| /* Write in reverse order, so reading will see the exports |
| first, thus building the overload chain will be |
| optimized. */ |
| for (unsigned jx = b->deps.length (); --jx;) |
| { |
| depset *dep = b->deps[jx]; |
| tree bound = dep->get_entity (); |
| unsigned flags = 0; |
| if (dep->get_entity_kind () == depset::EK_USING) |
| { |
| tree ovl = bound; |
| bound = OVL_FUNCTION (bound); |
| if (!(TREE_CODE (bound) == CONST_DECL |
| && UNSCOPED_ENUM_P (TREE_TYPE (bound)) |
| && decl == TYPE_NAME (TREE_TYPE (bound)))) |
| { |
| /* An unscope enumerator in its enumeration's |
| scope is not a using. */ |
| flags |= cbf_using; |
| if (OVL_USING_P (ovl)) |
| flags |= cbf_wrapped; |
| } |
| if (OVL_EXPORT_P (ovl)) |
| flags |= cbf_export; |
| } |
| else |
| { |
| /* An implicit typedef must be at one. */ |
| gcc_assert (!DECL_IMPLICIT_TYPEDEF_P (bound) || jx == 1); |
| if (dep->is_hidden ()) |
| flags |= cbf_hidden; |
| else if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (bound))) |
| flags |= cbf_export; |
| } |
| |
| gcc_checking_assert (DECL_P (bound)); |
| |
| sec.i (flags); |
| sec.tree_node (bound); |
| } |
| |
| /* Terminate the list. */ |
| sec.i (-1); |
| } |
| break; |
| |
| case depset::EK_USING: |
| dump () && dump ("Depset:%u %s %C:%N", ix, b->entity_kind_name (), |
| TREE_CODE (decl), decl); |
| break; |
| |
| case depset::EK_SPECIALIZATION: |
| case depset::EK_PARTIAL: |
| case depset::EK_DECL: |
| dump () && dump ("Depset:%u %s entity:%u %C:%N", ix, |
| b->entity_kind_name (), b->cluster, |
| TREE_CODE (decl), decl); |
| |
| sec.u (ct_decl); |
| sec.tree_node (decl); |
| |
| dump () && dump ("Wrote declaration entity:%u %C:%N", |
| b->cluster, TREE_CODE (decl), decl); |
| break; |
| } |
| } |
| |
| depset *namer = NULL; |
| |
| /* Write out definitions */ |
| for (unsigned ix = 0; ix != size; ix++) |
| { |
| depset *b = scc[ix]; |
| tree decl = b->get_entity (); |
| switch (b->get_entity_kind ()) |
| { |
| default: |
| break; |
| |
| case depset::EK_SPECIALIZATION: |
| case depset::EK_PARTIAL: |
| case depset::EK_DECL: |
| if (!namer) |
| namer = b; |
| |
| if (b->has_defn ()) |
| { |
| sec.u (ct_defn); |
| sec.tree_node (decl); |
| dump () && dump ("Writing definition %N", decl); |
| sec.write_definition (decl); |
| |
| if (!namer->has_defn ()) |
| namer = b; |
| } |
| break; |
| } |
| } |
| |
| /* We don't find the section by name. Use depset's decl's name for |
| human friendliness. */ |
| unsigned name = 0; |
| tree naming_decl = NULL_TREE; |
| if (namer) |
| { |
| naming_decl = namer->get_entity (); |
| if (namer->get_entity_kind () == depset::EK_USING) |
| /* This unfortunately names the section from the target of the |
| using decl. But the name is only a guide, so Do Not Care. */ |
| naming_decl = OVL_FUNCTION (naming_decl); |
| if (DECL_IMPLICIT_TYPEDEF_P (naming_decl)) |
| /* Lose any anonymousness. */ |
| naming_decl = TYPE_NAME (TREE_TYPE (naming_decl)); |
| name = to->qualified_name (naming_decl, namer->has_defn ()); |
| } |
| |
| unsigned bytes = sec.pos; |
| unsigned snum = sec.end (to, name, crc_ptr); |
| |
| for (unsigned ix = size; ix--;) |
| gcc_checking_assert (scc[ix]->section == snum); |
| |
| dump.outdent (); |
| dump () && dump ("Wrote section:%u named-by:%N", table.section, naming_decl); |
| |
| return bytes; |
| } |
| |
| /* Read a cluster from section SNUM. */ |
| |
| bool |
| module_state::read_cluster (unsigned snum) |
| { |
| trees_in sec (this); |
| |
| if (!sec.begin (loc, from (), snum)) |
| return false; |
| |
| dump () && dump ("Reading section:%u", snum); |
| dump.indent (); |
| |
| /* We care about structural equality. */ |
| comparing_dependent_aliases++; |
| |
| /* First seed the imports. */ |
| while (tree import = sec.tree_node ()) |
| dump (dumper::CLUSTER) && dump ("Seeded import %N", import); |
| |
| while (!sec.get_overrun () && sec.more_p ()) |
| { |
| unsigned ct = sec.u (); |
| switch (ct) |
| { |
| default: |
| sec.set_overrun (); |
| break; |
| |
| case ct_bind: |
| /* A set of namespace bindings. */ |
| { |
| tree ns = sec.tree_node (); |
| tree name = sec.tree_node (); |
| tree decls = NULL_TREE; |
| tree visible = NULL_TREE; |
| tree type = NULL_TREE; |
| bool dedup = false; |
| |
| /* We rely on the bindings being in the reverse order of |
| the resulting overload set. */ |
| for (;;) |
| { |
| int flags = sec.i (); |
| if (flags < 0) |
| break; |
| |
| if ((flags & cbf_hidden) |
| && (flags & (cbf_using | cbf_export))) |
| sec.set_overrun (); |
| |
| tree decl = sec.tree_node (); |
| if (sec.get_overrun ()) |
| break; |
| |
| if (decls && TREE_CODE (decl) == TYPE_DECL) |
| { |
| /* Stat hack. */ |
| if (type || !DECL_IMPLICIT_TYPEDEF_P (decl)) |
| sec.set_overrun (); |
| type = decl; |
| } |
| else |
| { |
| if (decls |
| || (flags & (cbf_hidden | cbf_wrapped)) |
| || DECL_FUNCTION_TEMPLATE_P (decl)) |
| { |
| decls = ovl_make (decl, decls); |
| if (flags & cbf_using) |
| { |
| dedup = true; |
| OVL_USING_P (decls) = true; |
| if (flags & cbf_export) |
| OVL_EXPORT_P (decls) = true; |
| } |
| |
| if (flags & cbf_hidden) |
| OVL_HIDDEN_P (decls) = true; |
| else if (dedup) |
| OVL_DEDUP_P (decls) = true; |
| } |
| else |
| decls = decl; |
| |
| if (flags & cbf_export |
| || (!(flags & cbf_hidden) |
| && (is_module () || is_partition ()))) |
| visible = decls; |
| } |
| } |
| |
| if (!decls) |
| sec.set_overrun (); |
| |
| if (sec.get_overrun ()) |
| break; /* Bail. */ |
| |
| dump () && dump ("Binding of %P", ns, name); |
| if (!set_module_binding (ns, name, mod, |
| is_header () ? -1 |
| : is_module () || is_partition () ? 1 |
| : 0, |
| decls, type, visible)) |
| sec.set_overrun (); |
| } |
| break; |
| |
| case ct_decl: |
| /* A decl. */ |
| { |
| tree decl = sec.tree_node (); |
| dump () && dump ("Read declaration of %N", decl); |
| } |
| break; |
| |
| case ct_defn: |
| { |
| tree decl = sec.tree_node (); |
| dump () && dump ("Reading definition of %N", decl); |
| sec.read_definition (decl); |
| } |
| break; |
| } |
| } |
| |
| /* When lazy loading is in effect, we can be in the middle of |
| parsing or instantiating a function. Save it away. |
| push_function_context does too much work. */ |
| tree old_cfd = current_function_decl; |
| struct function *old_cfun = cfun; |
| while (tree decl = sec.post_process ()) |
| { |
| bool abstract = false; |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| abstract = true; |
| decl = DECL_TEMPLATE_RESULT (decl); |
| } |
| |
| current_function_decl = decl; |
| allocate_struct_function (decl, abstract); |
| cfun->language = ggc_cleared_alloc<language_function> (); |
| cfun->language->base.x_stmt_tree.stmts_are_full_exprs_p = 1; |
| |
| if (abstract) |
| ; |
| else if (DECL_ABSTRACT_P (decl)) |
| vec_safe_push (post_load_decls, decl); |
| else |
| { |
| bool aggr = aggregate_value_p (DECL_RESULT (decl), decl); |
| #ifdef PCC_STATIC_STRUCT_RETURN |
| cfun->returns_pcc_struct = aggr; |
| #endif |
| cfun->returns_struct = aggr; |
| |
| if (DECL_COMDAT (decl)) |
| // FIXME: Comdat grouping? |
| comdat_linkage (decl); |
| note_vague_linkage_fn (decl); |
| cgraph_node::finalize_function (decl, true); |
| } |
| |
| } |
| /* Look, function.c's interface to cfun does too much for us, we |
| just need to restore the old value. I do not want to go |
| redesigning that API right now. */ |
| #undef cfun |
| cfun = old_cfun; |
| current_function_decl = old_cfd; |
| comparing_dependent_aliases--; |
| |
| dump.outdent (); |
| dump () && dump ("Read section:%u", snum); |
| |
| loaded_clusters++; |
| |
| if (!sec.end (from ())) |
| return false; |
| |
| return true; |
| } |
| |
| void |
| module_state::write_namespace (bytes_out &sec, depset *dep) |
| { |
| unsigned ns_num = dep->cluster; |
| unsigned ns_import = 0; |
| |
| if (dep->is_import ()) |
| ns_import = dep->section; |
| else if (dep->get_entity () != global_namespace) |
| ns_num++; |
| |
| sec.u (ns_import); |
| sec.u (ns_num); |
| } |
| |
| tree |
| module_state::read_namespace (bytes_in &sec) |
| { |
| unsigned ns_import = sec.u (); |
| unsigned ns_num = sec.u (); |
| tree ns = NULL_TREE; |
| |
| if (ns_import || ns_num) |
| { |
| if (!ns_import) |
| ns_num--; |
| |
| if (unsigned origin = slurp->remap_module (ns_import)) |
| { |
| module_state *from = (*modules)[origin]; |
| if (ns_num < from->entity_num) |
| { |
| binding_slot &slot = (*entity_ary)[from->entity_lwm + ns_num]; |
| |
| if (!slot.is_lazy ()) |
| ns = slot; |
| } |
| } |
| else |
| sec.set_overrun (); |
| } |
| else |
| ns = global_namespace; |
| |
| return ns; |
| } |
| |
| /* SPACES is a sorted vector of namespaces. Write out the namespaces |
| to MOD_SNAME_PFX.nms section. */ |
| |
| void |
| module_state::write_namespaces (elf_out *to, vec<depset *> spaces, |
| unsigned num, unsigned *crc_p) |
| { |
| dump () && dump ("Writing namespaces"); |
| dump.indent (); |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| for (unsigned ix = 0; ix != num; ix++) |
| { |
| depset *b = spaces[ix]; |
| tree ns = b->get_entity (); |
| |
| gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL); |
| /* P1815 may have something to say about this. */ |
| gcc_checking_assert (TREE_PUBLIC (ns)); |
| |
| unsigned flags = 0; |
| if (TREE_PUBLIC (ns)) |
| flags |= 1; |
| if (DECL_NAMESPACE_INLINE_P (ns)) |
| flags |= 2; |
| if (DECL_MODULE_PURVIEW_P (ns)) |
| flags |= 4; |
| if (DECL_MODULE_EXPORT_P (ns)) |
| flags |= 8; |
| |
| dump () && dump ("Writing namespace:%u %N%s%s%s%s", |
| b->cluster, ns, |
| flags & 1 ? ", public" : "", |
| flags & 2 ? ", inline" : "", |
| flags & 4 ? ", purview" : "", |
| flags & 8 ? ", export" : ""); |
| sec.u (b->cluster); |
| sec.u (to->name (DECL_NAME (ns))); |
| write_namespace (sec, b->deps[0]); |
| |
| sec.u (flags); |
| write_location (sec, DECL_SOURCE_LOCATION (ns)); |
| } |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".nms"), crc_p); |
| dump.outdent (); |
| } |
| |
| /* Read the namespace hierarchy from MOD_SNAME_PFX.namespace. Fill in |
| SPACES from that data. */ |
| |
| bool |
| module_state::read_namespaces (unsigned num) |
| { |
| bytes_in sec; |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".nms")) |
| return false; |
| |
| dump () && dump ("Reading namespaces"); |
| dump.indent (); |
| |
| for (unsigned ix = 0; ix != num; ix++) |
| { |
| unsigned entity_index = sec.u (); |
| unsigned name = sec.u (); |
| |
| tree parent = read_namespace (sec); |
| |
| /* See comment in write_namespace about why not bits. */ |
| unsigned flags = sec.u (); |
| location_t src_loc = read_location (sec); |
| |
| if (entity_index >= entity_num |
| || !parent |
| || (flags & 0xc) == 0x8) |
| sec.set_overrun (); |
| if (sec.get_overrun ()) |
| break; |
| |
| tree id = name ? get_identifier (from ()->name (name)) : NULL_TREE; |
| |
| dump () && dump ("Read namespace:%u %P%s%s%s%s", |
| entity_index, parent, id, |
| flags & 1 ? ", public" : "", |
| flags & 2 ? ", inline" : "", |
| flags & 4 ? ", purview" : "", |
| flags & 8 ? ", export" : ""); |
| bool visible_p = ((flags & 8) |
| || ((flags & 1) |
| && (flags & 4) |
| && (is_partition () || is_module ()))); |
| tree inner = add_imported_namespace (parent, id, src_loc, mod, |
| bool (flags & 2), visible_p); |
| if (!inner) |
| { |
| sec.set_overrun (); |
| break; |
| } |
| |
| if (is_partition ()) |
| { |
| if (flags & 4) |
| DECL_MODULE_PURVIEW_P (inner) = true; |
| if (flags & 8) |
| DECL_MODULE_EXPORT_P (inner) = true; |
| } |
| |
| /* Install the namespace. */ |
| (*entity_ary)[entity_lwm + entity_index] = inner; |
| if (DECL_MODULE_IMPORT_P (inner)) |
| { |
| bool existed; |
| unsigned *slot = &entity_map->get_or_insert |
| (DECL_UID (inner), &existed); |
| if (existed) |
| /* If it existed, it should match. */ |
| gcc_checking_assert (inner == (*entity_ary)[*slot]); |
| else |
| *slot = entity_lwm + entity_index; |
| } |
| } |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* Write the binding TABLE to MOD_SNAME_PFX.bnd */ |
| |
| unsigned |
| module_state::write_bindings (elf_out *to, vec<depset *> sccs, unsigned *crc_p) |
| { |
| dump () && dump ("Writing binding table"); |
| dump.indent (); |
| |
| unsigned num = 0; |
| bytes_out sec (to); |
| sec.begin (); |
| |
| for (unsigned ix = 0; ix != sccs.length (); ix++) |
| { |
| depset *b = sccs[ix]; |
| if (b->is_binding ()) |
| { |
| tree ns = b->get_entity (); |
| dump () && dump ("Bindings %P section:%u", ns, b->get_name (), |
| b->section); |
| sec.u (to->name (b->get_name ())); |
| write_namespace (sec, b->deps[0]); |
| sec.u (b->section); |
| num++; |
| } |
| } |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".bnd"), crc_p); |
| dump.outdent (); |
| |
| return num; |
| } |
| |
| /* Read the binding table from MOD_SNAME_PFX.bind. */ |
| |
| bool |
| module_state::read_bindings (unsigned num, unsigned lwm, unsigned hwm) |
| { |
| bytes_in sec; |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".bnd")) |
| return false; |
| |
| dump () && dump ("Reading binding table"); |
| dump.indent (); |
| for (; !sec.get_overrun () && num--;) |
| { |
| const char *name = from ()->name (sec.u ()); |
| tree ns = read_namespace (sec); |
| unsigned snum = sec.u (); |
| |
| if (!ns || !name || (snum - lwm) >= (hwm - lwm)) |
| sec.set_overrun (); |
| if (!sec.get_overrun ()) |
| { |
| tree id = get_identifier (name); |
| dump () && dump ("Bindings %P section:%u", ns, id, snum); |
| if (mod && !import_module_binding (ns, id, mod, snum)) |
| break; |
| } |
| } |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* Write the entity table to MOD_SNAME_PFX.ent |
| |
| Each entry is a section number. */ |
| |
| void |
| module_state::write_entities (elf_out *to, vec<depset *> depsets, |
| unsigned count, unsigned *crc_p) |
| { |
| dump () && dump ("Writing entities"); |
| dump.indent (); |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| unsigned current = 0; |
| for (unsigned ix = 0; ix < depsets.length (); ix++) |
| { |
| depset *d = depsets[ix]; |
| |
| switch (d->get_entity_kind ()) |
| { |
| default: |
| break; |
| |
| case depset::EK_NAMESPACE: |
| if (!d->is_import () && d->get_entity () != global_namespace) |
| { |
| gcc_checking_assert (d->cluster == current); |
| current++; |
| sec.u (0); |
| } |
| break; |
| |
| case depset::EK_DECL: |
| case depset::EK_SPECIALIZATION: |
| case depset::EK_PARTIAL: |
| gcc_checking_assert (!d->is_unreached () |
| && !d->is_import () |
| && d->cluster == current |
| && d->section); |
| current++; |
| sec.u (d->section); |
| break; |
| } |
| } |
| gcc_assert (count == current); |
| sec.end (to, to->name (MOD_SNAME_PFX ".ent"), crc_p); |
| dump.outdent (); |
| } |
| |
| bool |
| module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm) |
| { |
| trees_in sec (this); |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".ent")) |
| return false; |
| |
| dump () && dump ("Reading entities"); |
| dump.indent (); |
| |
| for (binding_slot *slot = entity_ary->begin () + entity_lwm; count--; slot++) |
| { |
| unsigned snum = sec.u (); |
| if (snum && (snum - lwm) >= (hwm - lwm)) |
| sec.set_overrun (); |
| if (sec.get_overrun ()) |
| break; |
| |
| if (snum) |
| slot->set_lazy (snum << 2); |
| } |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* Write the pending table to MOD_SNAME_PFX.pnd |
| |
| The pending table holds information about clusters that need to be |
| loaded because they contain information about something that is not |
| found by namespace-scope lookup. |
| |
| The three cases are: |
| |
| (a) Template (maybe-partial) specializations that we have |
| instantiated or defined. When an importer needs to instantiate |
| that template, they /must have/ the partial, explicit & extern |
| specializations available. If they have the other specializations |
| available, they'll have less work to do. Thus, when we're about to |
| instantiate FOO, we have to be able to ask 'are there any |
| specialization of FOO in our imports?'. |
| |
| (b) (Maybe-implicit) member functions definitions. A class could |
| be defined in one header, and an inline member defined in a |
| different header (this occurs in the STL). Similarly, like the |
| specialization case, an implicit member function could have been |
| 'instantiated' in one module, and it'd be nice to not have to |
| reinstantiate it in another. |
| |
| (c) A member classes completed elsewhere. A member class could be |
| declared in one header and defined in another. We need to know to |
| load the class definition before looking in it. This turns out to |
| be a specific case of #b, so we can treat these the same. But it |
| does highlight an issue -- there could be an intermediate import |
| between the outermost containing namespace-scope class and the |
| innermost being-defined member class. This is actually possible |
| with all of these cases, so be aware -- we're not just talking of |
| one level of import to get to the innermost namespace. |
| |
| This gets complicated fast, it took me multiple attempts to even |
| get something remotely working. Partially because I focussed on |
| optimizing what I think turns out to be a smaller problem, given |
| the known need to do the more general case *anyway*. I document |
| the smaller problem, because it does appear to be the natural way |
| to do it. It's trap! |
| |
| **** THE TRAP |
| |
| Let's refer to the primary template or the containing class as the |
| KEY. And the specialization or member as the PENDING-ENTITY. (To |
| avoid having to say those mouthfuls all the time.) |
| |
| In either case, we have an entity and we need some way of mapping |
| that to a set of entities that need to be loaded before we can |
| proceed with whatever processing of the entity we were going to do. |
| |
| We need to link the key to the pending-entity in some way. Given a |
| key, tell me the pending-entities I need to have loaded. However |
| we tie the key to the pending-entity must not rely on the key being |
| loaded -- that'd defeat the lazy loading scheme. |
| |
| As the key will be an import in we know its entity number (either |
| because we imported it, or we're writing it out too). Thus we can |
| generate a map of key-indices to pending-entities. The |
| pending-entity indices will be into our span of the entity table, |
| and thus allow them to be lazily loaded. The key index will be |
| into another slot of the entity table. Notice that this checking |
| could be expensive, we don't want to iterate over a bunch of |
| pending-entity indices (across multiple imports), every time we're |
| about do to the thing with the key. We need to quickly determine |
| 'definitely nothing needed'. |
| |
| That's almost good enough, except that key indices are not unique |
| in a couple of cases :( Specifically the Global Module or a module |
| partition can result in multiple modules assigning an entity index |
| for the key. The decl-merging on loading will detect that so we |
| only have one Key loaded, and in the entity hash it'll indicate the |
| entity index of first load. Which might be different to how we |
| know it. Notice this is restricted to GM entities or this-module |
| entities. Foreign imports cannot have this. |
| |
| We can simply resolve this in the direction of how this module |
| referred to the key to how the importer knows it. Look in the |
| entity table slot that we nominate, maybe lazy load it, and then |
| lookup the resultant entity in the entity hash to learn how the |
| importer knows it. |
| |
| But we need to go in the other direction :( Given the key, find all |
| the index-aliases of that key. We can partially solve that by |
| adding an alias hash table. Whenever we load a merged decl, add or |
| augment a mapping from the entity (or its entity-index) to the |
| newly-discovered index. Then when we look for pending entities of |
| a key, we also iterate over this aliases this mapping provides. |
| |
| But that requires the alias to be loaded. And that's not |
| necessarily true. |
| |
| *** THE SIMPLER WAY |
| |
| The remaining fixed thing we have is the innermost namespace |
| containing the ultimate namespace-scope container of the key and |
| the name of that container (which might be the key itself). I.e. a |
| namespace-decl/identifier/module tuple. Let's call this the |
| top-key. We'll discover that the module is not important here, |
| because of cross-module possibilities mentioned in case #c above. |
| We can't markup namespace-binding slots. The best we can do is |
| mark the binding vector with 'there's something here', and have |
| another map from namespace/identifier pairs to a vector of pending |
| entity indices. |
| |
| Maintain a pending-entity map. This is keyed by top-key, and |
| maps to a vector of pending-entity indices. On the binding vector |
| have flags saying whether the pending-name-entity map has contents. |
| (We might want to further extend the key to be GM-vs-Partition and |
| specialization-vs-member, but let's not get ahead of ourselves.) |
| |
| For every key-like entity, find the outermost namespace-scope |
| name. Use that to lookup in the pending-entity map and then make |
| sure the specified entities are loaded. |
| |
| An optimization might be to have a flag in each key-entity saying |
| that it's top key might be in the entity table. It's not clear to |
| me how to set that flag cheaply -- cheaper than just looking. |
| |
| FIXME: It'd be nice to have a bit in decls to tell us whether to |
| even try this. We can have a 'already done' flag, that we set when |
| we've done KLASS's lazy pendings. When we import a module that |
| registers pendings on the same top-key as KLASS we need to clear |
| the flag. A recursive walk of the top-key clearing the bit will |
| suffice. Plus we only need to recurse on classes that have the bit |
| set. (That means we need to set the bit on parents of KLASS here, |
| don't forget.) However, first: correctness, second: efficiency. */ |
| |
| unsigned |
| module_state::write_pendings (elf_out *to, vec<depset *> depsets, |
| depset::hash &table, unsigned *crc_p) |
| { |
| dump () && dump ("Writing pending-entities"); |
| dump.indent (); |
| |
| trees_out sec (to, this, table); |
| sec.begin (); |
| |
| unsigned count = 0; |
| tree cache_ns = NULL_TREE; |
| tree cache_id = NULL_TREE; |
| unsigned cache_section = ~0; |
| for (unsigned ix = 0; ix < depsets.length (); ix++) |
| { |
| depset *d = depsets[ix]; |
| |
| if (d->is_binding ()) |
| continue; |
| |
| if (d->is_import ()) |
| continue; |
| |
| if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION |
| || d->get_entity_kind () == depset::EK_PARTIAL |
| || (d->get_entity_kind () == depset::EK_DECL && d->is_member ()))) |
| continue; |
| |
| tree key_decl = nullptr; |
| tree key_ns = find_pending_key (d->get_entity (), &key_decl); |
| tree key_name = DECL_NAME (key_decl); |
| |
| if (IDENTIFIER_ANON_P (key_name)) |
| { |
| gcc_checking_assert (IDENTIFIER_LAMBDA_P (key_name)); |
| if (tree attached = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (key_decl))) |
| key_name = DECL_NAME (attached); |
| else |
| { |
| /* There's nothing to attach it to. Must |
| always reinstantiate. */ |
| dump () |
| && dump ("Unattached lambda %N[%u] section:%u", |
| d->get_entity_kind () == depset::EK_DECL |
| ? "Member" : "Specialization", d->get_entity (), |
| d->cluster, d->section); |
| continue; |
| } |
| } |
| |
| char const *also = ""; |
| if (d->section == cache_section |
| && key_ns == cache_ns |
| && key_name == cache_id) |
| /* Same section & key as previous, no need to repeat ourselves. */ |
| also = "also "; |
| else |
| { |
| cache_ns = key_ns; |
| cache_id = key_name; |
| cache_section = d->section; |
| gcc_checking_assert (table.find_dependency (cache_ns)); |
| sec.tree_node (cache_ns); |
| sec.tree_node (cache_id); |
| sec.u (d->cluster); |
| count++; |
| } |
| dump () && dump ("Pending %s %N entity:%u section:%u %skeyed to %P", |
| d->get_entity_kind () == depset::EK_DECL |
| ? "member" : "specialization", d->get_entity (), |
| d->cluster, cache_section, also, cache_ns, cache_id); |
| } |
| sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p); |
| dump.outdent (); |
| |
| return count; |
| } |
| |
| bool |
| module_state::read_pendings (unsigned count) |
| { |
| trees_in sec (this); |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".pnd")) |
| return false; |
| |
| dump () && dump ("Reading %u pendings", count); |
| dump.indent (); |
| |
| for (unsigned ix = 0; ix != count; ix++) |
| { |
| pending_key key; |
| unsigned index; |
| |
| key.ns = sec.tree_node (); |
| key.id = sec.tree_node (); |
| index = sec.u (); |
| |
| if (!key.ns || !key.id |
| || !(TREE_CODE (key.ns) == NAMESPACE_DECL |
| && !DECL_NAMESPACE_ALIAS (key.ns)) |
| || !identifier_p (key.id) |
| || index >= entity_num) |
| sec.set_overrun (); |
| |
| if (sec.get_overrun ()) |
| break; |
| |
| dump () && dump ("Pending:%u keyed to %P", index, key.ns, key.id); |
| |
| index += entity_lwm; |
| auto &vec = pending_table->get_or_insert (key); |
| vec.safe_push (index); |
| } |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| /* Read & write locations. */ |
| enum loc_kind { |
| LK_ORDINARY, |
| LK_MACRO, |
| LK_IMPORT_ORDINARY, |
| LK_IMPORT_MACRO, |
| LK_ADHOC, |
| LK_RESERVED, |
| }; |
| |
| static const module_state * |
| module_for_ordinary_loc (location_t loc) |
| { |
| unsigned pos = 0; |
| unsigned len = ool->length () - pos; |
| |
| while (len) |
| { |
| unsigned half = len / 2; |
| module_state *probe = (*ool)[pos + half]; |
| if (loc < probe->ordinary_locs.first) |
| len = half; |
| else if (loc < probe->ordinary_locs.second) |
| return probe; |
| else |
| { |
| pos += half + 1; |
| len = len - (half + 1); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| static const module_state * |
| module_for_macro_loc (location_t loc) |
| { |
| unsigned pos = 1; |
| unsigned len = modules->length () - pos; |
| |
| while (len) |
| { |
| unsigned half = len / 2; |
| module_state *probe = (*modules)[pos + half]; |
| if (loc >= probe->macro_locs.second) |
| len = half; |
| else if (loc >= probe->macro_locs.first) |
| return probe; |
| else |
| { |
| pos += half + 1; |
| len = len - (half + 1); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| location_t |
| module_state::imported_from () const |
| { |
| location_t from = loc; |
| line_map_ordinary const *fmap |
| = linemap_check_ordinary (linemap_lookup (line_table, from)); |
| |
| if (MAP_MODULE_P (fmap)) |
| from = linemap_included_from (fmap); |
| |
| return from; |
| } |
| |
| /* If we're not streaming, record that we need location LOC. |
| Otherwise stream it. */ |
| |
| void |
| module_state::write_location (bytes_out &sec, location_t loc) |
| { |
| if (!sec.streaming_p ()) |
| /* This is where we should note we use this location. See comment |
| about write_ordinary_maps. */ |
| return; |
| |
| if (loc < RESERVED_LOCATION_COUNT) |
| { |
| dump (dumper::LOCATION) && dump ("Reserved location %u", unsigned (loc)); |
| sec.u (LK_RESERVED + loc); |
| } |
| else if (IS_ADHOC_LOC (loc)) |
| { |
| dump (dumper::LOCATION) && dump ("Adhoc location"); |
| sec.u (LK_ADHOC); |
| location_t locus = get_location_from_adhoc_loc (line_table, loc); |
| write_location (sec, locus); |
| source_range range = get_range_from_loc (line_table, loc); |
| if (range.m_start == locus) |
| /* Compress. */ |
| range.m_start = UNKNOWN_LOCATION; |
| write_location (sec, range.m_start); |
| write_location (sec, range.m_finish); |
| } |
| else if (loc >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table)) |
| { |
| if (const loc_spans::span *span = spans.macro (loc)) |
| { |
| unsigned off = MAX_LOCATION_T - loc; |
| |
| off -= span->macro_delta; |
| |
| sec.u (LK_MACRO); |
| sec.u (off); |
| dump (dumper::LOCATION) |
| && dump ("Macro location %u output %u", loc, off); |
| } |
| else if (const module_state *import = module_for_macro_loc (loc)) |
| { |
| unsigned off = import->macro_locs.second - loc - 1; |
| sec.u (LK_IMPORT_MACRO); |
| sec.u (import->remap); |
| sec.u (off); |
| dump (dumper::LOCATION) |
| && dump ("Imported macro location %u output %u:%u", |
| loc, import->remap, off); |
| } |
| else |
| gcc_unreachable (); |
| } |
| else if (IS_ORDINARY_LOC (loc)) |
| { |
| if (const loc_spans::span *span = spans.ordinary (loc)) |
| { |
| unsigned off = loc; |
| |
| off += span->ordinary_delta; |
| sec.u (LK_ORDINARY); |
| sec.u (off); |
| |
| dump (dumper::LOCATION) |
| && dump ("Ordinary location %u output %u", loc, off); |
| } |
| else if (const module_state *import = module_for_ordinary_loc (loc)) |
| { |
| unsigned off = loc - import->ordinary_locs.first; |
| sec.u (LK_IMPORT_ORDINARY); |
| sec.u (import->remap); |
| sec.u (off); |
| dump (dumper::LOCATION) |
| && dump ("Imported ordinary location %u output %u:%u", |
| import->remap, import->remap, off); |
| } |
| else |
| gcc_unreachable (); |
| } |
| else |
| gcc_unreachable (); |
| } |
| |
| location_t |
| module_state::read_location (bytes_in &sec) const |
| { |
| location_t locus = UNKNOWN_LOCATION; |
| unsigned kind = sec.u (); |
| switch (kind) |
| { |
| default: |
| { |
| if (kind < LK_RESERVED + RESERVED_LOCATION_COUNT) |
| locus = location_t (kind - LK_RESERVED); |
| else |
| sec.set_overrun (); |
| dump (dumper::LOCATION) |
| && dump ("Reserved location %u", unsigned (locus)); |
| } |
| break; |
| |
| case LK_ADHOC: |
| { |
| dump (dumper::LOCATION) && dump ("Adhoc location"); |
| locus = read_location (sec); |
| source_range range; |
| range.m_start = read_location (sec); |
| if (range.m_start == UNKNOWN_LOCATION) |
| range.m_start = locus; |
| range.m_finish = read_location (sec); |
| if (locus != loc && range.m_start != loc && range.m_finish != loc) |
| locus = get_combined_adhoc_loc (line_table, locus, range, NULL); |
| } |
| break; |
| |
| case LK_MACRO: |
| { |
| unsigned off = sec.u (); |
| |
| if (macro_locs.first) |
| { |
| location_t adjusted = MAX_LOCATION_T - off; |
| adjusted -= slurp->loc_deltas.second; |
| if (adjusted < macro_locs.first) |
| sec.set_overrun (); |
| else if (adjusted < macro_locs.second) |
| locus = adjusted; |
| else |
| sec.set_overrun (); |
| } |
| else |
| locus = loc; |
| dump (dumper::LOCATION) |
| && dump ("Macro %u becoming %u", off, locus); |
| } |
| break; |
| |
| case LK_ORDINARY: |
| { |
| unsigned off = sec.u (); |
| if (ordinary_locs.second) |
| { |
| location_t adjusted = off; |
| |
| adjusted += slurp->loc_deltas.first; |
| if (adjusted >= ordinary_locs.second) |
| sec.set_overrun (); |
| else if (adjusted >= ordinary_locs.first) |
| locus = adjusted; |
| else if (adjusted < spans.main_start ()) |
| locus = off; |
| } |
| else |
| locus = loc; |
| |
| dump (dumper::LOCATION) |
| && dump ("Ordinary location %u becoming %u", off, locus); |
| } |
| break; |
| |
| case LK_IMPORT_MACRO: |
| case LK_IMPORT_ORDINARY: |
| { |
| unsigned mod = sec.u (); |
| unsigned off = sec.u (); |
| const module_state *import = NULL; |
| |
| if (!mod && !slurp->remap) |
| /* This is an early read of a partition location during the |
| read of our ordinary location map. */ |
| import = this; |
| else |
| { |
| mod = slurp->remap_module (mod); |
| if (!mod) |
| sec.set_overrun (); |
| else |
| import = (*modules)[mod]; |
| } |
| |
| if (import) |
| { |
| if (kind == LK_IMPORT_MACRO) |
| { |
| if (!import->macro_locs.first) |
| locus = import->loc; |
| else if (off < import->macro_locs.second - macro_locs.first) |
| locus = import->macro_locs.second - off - 1; |
| else |
| sec.set_overrun (); |
| } |
| else |
| { |
| if (!import->ordinary_locs.second) |
| locus = import->loc; |
| else if (off < (import->ordinary_locs.second |
| - import->ordinary_locs.first)) |
| locus = import->ordinary_locs.first + off; |
| else |
| sec.set_overrun (); |
| } |
| } |
| } |
| break; |
| } |
| |
| return locus; |
| } |
| |
| /* Prepare the span adjustments. */ |
| |
| // FIXME:QOI I do not prune the unreachable locations. Modules with |
| // textually-large GMFs could well cause us to run out of locations. |
| // Regular single-file modules could also be affected. We should |
| // determine which locations we need to represent, so that we do not |
| // grab more locations than necessary. An example is in |
| // write_macro_maps where we work around macro expansions that are not |
| // covering any locations -- the macro expands to nothing. Perhaps we |
| // should decompose locations so that we can have a more graceful |
| // degradation upon running out? |
| |
| location_map_info |
| module_state::write_prepare_maps (module_state_config *) |
| { |
| dump () && dump ("Preparing locations"); |
| dump.indent (); |
| |
| dump () && dump ("Reserved locations [%u,%u) macro [%u,%u)", |
| spans[loc_spans::SPAN_RESERVED].ordinary.first, |
| spans[loc_spans::SPAN_RESERVED].ordinary.second, |
| spans[loc_spans::SPAN_RESERVED].macro.first, |
| spans[loc_spans::SPAN_RESERVED].macro.second); |
| |
| location_map_info info; |
| |
| info.num_maps.first = info.num_maps.second = 0; |
| |
| /* Figure the alignment of ordinary location spans. */ |
| unsigned max_range = 0; |
| for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++) |
| { |
| loc_spans::span &span = spans[ix]; |
| |
| if (span.ordinary.first != span.ordinary.second) |
| { |
| line_map_ordinary const *omap |
| = linemap_check_ordinary (linemap_lookup (line_table, |
| span.ordinary.first)); |
| |
| /* We should exactly match up. */ |
| gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first); |
| |
| line_map_ordinary const *fmap = omap; |
| for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++) |
| { |
| /* We should never find a module linemap in an interval. */ |
| gcc_checking_assert (!MAP_MODULE_P (omap)); |
| |
| if (max_range < omap->m_range_bits) |
| max_range = omap->m_range_bits; |
| } |
| |
| info.num_maps.first += omap - fmap; |
| } |
| |
| if (span.macro.first != span.macro.second) |
| { |
| /* Iterate over the span's macros, to elide the empty |
| expansions. */ |
| unsigned count = 0; |
| for (unsigned macro |
| = linemap_lookup_macro_index (line_table, |
| span.macro.second - 1); |
| macro < LINEMAPS_MACRO_USED (line_table); |
| macro++) |
| { |
| line_map_macro const *mmap |
| = LINEMAPS_MACRO_MAP_AT (line_table, macro); |
| if (MAP_START_LOCATION (mmap) < span.macro.first) |
| /* Fallen out of the span. */ |
| break; |
| |
| if (mmap->n_tokens) |
| count++; |
| } |
| dump (dumper::LOCATION) && dump ("Span:%u %u macro maps", ix, count); |
| info.num_maps.second += count; |
| } |
| } |
| |
| /* Adjust the maps. Ordinary ones ascend, and we must maintain |
| alignment. Macro ones descend, but are unaligned. */ |
| location_t ord_off = spans[loc_spans::SPAN_FIRST].ordinary.first; |
| location_t mac_off = spans[loc_spans::SPAN_FIRST].macro.second; |
| location_t range_mask = (1u << max_range) - 1; |
| |
| dump () && dump ("Ordinary maps range bits:%u, preserve:%x, zero:%u", |
| max_range, ord_off & range_mask, ord_off & ~range_mask); |
| |
| for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++) |
| { |
| loc_spans::span &span = spans[ix]; |
| |
| span.macro_delta = mac_off - span.macro.second; |
| mac_off -= span.macro.second - span.macro.first; |
| dump () && dump ("Macro span:%u [%u,%u):%u->%d(%u)", ix, |
| span.macro.first, span.macro.second, |
| span.macro.second - span.macro.first, |
| span.macro_delta, span.macro.first + span.macro_delta); |
| |
| line_map_ordinary const *omap |
| = linemap_check_ordinary (linemap_lookup (line_table, |
| span.ordinary.first)); |
| location_t base = MAP_START_LOCATION (omap); |
| |
| /* Preserve the low MAX_RANGE bits of base by incrementing ORD_OFF. */ |
| unsigned low_bits = base & range_mask; |
| if ((ord_off & range_mask) > low_bits) |
| low_bits += range_mask + 1; |
| ord_off = (ord_off & ~range_mask) + low_bits; |
| span.ordinary_delta = ord_off - base; |
| |
| for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++) |
| { |
| location_t start_loc = MAP_START_LOCATION (omap); |
| unsigned to = start_loc + span.ordinary_delta; |
| location_t end_loc = MAP_START_LOCATION (omap + 1); |
| |
| dump () && dump ("Ordinary span:%u [%u,%u):%u->%d(%u)", |
| ix, start_loc, |
| end_loc, end_loc - start_loc, |
| span.ordinary_delta, to); |
| |
| /* There should be no change in the low order bits. */ |
| gcc_checking_assert (((start_loc ^ to) & range_mask) == 0); |
| } |
| |
| /* The ending serialized value. */ |
| ord_off = span.ordinary.second + span.ordinary_delta; |
| } |
| |
| dump () && dump ("Ordinary:%u maps hwm:%u macro:%u maps lwm:%u ", |
| info.num_maps.first, ord_off, |
| info.num_maps.second, mac_off); |
| |
| dump.outdent (); |
| |
| info.max_range = max_range; |
| |
| return info; |
| } |
| |
| bool |
| module_state::read_prepare_maps (const module_state_config *cfg) |
| { |
| location_t ordinary = line_table->highest_location + 1; |
| ordinary = ((ordinary + (1u << cfg->ordinary_loc_align)) |
| & ~((1u << cfg->ordinary_loc_align) - 1)); |
| ordinary += cfg->ordinary_locs; |
| |
| location_t macro = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); |
| macro -= cfg->macro_locs; |
| |
| if (ordinary < LINE_MAP_MAX_LOCATION_WITH_COLS |
| && macro >= LINE_MAP_MAX_LOCATION) |
| /* OK, we have enough locations. */ |
| return true; |
| |
| ordinary_locs.first = ordinary_locs.second = 0; |
| macro_locs.first = macro_locs.second = 0; |
| |
| static bool informed = false; |
| if (!informed) |
| { |
| /* Just give the notice once. */ |
| informed = true; |
| inform (loc, "unable to represent further imported source locations"); |
| } |
| |
| return false; |
| } |
| |
| /* Write the location maps. This also determines the shifts for the |
| location spans. */ |
| |
| void |
| module_state::write_ordinary_maps (elf_out *to, location_map_info &info, |
| module_state_config *cfg, bool has_partitions, |
| unsigned *crc_p) |
| { |
| dump () && dump ("Writing ordinary location maps"); |
| dump.indent (); |
| |
| vec<const char *> filenames; |
| filenames.create (20); |
| |
| /* Determine the unique filenames. */ |
| // FIXME:QOI We should find the set of filenames when working out |
| // which locations we actually need. See write_prepare_maps. |
| for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++) |
| { |
| loc_spans::span &span = spans[ix]; |
| line_map_ordinary const *omap |
| = linemap_check_ordinary (linemap_lookup (line_table, |
| span.ordinary.first)); |
| |
| /* We should exactly match up. */ |
| gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first); |
| |
| for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++) |
| { |
| const char *fname = ORDINARY_MAP_FILE_NAME (omap); |
| |
| /* We should never find a module linemap in an interval. */ |
| gcc_checking_assert (!MAP_MODULE_P (omap)); |
| |
| /* We expect very few filenames, so just an array. |
| (Not true when headers are still in play :() */ |
| for (unsigned jx = filenames.length (); jx--;) |
| { |
| const char *name = filenames[jx]; |
| if (0 == strcmp (name, fname)) |
| { |
| /* Reset the linemap's name, because for things like |
| preprocessed input we could have multiple |
| instances of the same name, and we'd rather not |
| percolate that. */ |
| const_cast<line_map_ordinary *> (omap)->to_file = name; |
| fname = NULL; |
| break; |
| } |
| } |
| if (fname) |
| filenames.safe_push (fname); |
| } |
| } |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| /* Write the filenames. */ |
| unsigned len = filenames.length (); |
| sec.u (len); |
| dump () && dump ("%u source file names", len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| const char *fname = filenames[ix]; |
| dump (dumper::LOCATION) && dump ("Source file[%u]=%s", ix, fname); |
| sec.str (fname); |
| } |
| |
| location_t offset = spans[loc_spans::SPAN_FIRST].ordinary.first; |
| location_t range_mask = (1u << info.max_range) - 1; |
| |
| dump () && dump ("Ordinary maps:%u, range bits:%u, preserve:%x, zero:%u", |
| info.num_maps.first, info.max_range, offset & range_mask, |
| offset & ~range_mask); |
| sec.u (info.num_maps.first); /* Num maps. */ |
| sec.u (info.max_range); /* Maximum range bits */ |
| sec.u (offset & range_mask); /* Bits to preserve. */ |
| sec.u (offset & ~range_mask); |
| |
| for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++) |
| { |
| loc_spans::span &span = spans[ix]; |
| line_map_ordinary const *omap |
| = linemap_check_ordinary (linemap_lookup (line_table, |
| span.ordinary.first)); |
| for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++) |
| { |
| location_t start_loc = MAP_START_LOCATION (omap); |
| unsigned to = start_loc + span.ordinary_delta; |
| |
| dump (dumper::LOCATION) |
| && dump ("Span:%u ordinary [%u,%u)->%u", ix, start_loc, |
| MAP_START_LOCATION (omap + 1), to); |
| |
| /* There should be no change in the low order bits. */ |
| gcc_checking_assert (((start_loc ^ to) & range_mask) == 0); |
| sec.u (to); |
| |
| /* Making accessors just for here, seems excessive. */ |
| sec.u (omap->reason); |
| sec.u (omap->sysp); |
| sec.u (omap->m_range_bits); |
| sec.u (omap->m_column_and_range_bits - omap->m_range_bits); |
| |
| const char *fname = ORDINARY_MAP_FILE_NAME (omap); |
| for (unsigned ix = 0; ix != filenames.length (); ix++) |
| if (filenames[ix] == fname) |
| { |
| sec.u (ix); |
| break; |
| } |
| sec.u (ORDINARY_MAP_STARTING_LINE_NUMBER (omap)); |
| |
| /* Write the included from location, which means reading it |
| while reading in the ordinary maps. So we'd better not |
| be getting ahead of ourselves. */ |
| location_t from = linemap_included_from (omap); |
| gcc_checking_assert (from < MAP_START_LOCATION (omap)); |
| if (from != UNKNOWN_LOCATION && has_partitions) |
| { |
| /* A partition's span will have a from pointing at a |
| MODULE_INC. Find that map's from. */ |
| line_map_ordinary const *fmap |
| = linemap_check_ordinary (linemap_lookup (line_table, from)); |
| if (MAP_MODULE_P (fmap)) |
| from = linemap_included_from (fmap); |
| } |
| write_location (sec, from); |
| } |
| /* The ending serialized value. */ |
| offset = MAP_START_LOCATION (omap) + span.ordinary_delta; |
| } |
| dump () && dump ("Ordinary location hwm:%u", offset); |
| sec.u (offset); |
| |
| // Record number of locations and alignment. |
| cfg->ordinary_loc_align = info.max_range; |
| cfg->ordinary_locs = offset; |
| |
| filenames.release (); |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".olm"), crc_p); |
| dump.outdent (); |
| } |
| |
| void |
| module_state::write_macro_maps (elf_out *to, location_map_info &info, |
| module_state_config *cfg, unsigned *crc_p) |
| { |
| dump () && dump ("Writing macro location maps"); |
| dump.indent (); |
| |
| bytes_out sec (to); |
| sec.begin (); |
| |
| dump () && dump ("Macro maps:%u", info.num_maps.second); |
| sec.u (info.num_maps.second); |
| |
| location_t offset = spans[loc_spans::SPAN_FIRST].macro.second; |
| sec.u (offset); |
| |
| unsigned macro_num = 0; |
| for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++) |
| { |
| loc_spans::span &span = spans[ix]; |
| if (span.macro.first == span.macro.second) |
| /* Empty span. */ |
| continue; |
| |
| for (unsigned macro |
| = linemap_lookup_macro_index (line_table, span.macro.second - 1); |
| macro < LINEMAPS_MACRO_USED (line_table); |
| macro++) |
| { |
| line_map_macro const *mmap |
| = LINEMAPS_MACRO_MAP_AT (line_table, macro); |
| location_t start_loc = MAP_START_LOCATION (mmap); |
| if (start_loc < span.macro.first) |
| /* Fallen out of the span. */ |
| break; |
| |
| if (!mmap->n_tokens) |
| /* Empty expansion. */ |
| continue; |
| |
| sec.u (offset); |
| sec.u (mmap->n_tokens); |
| sec.cpp_node (mmap->macro); |
| write_location (sec, mmap->expansion); |
| const location_t *locs = mmap->macro_locations; |
| /* There are lots of identical runs. */ |
| location_t prev = UNKNOWN_LOCATION; |
| unsigned count = 0; |
| unsigned runs = 0; |
| for (unsigned jx = mmap->n_tokens * 2; jx--;) |
| { |
| location_t tok_loc = locs[jx]; |
| if (tok_loc == prev) |
| { |
| count++; |
| continue; |
| } |
| runs++; |
| sec.u (count); |
| count = 1; |
| prev = tok_loc; |
| write_location (sec, tok_loc); |
| } |
| sec.u (count); |
| dump (dumper::LOCATION) |
| && dump ("Span:%u macro:%u %I %u/%u*2 locations [%u,%u)->%u", |
| ix, macro_num, identifier (mmap->macro), |
| runs, mmap->n_tokens, |
| start_loc, start_loc + mmap->n_tokens, |
| start_loc + span.macro_delta); |
| macro_num++; |
| offset -= mmap->n_tokens; |
| gcc_checking_assert (offset == start_loc + span.macro_delta); |
| } |
| } |
| dump () && dump ("Macro location lwm:%u", offset); |
| sec.u (offset); |
| gcc_assert (macro_num == info.num_maps.second); |
| |
| cfg->macro_locs = MAX_LOCATION_T + 1 - offset; |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".mlm"), crc_p); |
| dump.outdent (); |
| } |
| |
| bool |
| module_state::read_ordinary_maps () |
| { |
| bytes_in sec; |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".olm")) |
| return false; |
| dump () && dump ("Reading ordinary location maps"); |
| dump.indent (); |
| |
| /* Read the filename table. */ |
| unsigned len = sec.u (); |
| dump () && dump ("%u source file names", len); |
| vec<const char *> filenames; |
| filenames.create (len); |
| for (unsigned ix = 0; ix != len; ix++) |
| { |
| size_t l; |
| const char *buf = sec.str (&l); |
| char *fname = XNEWVEC (char, l + 1); |
| memcpy (fname, buf, l + 1); |
| dump (dumper::LOCATION) && dump ("Source file[%u]=%s", ix, fname); |
| /* We leak these names into the line-map table. But it |
| doesn't own them. */ |
| filenames.quick_push (fname); |
| } |
| |
| unsigned num_ordinary = sec.u (); |
| unsigned max_range = sec.u (); |
| unsigned low_bits = sec.u (); |
| location_t zero = sec.u (); |
| location_t range_mask = (1u << max_range) - 1; |
| |
| dump () && dump ("Ordinary maps:%u, range bits:%u, preserve:%x, zero:%u", |
| num_ordinary, max_range, low_bits, zero); |
| |
| location_t offset = line_table->highest_location + 1; |
| /* Ensure offset doesn't go backwards at the start. */ |
| if ((offset & range_mask) > low_bits) |
| offset += range_mask + 1; |
| offset = (offset & ~range_mask); |
| |
| bool propagated = spans.maybe_propagate (this, offset + low_bits); |
| |
| line_map_ordinary *maps = static_cast<line_map_ordinary *> |
| (line_map_new_raw (line_table, false, num_ordinary)); |
| |
| location_t lwm = offset; |
| slurp->loc_deltas.first = offset - zero; |
| ordinary_locs.first = zero + low_bits + slurp->loc_deltas.first; |
| dump () && dump ("Ordinary loc delta %d", slurp->loc_deltas.first); |
| |
| for (unsigned ix = 0; ix != num_ordinary && !sec.get_overrun (); ix++) |
| { |
| line_map_ordinary *map = &maps[ix]; |
| unsigned hwm = sec.u (); |
| |
| /* Record the current HWM so that the below read_location is |
| ok. */ |
| ordinary_locs.second = hwm + slurp->loc_deltas.first; |
| map->start_location = hwm + (offset - zero); |
| if (map->start_location < lwm) |
| sec.set_overrun (); |
| lwm = map->start_location; |
| dump (dumper::LOCATION) && dump ("Map:%u %u->%u", ix, hwm, lwm); |
| map->reason = lc_reason (sec.u ()); |
| map->sysp = sec.u (); |
| map->m_range_bits = sec.u (); |
| map->m_column_and_range_bits = map->m_range_bits + sec.u (); |
| |
| unsigned fnum = sec.u (); |
| map->to_file = (fnum < filenames.length () ? filenames[fnum] : ""); |
| map->to_line = sec.u (); |
| |
| /* Root the outermost map at our location. */ |
| location_t from = read_location (sec); |
| map->included_from = from != UNKNOWN_LOCATION ? from : loc; |
| } |
| |
| location_t hwm = sec.u (); |
| ordinary_locs.second = hwm + slurp->loc_deltas.first; |
| |
| /* highest_location is the one handed out, not the next one to |
| hand out. */ |
| line_table->highest_location = ordinary_locs.second - 1; |
| |
| if (line_table->highest_location >= LINE_MAP_MAX_LOCATION_WITH_COLS) |
| /* We shouldn't run out of locations, as we checked before |
| starting. */ |
| sec.set_overrun (); |
| dump () && dump ("Ordinary location hwm:%u", ordinary_locs.second); |
| |
| if (propagated) |
| spans.close (); |
| |
| filenames.release (); |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| |
| return true; |
| } |
| |
| bool |
| module_state::read_macro_maps () |
| { |
| bytes_in sec; |
| |
| if (!sec.begin (loc, from (), MOD_SNAME_PFX ".mlm")) |
| return false; |
| dump () && dump ("Reading macro location maps"); |
| dump.indent (); |
| |
| unsigned num_macros = sec.u (); |
| location_t zero = sec.u (); |
| dump () && dump ("Macro maps:%u zero:%u", num_macros, zero); |
| |
| bool propagated = spans.maybe_propagate (this, |
| line_table->highest_location + 1); |
| |
| location_t offset = LINEMAPS_MACRO_LOWEST_LOCATION (line_table); |
| slurp->loc_deltas.second = zero - offset; |
| macro_locs.second = zero - slurp->loc_deltas.second; |
| dump () && dump ("Macro loc delta %d", slurp->loc_deltas.second); |
| |
| for (unsigned ix = 0; ix != num_macros && !sec.get_overrun (); ix++) |
| { |
| unsigned lwm = sec.u (); |
| /* Record the current LWM so that the below read_location is |
| ok. */ |
| macro_locs.first = lwm - slurp->loc_deltas.second; |
| |
| unsigned n_tokens = sec.u (); |
| cpp_hashnode *node = sec.cpp_node (); |
| location_t exp_loc = read_location (sec); |
| |
| const line_map_macro *macro |
| = linemap_enter_macro (line_table, node, exp_loc, n_tokens); |
| if (!macro) |
| /* We shouldn't run out of locations, as we checked that we |
| had enough before starting. */ |
| break; |
| |
| location_t *locs = macro->macro_locations; |
| location_t tok_loc = UNKNOWN_LOCATION; |
| unsigned count = sec.u (); |
| unsigned runs = 0; |
| for (unsigned jx = macro->n_tokens * 2; jx-- && !sec.get_overrun ();) |
| { |
| while (!count-- && !sec.get_overrun ()) |
| { |
| runs++; |
| tok_loc = read_location (sec); |
| count = sec.u (); |
| } |
| locs[jx] = tok_loc; |
| } |
| if (count) |
| sec.set_overrun (); |
| dump (dumper::LOCATION) |
| && dump ("Macro:%u %I %u/%u*2 locations [%u,%u)", |
| ix, identifier (node), runs, n_tokens, |
| MAP_START_LOCATION (macro), |
| MAP_START_LOCATION (macro) + n_tokens); |
| } |
| location_t lwm = sec.u (); |
| macro_locs.first = lwm - slurp->loc_deltas.second; |
| |
| dump () && dump ("Macro location lwm:%u", macro_locs.first); |
| |
| if (propagated) |
| spans.close (); |
| |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| |
| return true; |
| } |
| |
| /* Serialize the definition of MACRO. */ |
| |
| void |
| module_state::write_define (bytes_out &sec, const cpp_macro *macro, bool located) |
| { |
| sec.u (macro->count); |
| |
| sec.b (macro->fun_like); |
| sec.b (macro->variadic); |
| sec.b (macro->syshdr); |
| sec.bflush (); |
| |
| if (located) |
| write_location (sec, macro->line); |
| if (macro->fun_like) |
| { |
| sec.u (macro->paramc); |
| const cpp_hashnode *const *parms = macro->parm.params; |
| for (unsigned ix = 0; ix != macro->paramc; ix++) |
| sec.cpp_node (parms[ix]); |
| } |
| |
| unsigned len = 0; |
| for (unsigned ix = 0; ix != macro->count; ix++) |
| { |
| const cpp_token *token = ¯o->exp.tokens[ix]; |
| if (located) |
| write_location (sec, token->src_loc); |
| sec.u (token->type); |
| sec.u (token->flags); |
| switch (cpp_token_val_index (token)) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case CPP_TOKEN_FLD_ARG_NO: |
| /* An argument reference. */ |
| sec.u (token->val.macro_arg.arg_no); |
| sec.cpp_node (token->val.macro_arg.spelling); |
| break; |
| |
| case CPP_TOKEN_FLD_NODE: |
| /* An identifier. */ |
| sec.cpp_node (token->val.node.node); |
| if (token->val.node.spelling == token->val.node.node) |
| /* The spelling will usually be the same. so optimize |
| that. */ |
| sec.str (NULL, 0); |
| else |
| sec.cpp_node (token->val.node.spelling); |
| break; |
| |
| case CPP_TOKEN_FLD_NONE: |
| break; |
| |
| case CPP_TOKEN_FLD_STR: |
| /* A string, number or comment. Not always NUL terminated, |
| we stream out in a single contatenation with embedded |
| NULs as that's a safe default. */ |
| len += token->val.str.len + 1; |
| sec.u (token->val.str.len); |
| break; |
| |
| case CPP_TOKEN_FLD_SOURCE: |
| case CPP_TOKEN_FLD_TOKEN_NO: |
| case CPP_TOKEN_FLD_PRAGMA: |
| /* These do not occur inside a macro itself. */ |
| gcc_unreachable (); |
| } |
| } |
| |
| if (len) |
| { |
| char *ptr = reinterpret_cast<char *> (sec.buf (len)); |
| len = 0; |
| for (unsigned ix = 0; ix != macro->count; ix++) |
| { |
| const cpp_token *token = ¯o->exp.tokens[ix]; |
| if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR) |
| { |
| memcpy (ptr + len, token->val.str.text, |
| token->val.str.len); |
| len += token->val.str.len; |
| ptr[len++] = 0; |
| } |
| } |
| } |
| } |
| |
| /* Read a macro definition. */ |
| |
| cpp_macro * |
| module_state::read_define (bytes_in &sec, cpp_reader *reader, bool located) const |
| { |
| unsigned count = sec.u (); |
| /* We rely on knowing cpp_reader's hash table is ident_hash, and |
| it's subobject allocator is stringpool_ggc_alloc and that is just |
| a wrapper for ggc_alloc_atomic. */ |
| cpp_macro *macro |
| = (cpp_macro *)ggc_alloc_atomic (sizeof (cpp_macro) |
| + sizeof (cpp_token) * (count - !!count)); |
| memset (macro, 0, sizeof (cpp_macro) + sizeof (cpp_token) * (count - !!count)); |
| |
| macro->count = count; |
| macro->kind = cmk_macro; |
| macro->imported_p = true; |
| |
| macro->fun_like = sec.b (); |
| macro->variadic = sec.b (); |
| macro->syshdr = sec.b (); |
| sec.bflush (); |
| |
| macro->line = located ? read_location (sec) : loc; |
| |
| if (macro->fun_like) |
| { |
| unsigned paramc = sec.u (); |
| cpp_hashnode **params |
| = (cpp_hashnode **)ggc_alloc_atomic (sizeof (cpp_hashnode *) * paramc); |
| macro->paramc = paramc; |
| macro->parm.params = params; |
| for (unsigned ix = 0; ix != paramc; ix++) |
| params[ix] = sec.cpp_node (); |
| } |
| |
| unsigned len = 0; |
| for (unsigned ix = 0; ix != count && !sec.get_overrun (); ix++) |
| { |
| cpp_token *token = ¯o->exp.tokens[ix]; |
| token->src_loc = located ? read_location (sec) : loc; |
| token->type = cpp_ttype (sec.u ()); |
| token->flags = sec.u (); |
| switch (cpp_token_val_index (token)) |
| { |
| default: |
| sec.set_overrun (); |
| break; |
| |
| case CPP_TOKEN_FLD_ARG_NO: |
| /* An argument reference. */ |
| { |
| unsigned arg_no = sec.u (); |
| if (arg_no - 1 >= macro->paramc) |
| sec.set_overrun (); |
| token->val.macro_arg.arg_no = arg_no; |
| token->val.macro_arg.spelling = sec.cpp_node (); |
| } |
| break; |
| |
| case CPP_TOKEN_FLD_NODE: |
| /* An identifier. */ |
| token->val.node.node = sec.cpp_node (); |
| token->val.node.spelling = sec.cpp_node (); |
| if (!token->val.node.spelling) |
| token->val.node.spelling = token->val.node.node; |
| break; |
| |
| case CPP_TOKEN_FLD_NONE: |
| break; |
| |
| case CPP_TOKEN_FLD_STR: |
| /* A string, number or comment. */ |
| token->val.str.len = sec.u (); |
| len += token->val.str.len + 1; |
| break; |
| } |
| } |
| |
| if (len) |
| if (const char *ptr = reinterpret_cast<const char *> (sec.buf (len))) |
| { |
| /* There should be a final NUL. */ |
| if (ptr[len-1]) |
| sec.set_overrun (); |
| /* cpp_alloc_token_string will add a final NUL. */ |
| const unsigned char *buf |
| = cpp_alloc_token_string (reader, (const unsigned char *)ptr, len - 1); |
| len = 0; |
| for (unsigned ix = 0; ix != count && !sec.get_overrun (); ix++) |
| { |
| cpp_token *token = ¯o->exp.tokens[ix]; |
| if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR) |
| { |
| token->val.str.text = buf + len; |
| len += token->val.str.len; |
| if (buf[len++]) |
| sec.set_overrun (); |
| } |
| } |
| } |
| |
| if (sec.get_overrun ()) |
| return NULL; |
| return macro; |
| } |
| |
| /* Exported macro data. */ |
| struct GTY(()) macro_export { |
| cpp_macro *def; |
| location_t undef_loc; |
| |
| macro_export () |
| :def (NULL), undef_loc (UNKNOWN_LOCATION) |
| { |
| } |
| }; |
| |
| /* Imported macro data. */ |
| class macro_import { |
| public: |
| struct slot { |
| #if defined (WORDS_BIGENDIAN) && SIZEOF_VOID_P == 8 |
| int offset; |
| #endif |
| /* We need to ensure we don't use the LSB for representation, as |
| that's the union discriminator below. */ |
| unsigned bits; |
| |
| #if !(defined (WORDS_BIGENDIAN) && SIZEOF_VOID_P == 8) |
| int offset; |
| #endif |
| |
| public: |
| enum Layout { |
| L_DEF = 1, |
| L_UNDEF = 2, |
| L_BOTH = 3, |
| L_MODULE_SHIFT = 2 |
| }; |
| |
| public: |
| /* Not a regular ctor, because we put it in a union, and that's |
| not allowed in C++ 98. */ |
| static slot ctor (unsigned module, unsigned defness) |
| { |
| gcc_checking_assert (defness); |
| slot s; |
| s.bits = defness | (module << L_MODULE_SHIFT); |
| s.offset = -1; |
| return s; |
| } |
| |
| public: |
| unsigned get_defness () const |
| { |
| return bits & L_BOTH; |
| } |
| unsigned get_module () const |
| { |
| return bits >> L_MODULE_SHIFT; |
| } |
| void become_undef () |
| { |
| bits &= ~unsigned (L_DEF); |
| bits |= unsigned (L_UNDEF); |
| } |
| }; |
| |
| private: |
| typedef vec<slot, va_heap, vl_embed> ary_t; |
| union either { |
| /* Discriminated by bits 0|1 != 0. The expected case is that |
| there will be exactly one slot per macro, hence the effort of |
| packing that. */ |
| ary_t *ary; |
| slot single; |
| } u; |
| |
| public: |
| macro_import () |
| { |
| u.ary = NULL; |
| } |
| |
| private: |
| bool single_p () const |
| { |
| return u.single.bits & slot::L_BOTH; |
| } |
| bool occupied_p () const |
| { |
| return u.ary != NULL; |
| } |
| |
| public: |
| unsigned length () const |
| { |
| gcc_checking_assert (occupied_p ()); |
| return single_p () ? 1 : u.ary->length (); |
| } |
| slot &operator[] (unsigned ix) |
| { |
| gcc_checking_assert (occupied_p ()); |
| if (single_p ()) |
| { |
| gcc_checking_assert (!ix); |
| return u.single; |
| } |
| else |
| return (*u.ary)[ix]; |
| } |
| |
| public: |
| slot &exported (); |
| slot &append (unsigned module, unsigned defness); |
| }; |
| |
| /* O is a new import to append to the list for. If we're an empty |
| set, initialize us. */ |
| |
| macro_import::slot & |
| macro_import::append (unsigned module, unsigned defness) |
| { |
| if (!occupied_p ()) |
| { |
| u.single = slot::ctor (module, defness); |
| return u.single; |
| } |
| else |
| { |
| bool single = single_p (); |
| ary_t *m = single ? NULL : u.ary; |
| vec_safe_reserve (m, 1 + single); |
| if (single) |
| m->quick_push (u.single); |
| u.ary = m; |
| return *u.ary->quick_push (slot::ctor (module, defness)); |
| } |
| } |
| |
| /* We're going to export something. Make sure the first import slot |
| is us. */ |
| |
| macro_import::slot & |
| macro_import::exported () |
| { |
| if (occupied_p () && !(*this)[0].get_module ()) |
| { |
| slot &res = (*this)[0]; |
| res.bits |= slot::L_DEF; |
| return res; |
| } |
| |
| slot *a = &append (0, slot::L_DEF); |
| if (!single_p ()) |
| { |
| slot &f = (*this)[0]; |
| std::swap (f, *a); |
| a = &f; |
| } |
| return *a; |
| } |
| |
| /* The import (&exported) macros. cpp_hasnode's deferred field |
| indexes this array (offset by 1, so zero means 'not present'. */ |
| |
| static vec<macro_import, va_heap, vl_embed> *macro_imports; |
| |
| /* The exported macros. A macro_import slot's zeroth element's offset |
| indexes this array. If the zeroth slot is not for module zero, |
| there is no export. */ |
| |
| static GTY(()) vec<macro_export, va_gc> *macro_exports; |
| |
| /* The reachable set of header imports from this TU. */ |
| |
| static GTY(()) bitmap headers; |
| |
| /* Get the (possibly empty) macro imports for NODE. */ |
| |
| static macro_import & |
| get_macro_imports (cpp_hashnode *node) |
| { |
| if (node->deferred) |
| return (*macro_imports)[node->deferred - 1]; |
| |
| vec_safe_reserve (macro_imports, 1); |
| node->deferred = macro_imports->length () + 1; |
| return *vec_safe_push (macro_imports, macro_import ()); |
| } |
| |
| /* Get the macro export for export EXP of NODE. */ |
| |
| static macro_export & |
| get_macro_export (macro_import::slot &slot) |
| { |
| if (slot.offset >= 0) |
| return (*macro_exports)[slot.offset]; |
| |
| vec_safe_reserve (macro_exports, 1); |
| slot.offset = macro_exports->length (); |
| return *macro_exports->quick_push (macro_export ()); |
| } |
| |
| /* If NODE is an exportable macro, add it to the export set. */ |
| |
| static int |
| maybe_add_macro (cpp_reader *, cpp_hashnode *node, void *data_) |
| { |
| bool exporting = false; |
| |
| if (cpp_user_macro_p (node)) |
| if (cpp_macro *macro = node->value.macro) |
| /* Ignore imported, builtins, command line and forced header macros. */ |
| if (!macro->imported_p |
| && !macro->lazy && macro->line >= spans.main_start ()) |
| { |
| gcc_checking_assert (macro->kind == cmk_macro); |
| /* I don't want to deal with this corner case, that I suspect is |
| a devil's advocate reading of the standard. */ |
| gcc_checking_assert (!macro->extra_tokens); |
| |
| macro_import::slot &slot = get_macro_imports (node).exported (); |
| macro_export &exp = get_macro_export (slot); |
| exp.def = macro; |
| exporting = true; |
| } |
| |
| if (!exporting && node->deferred) |
| { |
| macro_import &imports = (*macro_imports)[node->deferred - 1]; |
| macro_import::slot &slot = imports[0]; |
| if (!slot.get_module ()) |
| { |
| gcc_checking_assert (slot.get_defness ()); |
| exporting = true; |
| } |
| } |
| |
| if (exporting) |
| static_cast<vec<cpp_hashnode *> *> (data_)->safe_push (node); |
| |
| return 1; /* Don't stop. */ |
| } |
| |
| /* Order cpp_hashnodes A_ and B_ by their exported macro locations. */ |
| |
| static int |
| macro_loc_cmp (const void *a_, const void *b_) |
| { |
| const cpp_hashnode *node_a = *(const cpp_hashnode *const *)a_; |
| macro_import &import_a = (*macro_imports)[node_a->deferred - 1]; |
| const macro_export &export_a = (*macro_exports)[import_a[0].offset]; |
| location_t loc_a = export_a.def ? export_a.def->line : export_a.undef_loc; |
| |
| const cpp_hashnode *node_b = *(const cpp_hashnode *const *)b_; |
| macro_import &import_b = (*macro_imports)[node_b->deferred - 1]; |
| const macro_export &export_b = (*macro_exports)[import_b[0].offset]; |
| location_t loc_b = export_b.def ? export_b.def->line : export_b.undef_loc; |
| |
| if (loc_a < loc_b) |
| return +1; |
| else if (loc_a > loc_b) |
| return -1; |
| else |
| return 0; |
| } |
| |
| /* Write out the exported defines. This is two sections, one |
| containing the definitions, the other a table of node names. */ |
| |
| unsigned |
| module_state::write_macros (elf_out *to, cpp_reader *reader, unsigned *crc_p) |
| { |
| dump () && dump ("Writing macros"); |
| dump.indent (); |
| |
| vec<cpp_hashnode *> macros; |
| macros.create (100); |
| cpp_forall_identifiers (reader, maybe_add_macro, ¯os); |
| |
| dump (dumper::MACRO) && dump ("No more than %u macros", macros.length ()); |
| |
| macros.qsort (macro_loc_cmp); |
| |
| /* Write the defs */ |
| bytes_out sec (to); |
| sec.begin (); |
| |
| unsigned count = 0; |
| for (unsigned ix = macros.length (); ix--;) |
| { |
| cpp_hashnode *node = macros[ix]; |
| macro_import::slot &slot = (*macro_imports)[node->deferred - 1][0]; |
| gcc_assert (!slot.get_module () && slot.get_defness ()); |
| |
| macro_export &mac = (*macro_exports)[slot.offset]; |
| gcc_assert (!!(slot.get_defness () & macro_import::slot::L_UNDEF) |
| == (mac.undef_loc != UNKNOWN_LOCATION) |
| && !!(slot.get_defness () & macro_import::slot::L_DEF) |
| == (mac.def != NULL)); |
| |
| if (IDENTIFIER_KEYWORD_P (identifier (node))) |
| { |
| warning_at (mac.def->line, 0, |
| "not exporting %<#define %E%> as it is a keyword", |
| identifier (node)); |
| slot.offset = 0; |
| continue; |
| } |
| |
| count++; |
| slot.offset = sec.pos; |
| dump (dumper::MACRO) |
| && dump ("Writing macro %s%s%s %I at %u", |
| slot.get_defness () & macro_import::slot::L_UNDEF |
| ? "#undef" : "", |
| slot.get_defness () == macro_import::slot::L_BOTH |
| ? " & " : "", |
| slot.get_defness () & macro_import::slot::L_DEF |
| ? "#define" : "", |
| identifier (node), slot.offset); |
| if (mac.undef_loc != UNKNOWN_LOCATION) |
| write_location (sec, mac.undef_loc); |
| if (mac.def) |
| write_define (sec, mac.def); |
| } |
| sec.end (to, to->name (MOD_SNAME_PFX ".def"), crc_p); |
| |
| if (count) |
| { |
| /* Write the table. */ |
| bytes_out sec (to); |
| sec.begin (); |
| sec.u (count); |
| |
| for (unsigned ix = macros.length (); ix--;) |
| { |
| const cpp_hashnode *node = macros[ix]; |
| macro_import::slot &slot = (*macro_imports)[node->deferred - 1][0]; |
| |
| if (slot.offset) |
| { |
| sec.cpp_node (node); |
| sec.u (slot.get_defness ()); |
| sec.u (slot.offset); |
| } |
| } |
| sec.end (to, to->name (MOD_SNAME_PFX ".mac"), crc_p); |
| } |
| |
| macros.release (); |
| dump.outdent (); |
| return count; |
| } |
| |
| bool |
| module_state::read_macros () |
| { |
| /* Get the def section. */ |
| if (!slurp->macro_defs.begin (loc, from (), MOD_SNAME_PFX ".def")) |
| return false; |
| |
| /* Get the tbl section, if there are defs. */ |
| if (slurp->macro_defs.more_p () |
| && !slurp->macro_tbl.begin (loc, from (), MOD_SNAME_PFX ".mac")) |
| return false; |
| |
| return true; |
| } |
| |
| /* Install the macro name table. */ |
| |
| void |
| module_state::install_macros () |
| { |
| bytes_in &sec = slurp->macro_tbl; |
| if (!sec.size) |
| return; |
| |
| dump () && dump ("Reading macro table %M", this); |
| dump.indent (); |
| |
| unsigned count = sec.u (); |
| dump () && dump ("%u macros", count); |
| while (count--) |
| { |
| cpp_hashnode *node = sec.cpp_node (); |
| macro_import &imp = get_macro_imports (node); |
| unsigned flags = sec.u () & macro_import::slot::L_BOTH; |
| if (!flags) |
| sec.set_overrun (); |
| |
| if (sec.get_overrun ()) |
| break; |
| |
| macro_import::slot &slot = imp.append (mod, flags); |
| slot.offset = sec.u (); |
| |
| dump (dumper::MACRO) |
| && dump ("Read %s macro %s%s%s %I at %u", |
| imp.length () > 1 ? "add" : "new", |
| flags & macro_import::slot::L_UNDEF ? "#undef" : "", |
| flags == macro_import::slot::L_BOTH ? " & " : "", |
| flags & macro_import::slot::L_DEF ? "#define" : "", |
| identifier (node), slot.offset); |
| |
| /* We'll leak an imported definition's TOKEN_FLD_STR's data |
| here. But that only happens when we've had to resolve the |
| deferred macro before this import -- why are you doing |
| that? */ |
| if (cpp_macro *cur = cpp_set_deferred_macro (node)) |
| if (!cur->imported_p) |
| { |
| macro_import::slot &slot = imp.exported (); |
| macro_export &exp = get_macro_export (slot); |
| exp.def = cur; |
| dump (dumper::MACRO) |
| && dump ("Saving current #define %I", identifier (node)); |
| } |
| } |
| |
| /* We're now done with the table. */ |
| elf_in::release (slurp->from, sec); |
| |
| dump.outdent (); |
| } |
| |
| /* Import the transitive macros. */ |
| |
| void |
| module_state::import_macros () |
| { |
| bitmap_ior_into (headers, slurp->headers); |
| |
| bitmap_iterator bititer; |
| unsigned bitnum; |
| EXECUTE_IF_SET_IN_BITMAP (slurp->headers, 0, bitnum, bititer) |
| (*modules)[bitnum]->install_macros (); |
| } |
| |
| /* NODE is being undefined at LOC. Record it in the export table, if |
| necessary. */ |
| |
| void |
| module_state::undef_macro (cpp_reader *, location_t loc, cpp_hashnode *node) |
| { |
| if (!node->deferred) |
| /* The macro is not imported, so our undef is irrelevant. */ |
| return; |
| |
| unsigned n = dump.push (NULL); |
| |
| macro_import::slot &slot = (*macro_imports)[node->deferred - 1].exported (); |
| macro_export &exp = get_macro_export (slot); |
| |
| exp.undef_loc = loc; |
| slot.become_undef (); |
| exp.def = NULL; |
| |
| dump (dumper::MACRO) && dump ("Recording macro #undef %I", identifier (node)); |
| |
| dump.pop (n); |
| } |
| |
| /* NODE is a deferred macro node. Determine the definition and return |
| it, with NULL if undefined. May issue diagnostics. |
| |
| This can leak memory, when merging declarations -- the string |
| contents (TOKEN_FLD_STR) of each definition are allocated in |
| unreclaimable cpp objstack. Only one will win. However, I do not |
| expect this to be common -- mostly macros have a single point of |
| definition. Perhaps we could restore the objstack to its position |
| after the first imported definition (if that wins)? The macros |
| themselves are GC'd. */ |
| |
| cpp_macro * |
| module_state::deferred_macro (cpp_reader *reader, location_t loc, |
| cpp_hashnode *node) |
| { |
| macro_import &imports = (*macro_imports)[node->deferred - 1]; |
| |
| unsigned n = dump.push (NULL); |
| dump (dumper::MACRO) && dump ("Deferred macro %I", identifier (node)); |
| |
| bitmap visible (BITMAP_GGC_ALLOC ()); |
| |
| if (!((imports[0].get_defness () & macro_import::slot::L_UNDEF) |
| && !imports[0].get_module ())) |
| { |
| /* Calculate the set of visible header imports. */ |
| bitmap_copy (visible, headers); |
| for (unsigned ix = imports.length (); ix--;) |
| { |
| const macro_import::slot &slot = imports[ix]; |
| unsigned mod = slot.get_module (); |
| if ((slot.get_defness () & macro_import::slot::L_UNDEF) |
| && bitmap_bit_p (visible, mod)) |
| { |
| bitmap arg = mod ? (*modules)[mod]->slurp->headers : headers; |
| bitmap_and_compl_into (visible, arg); |
| bitmap_set_bit (visible, mod); |
| } |
| } |
| } |
| bitmap_set_bit (visible, 0); |
| |
| /* Now find the macros that are still visible. */ |
| bool failed = false; |
| cpp_macro *def = NULL; |
| vec<macro_export> defs; |
| defs.create (imports.length ()); |
| for (unsigned ix = imports.length (); ix--;) |
| { |
| const macro_import::slot &slot = imports[ix]; |
| unsigned mod = slot.get_module (); |
| if (bitmap_bit_p (visible, mod)) |
| { |
| macro_export *pushed = NULL; |
| if (mod) |
| { |
| const module_state *imp = (*modules)[mod]; |
| bytes_in &sec = imp->slurp->macro_defs; |
| if (!sec.get_overrun ()) |
| { |
| dump (dumper::MACRO) |
| && dump ("Reading macro %s%s%s %I module %M at %u", |
| slot.get_defness () & macro_import::slot::L_UNDEF |
| ? "#undef" : "", |
| slot.get_defness () == macro_import::slot::L_BOTH |
| ? " & " : "", |
| slot.get_defness () & macro_import::slot::L_DEF |
| ? "#define" : "", |
| identifier (node), imp, slot.offset); |
| sec.random_access (slot.offset); |
| |
| macro_export exp; |
| if (slot.get_defness () & macro_import::slot::L_UNDEF) |
| exp.undef_loc = imp->read_location (sec); |
| if (slot.get_defness () & macro_import::slot::L_DEF) |
| exp.def = imp->read_define (sec, reader); |
| if (sec.get_overrun ()) |
| error_at (loc, "macro definitions of %qE corrupted", |
| imp->name); |
| else |
| pushed = defs.quick_push (exp); |
| } |
| } |
| else |
| pushed = defs.quick_push ((*macro_exports)[slot.offset]); |
| if (pushed && pushed->def) |
| { |
| if (!def) |
| def = pushed->def; |
| else if (cpp_compare_macros (def, pushed->def)) |
| failed = true; |
| } |
| } |
| } |
| |
| if (failed) |
| { |
| /* If LOC is the first loc, this is the end of file check, which |
| is a warning. */ |
| if (loc == MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0))) |
| warning_at (loc, OPT_Winvalid_imported_macros, |
| "inconsistent imported macro definition %qE", |
| identifier (node)); |
| else |
| error_at (loc, "inconsistent imported macro definition %qE", |
| identifier (node)); |
| for (unsigned ix = defs.length (); ix--;) |
| { |
| macro_export &exp = defs[ix]; |
| if (exp.undef_loc) |
| inform (exp.undef_loc, "%<#undef %E%>", identifier (node)); |
| if (exp.def) |
| inform (exp.def->line, "%<#define %s%>", |
| cpp_macro_definition (reader, node, exp.def)); |
| } |
| def = NULL; |
| } |
| |
| defs.release (); |
| |
| dump.pop (n); |
| |
| return def; |
| } |
| |
| /* Stream the static aggregates. Sadly some headers (ahem: |
| iostream) contain static vars, and rely on them to run global |
| ctors. */ |
| unsigned |
| module_state::write_inits (elf_out *to, depset::hash &table, unsigned *crc_ptr) |
| { |
| if (!static_aggregates && !tls_aggregates) |
| return 0; |
| |
| dump () && dump ("Writing initializers"); |
| dump.indent (); |
| |
| static_aggregates = nreverse (static_aggregates); |
| tls_aggregates = nreverse (tls_aggregates); |
| |
| unsigned count = 0; |
| trees_out sec (to, this, table, ~0u); |
| sec.begin (); |
| |
| tree list = static_aggregates; |
| for (int passes = 0; passes != 2; passes++) |
| { |
| for (tree init = list; init; init = TREE_CHAIN (init), count++) |
| if (TREE_LANG_FLAG_0 (init)) |
| { |
| tree decl = TREE_VALUE (init); |
| |
| dump ("Initializer:%u for %N", count, decl); |
| sec.tree_node (decl); |
| } |
| |
| list = tls_aggregates; |
| } |
| |
| sec.end (to, to->name (MOD_SNAME_PFX ".ini"), crc_ptr); |
| dump.outdent (); |
| |
| return count; |
| } |
| |
| /* We have to defer some post-load processing until we've completed |
| reading, because they can cause more reading. */ |
| |
| static void |
| post_load_processing () |
| { |
| /* We mustn't cause a GC, our caller should have arranged for that |
| not to happen. */ |
| gcc_checking_assert (function_depth); |
| |
| if (!post_load_decls) |
| return; |
| |
| tree old_cfd = current_function_decl; |
| struct function *old_cfun = cfun; |
| while (post_load_decls->length ()) |
| { |
| tree decl = post_load_decls->pop (); |
| |
| dump () && dump ("Post-load processing of %N", decl); |
| |
| gcc_checking_assert (DECL_ABSTRACT_P (decl)); |
| /* Cloning can cause loading -- specifically operator delete for |
| the deleting dtor. */ |
| maybe_clone_body (decl); |
| } |
| |
| cfun = old_cfun; |
| current_function_decl = old_cfd; |
| } |
| |
| bool |
| module_state::read_inits (unsigned count) |
| { |
| trees_in sec (this); |
| if (!sec.begin (loc, from (), from ()->find (MOD_SNAME_PFX ".ini"))) |
| return false; |
| dump () && dump ("Reading %u initializers", count); |
| dump.indent (); |
| |
| lazy_snum = ~0u; |
| for (unsigned ix = 0; ix != count; ix++) |
| { |
| /* Merely referencing the decl causes its initializer to be read |
| and added to the correct list. */ |
| tree decl = sec.tree_node (); |
| |
| if (sec.get_overrun ()) |
| break; |
| if (decl) |
| dump ("Initializer:%u for %N", count, decl); |
| } |
| lazy_snum = 0; |
| post_load_processing (); |
| dump.outdent (); |
| if (!sec.end (from ())) |
| return false; |
| return true; |
| } |
| |
| void |
| module_state::write_counts (elf_out *to, unsigned counts[MSC_HWM], |
| unsigned *crc_ptr) |
| { |
| bytes_out cfg (to); |
| |
| cfg.begin (); |
| |
| for (unsigned ix = MSC_HWM; ix--;) |
| cfg.u (counts[ix]); |
| |
| if (dump ()) |
| { |
| dump ("Cluster sections are [%u,%u)", |
| counts[MSC_sec_lwm], counts[MSC_sec_hwm]); |
| dump ("Bindings %u", counts[MSC_bindings]); |
| dump ("Pendings %u", counts[MSC_pendings]); |
| dump ("Entities %u", counts[MSC_entities]); |
| dump ("Namespaces %u", counts[MSC_namespaces]); |
| dump ("Macros %u", counts[MSC_macros]); |
| dump ("Initializers %u", counts[MSC_inits]); |
| } |
| |
| cfg.end (to, to->name (MOD_SNAME_PFX ".cnt"), crc_ptr); |
| } |
| |
| bool |
| module_state::read_counts (unsigned counts[MSC_HWM]) |
| { |
| bytes_in cfg; |
| |
| if (!cfg.begin (loc, from (), MOD_SNAME_PFX ".cnt")) |
| return false; |
| |
| for (unsigned ix = MSC_HWM; ix--;) |
| counts[ix] = cfg.u (); |
| |
| if (dump ()) |
| { |
| dump ("Declaration sections are [%u,%u)", |
| counts[MSC_sec_lwm], counts[MSC_sec_hwm]); |
| dump ("Bindings %u", counts[MSC_bindings]); |
| dump ("Pendings %u", counts[MSC_pendings]); |
| dump ("Entities %u", counts[MSC_entities]); |
| dump ("Namespaces %u", counts[MSC_namespaces]); |
| dump ("Macros %u", counts[MSC_macros]); |
| dump ("Initializers %u", counts[MSC_inits]); |
| } |
| |
| return cfg.end (from ()); |
| } |
| |
| /* Tool configuration: MOD_SNAME_PFX .config |
| |
| This is data that confirms current state (or fails). */ |
| |
| void |
| module_state::write_config (elf_out *to, module_state_config &config, |
| unsigned inner_crc) |
| { |
| bytes_out cfg (to); |
| |
| cfg.begin (); |
| |
| /* Write version and inner crc as u32 values, for easier |
| debug inspection. */ |
| dump () && dump ("Writing version=%V, inner_crc=%x", |
| MODULE_VERSION, inner_crc); |
| cfg.u32 (unsigned (MODULE_VERSION)); |
| cfg.u32 (inner_crc); |
| |
| cfg.u (to->name (is_header () ? "" : get_flatname ())); |
| |
| /* Configuration. */ |
| dump () && dump ("Writing target='%s', host='%s'", |
| TARGET_MACHINE, HOST_MACHINE); |
| unsigned target = to->name (TARGET_MACHINE); |
| unsigned host = (!strcmp (TARGET_MACHINE, HOST_MACHINE) |
| ? target : to->name (HOST_MACHINE)); |
| cfg.u (target); |
| cfg.u (host); |
| |
| cfg.str (config.dialect_str); |
| cfg.u (extensions); |
| |
| /* Global tree information. We write the globals crc separately, |
| rather than mix it directly into the overall crc, as it is used |
| to ensure data match between instances of the compiler, not |
| integrity of the file. */ |
| dump () && dump ("Writing globals=%u, crc=%x", |
| fixed_trees->length (), global_crc); |
| cfg.u (fixed_trees->length ()); |
| cfg.u32 (global_crc); |
| |
| if (is_partition ()) |
| cfg.u (is_interface ()); |
| |
| cfg.u (config.num_imports); |
| cfg.u (config.num_partitions); |
| cfg.u (config.num_entities); |
| |
| cfg.u (config.ordinary_locs); |
| cfg.u (config.macro_locs); |
| cfg.u (config.ordinary_loc_align); |
| |
| /* Now generate CRC, we'll have incorporated the inner CRC because |
| of its serialization above. */ |
| cfg.end (to, to->name (MOD_SNAME_PFX ".cfg"), &crc); |
| dump () && dump ("Writing CRC=%x", crc); |
| } |
| |
| void |
| module_state::note_cmi_name () |
| { |
| if (!cmi_noted_p && filename) |
| { |
| cmi_noted_p = true; |
| inform (loc, "compiled module file is %qs", |
| maybe_add_cmi_prefix (filename)); |
| } |
| } |
| |
| bool |
| module_state::read_config (module_state_config &config) |
| { |
| bytes_in cfg; |
| |
| if (!cfg.begin (loc, from (), MOD_SNAME_PFX ".cfg")) |
| return false; |
| |
| /* Check version. */ |
| unsigned my_ver = MODULE_VERSION; |
| unsigned their_ver = cfg.u32 (); |
| dump () && dump (my_ver == their_ver ? "Version %V" |
| : "Expecting %V found %V", my_ver, their_ver); |
| if (their_ver != my_ver) |
| { |
| /* The compiler versions differ. Close enough? */ |
| verstr_t my_string, their_string; |
| |
| version2string (my_ver, my_string); |
| version2string (their_ver, their_string); |
| |
| /* Reject when either is non-experimental or when experimental |
| major versions differ. */ |
| bool reject_p = ((!IS_EXPERIMENTAL (my_ver) |
| || !IS_EXPERIMENTAL (their_ver) |
| || MODULE_MAJOR (my_ver) != MODULE_MAJOR (their_ver)) |
| /* The 'I know what I'm doing' switch. */ |
| && !flag_module_version_ignore); |
| bool inform_p = true; |
| if (reject_p) |
| { |
| cfg.set_overrun (); |
| error_at (loc, "compiled module is %sversion %s", |
| IS_EXPERIMENTAL (their_ver) ? "experimental " : "", |
| their_string); |
| } |
| else |
| inform_p = warning_at (loc, 0, "compiled module is %sversion %s", |
| IS_EXPERIMENTAL (their_ver) ? "experimental " : "", |
| their_string); |
| |
| if (inform_p) |
| { |
| inform (loc, "compiler is %sversion %s%s%s", |
| IS_EXPERIMENTAL (my_ver) ? "experimental " : "", |
| my_string, |
| reject_p ? "" : flag_module_version_ignore |
| ? ", be it on your own head!" : ", close enough?", |
| reject_p ? "" : " \xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf"); |
| note_cmi_name (); |
| } |
| |
| if (reject_p) |
| goto done; |
| } |
| |
| /* We wrote the inner crc merely to merge it, so simply read it |
| back and forget it. */ |
| cfg.u32 (); |
| |
| /* Check module name. */ |
| { |
| const char *their_name = from ()->name (cfg.u ()); |
| const char *our_name = ""; |
| |
| if (!is_header ()) |
| our_name = get_flatname (); |
| |
| /* Header units can be aliased, so name checking is |
| inappropriate. */ |
| if (0 != strcmp (their_name, our_name)) |
| { |
| error_at (loc, |
| their_name[0] && our_name[0] ? G_("module %qs found") |
| : their_name[0] |
| ? G_("header module expected, module %qs found") |
| : G_("module %qs expected, header module found"), |
| their_name[0] ? their_name : our_name); |
| cfg.set_overrun (); |
| goto done; |
| } |
| } |
| |
| /* Check the CRC after the above sanity checks, so that the user is |
| clued in. */ |
| { |
| unsigned e_crc = crc; |
| crc = cfg.get_crc (); |
| dump () && dump ("Reading CRC=%x", crc); |
| if (!is_direct () && crc != e_crc) |
| { |
| error_at (loc, "module %qs CRC mismatch", get_flatname ()); |
| cfg.set_overrun (); |
| goto done; |
| } |
| } |
| |
| /* Check target & host. */ |
| { |
| const char *their_target = from ()->name (cfg.u ()); |
| const char *their_host = from ()->name (cfg.u ()); |
| dump () && dump ("Read target='%s', host='%s'", their_target, their_host); |
| if (strcmp (their_target, TARGET_MACHINE) |
| || strcmp (their_host, HOST_MACHINE)) |
| { |
| error_at (loc, "target & host is %qs:%qs, expected %qs:%qs", |
| their_target, TARGET_MACHINE, their_host, HOST_MACHINE); |
| cfg.set_overrun (); |
| goto done; |
| } |
| } |
| |
| /* Check compilation dialect. This must match. */ |
| { |
| const char *their_dialect = cfg.str (); |
| if (strcmp (their_dialect, config.dialect_str)) |
| { |
| error_at (loc, "language dialect differs %qs, expected %qs", |
| their_dialect, config.dialect_str); |
| cfg.set_overrun (); |
| goto done; |
| } |
| } |
| |
| /* Check for extensions. If they set any, we must have them set |
| too. */ |
| { |
| unsigned ext = cfg.u (); |
| unsigned allowed = (flag_openmp ? SE_OPENMP : 0); |
| |
| if (unsigned bad = ext & ~allowed) |
| { |
| if (bad & SE_OPENMP) |
| error_at (loc, "module contains OpenMP, use %<-fopenmp%> to enable"); |
| cfg.set_overrun (); |
| goto done; |
| } |
| extensions = ext; |
| } |
| |
| /* Check global trees. */ |
| { |
| unsigned their_fixed_length = cfg.u (); |
| unsigned their_fixed_crc = cfg.u32 (); |
| dump () && dump ("Read globals=%u, crc=%x", |
| their_fixed_length, their_fixed_crc); |
| if (!flag_preprocess_only |
| && (their_fixed_length != fixed_trees->length () |
| || their_fixed_crc != global_crc)) |
| { |
| error_at (loc, "fixed tree mismatch"); |
| cfg.set_overrun (); |
| goto done; |
| } |
| } |
| |
| /* All non-partitions are interfaces. */ |
| interface_p = !is_partition () || cfg.u (); |
| |
| config.num_imports = cfg.u (); |
| config.num_partitions = cfg.u (); |
| config.num_entities = cfg.u (); |
| |
| config.ordinary_locs = cfg.u (); |
| config.macro_locs = cfg.u (); |
| config.ordinary_loc_align = cfg.u (); |
| |
| done: |
| return cfg.end (from ()); |
| } |
| |
| /* Comparator for ordering the Ordered Ordinary Location array. */ |
| |
| static int |
| ool_cmp (const void *a_, const void *b_) |
| { |
| auto *a = *static_cast<const module_state *const *> (a_); |
| auto *b = *static_cast<const module_state *const *> (b_); |
| if (a == b) |
| return 0; |
| else if (a->ordinary_locs.first < b->ordinary_locs.second) |
| return -1; |
| else |
| return +1; |
| } |
| |
| /* Use ELROND format to record the following sections: |
| qualified-names : binding value(s) |
| MOD_SNAME_PFX.README : human readable, strings |
| MOD_SNAME_PFX.ENV : environment strings, strings |
| MOD_SNAME_PFX.nms : namespace hierarchy |
| MOD_SNAME_PFX.bnd : binding table |
| MOD_SNAME_PFX.spc : specialization table |
| MOD_SNAME_PFX.imp : import table |
| MOD_SNAME_PFX.ent : entity table |
| MOD_SNAME_PFX.prt : partitions table |
| MOD_SNAME_PFX.olm : ordinary line maps |
| MOD_SNAME_PFX.mlm : macro line maps |
| MOD_SNAME_PFX.def : macro definitions |
| MOD_SNAME_PFX.mac : macro index |
| MOD_SNAME_PFX.ini : inits |
| MOD_SNAME_PFX.cnt : counts |
| MOD_SNAME_PFX.cfg : config data |
| */ |
| |
| void |
| module_state::write (elf_out *to, cpp_reader *reader) |
| { |
| /* Figure out remapped module numbers, which might elide |
| partitions. */ |
| bitmap partitions = NULL; |
| if (!is_header () && !is_partition ()) |
| partitions = BITMAP_GGC_ALLOC (); |
| |
| unsigned mod_hwm = 1; |
| for (unsigned ix = 1; ix != modules->length (); ix++) |
| { |
| module_state *imp = (*modules)[ix]; |
| |
| /* Promote any non-partition direct import from a partition, unless |
| we're a partition. */ |
| if (!is_partition () && !imp->is_partition () |
| && imp->is_partition_direct ()) |
| imp->directness = MD_PURVIEW_DIRECT; |
| |
| /* Write any import that is not a partition, unless we're a |
| partition. */ |
| if (!partitions || !imp->is_partition ()) |
| imp->remap = mod_hwm++; |
| else |
| { |
| dump () && dump ("Partition %M %u", imp, ix); |
| bitmap_set_bit (partitions, ix); |
| imp->remap = 0; |
| /* All interface partitions must be exported. */ |
| if (imp->is_interface () && !bitmap_bit_p (exports, imp->mod)) |
| { |
| error_at (imp->loc, "interface partition is not exported"); |
| bitmap_set_bit (exports, imp->mod); |
| } |
| |
| /* All the partition entities should have been loaded when |
| loading the partition. */ |
| if (CHECKING_P) |
| for (unsigned jx = 0; jx != imp->entity_num; jx++) |
| { |
| binding_slot *slot = &(*entity_ary)[imp->entity_lwm + jx]; |
| gcc_checking_assert (!slot->is_lazy ()); |
| } |
| } |
| } |
| |
| if (partitions && bitmap_empty_p (partitions)) |
| /* No partitions present. */ |
| partitions = nullptr; |
| |
| /* Find the set of decls we must write out. */ |
| depset::hash table (DECL_NAMESPACE_BINDINGS (global_namespace)->size () * 8); |
| /* Add the specializations before the writables, so that we can |
| detect injected friend specializations. */ |
| table.add_specializations (true); |
| table.add_specializations (false); |
| if (partial_specializations) |
| { |
| table.add_partial_entities (partial_specializations); |
| partial_specializations = NULL; |
| } |
| table.add_namespace_entities (global_namespace, partitions); |
| if (class_members) |
| { |
| table.add_class_entities (class_members); |
| class_members = NULL; |
| } |
| |
| /* Now join everything up. */ |
| table.find_dependencies (this); |
| |
| if (!table.finalize_dependencies ()) |
| { |
| to->set_error (); |
| return; |
| } |
| |
| #if CHECKING_P |
| /* We're done verifying at-most once reading, reset to verify |
| at-most once writing. */ |
| note_defs = note_defs_table_t::create_ggc (1000); |
| #endif |
| |
| /* Determine Strongy Connected Components. */ |
| vec<depset *> sccs = table.connect (); |
| |
| vec_alloc (ool, modules->length ()); |
| for (unsigned ix = modules->length (); --ix;) |
| { |
| auto *import = (*modules)[ix]; |
| if (import->loadedness > ML_NONE |
| && !(partitions && bitmap_bit_p (partitions, import->mod))) |
| ool->quick_push (import); |
| } |
| ool->qsort (ool_cmp); |
| |
| unsigned crc = 0; |
| module_state_config config; |
| location_map_info map_info = write_prepare_maps (&config); |
| unsigned counts[MSC_HWM]; |
| |
| config.num_imports = mod_hwm; |
| config.num_partitions = modules->length () - mod_hwm; |
| memset (counts, 0, sizeof (counts)); |
| |
| /* depset::cluster is the cluster number, |
| depset::section is unspecified scratch value. |
| |
| The following loops make use of the tarjan property that |
| dependencies will be earlier in the SCCS array. */ |
| |
| /* This first loop determines the number of depsets in each SCC, and |
| also the number of namespaces we're dealing with. During the |
| loop, the meaning of a couple of depset fields now change: |
| |
| depset::cluster -> size_of cluster, if first of cluster & !namespace |
| depset::section -> section number of cluster (if !namespace). */ |
| |
| unsigned n_spaces = 0; |
| counts[MSC_sec_lwm] = counts[MSC_sec_hwm] = to->get_section_limit (); |
| for (unsigned size, ix = 0; ix < sccs.length (); ix += size) |
| { |
| depset **base = &sccs[ix]; |
| |
| if (base[0]->get_entity_kind () == depset::EK_NAMESPACE) |
| { |
| n_spaces++; |
| size = 1; |
| } |
| else |
| { |
| /* Count the members in this cluster. */ |
| for (size = 1; ix + size < sccs.length (); size++) |
| if (base[size]->cluster != base[0]->cluster) |
| break; |
| |
| for (unsigned jx = 0; jx != size; jx++) |
| { |
| /* Set the section number. */ |
| base[jx]->cluster = ~(~0u >> 1); /* A bad value. */ |
| base[jx]->section = counts[MSC_sec_hwm]; |
| } |
| |
| /* Save the size in the first member's cluster slot. */ |
| base[0]->cluster = size; |
| |
| counts[MSC_sec_hwm]++; |
| } |
| } |
| |
| /* Write the clusters. Namespace decls are put in the spaces array. |
| The meaning of depset::cluster changes to provide the |
| unnamed-decl count of the depset's decl (and remains zero for |
| non-decls and non-unnamed). */ |
| unsigned bytes = 0; |
| vec<depset *> spaces; |
| spaces.create (n_spaces); |
| |
| for (unsigned size, ix = 0; ix < sccs.length (); ix += size) |
| { |
| depset **base = &sccs[ix]; |
| |
| if (base[0]->get_entity_kind () == depset::EK_NAMESPACE) |
| { |
| tree decl = base[0]->get_entity (); |
| if (decl == global_namespace) |
| base[0]->cluster = 0; |
| else if (!base[0]->is_import ()) |
| { |
| base[0]->cluster = counts[MSC_entities]++; |
| spaces.quick_push (base[0]); |
| counts[MSC_namespaces]++; |
| if (CHECKING_P) |
| { |
| /* Add it to the entity map, such that we can tell it is |
| part of us. */ |
| bool existed; |
| unsigned *slot = &entity_map->get_or_insert |
| (DECL_UID (decl), &existed); |
| if (existed) |
| /* It must have come from a partition. */ |
| gcc_checking_assert |
| (import_entity_module (*slot)->is_partition ()); |
| *slot = ~base[0]->cluster; |
| } |
| dump (dumper::CLUSTER) && dump ("Cluster namespace %N", decl); |
| } |
| size = 1; |
| } |
| else |
| { |
| size = base[0]->cluster; |
| |
| /* Cluster is now used to number entities. */ |
| base[0]->cluster = ~(~0u >> 1); /* A bad value. */ |
| |
| sort_cluster (&table, base, size); |
| |
| /* Record the section for consistency checking during stream |
| out -- we don't want to start writing decls in different |
| sections. */ |
| table.section = base[0]->section; |
| bytes += write_cluster (to, base, size, table, counts, &crc); |
| table.section = 0; |
| } |
| } |
| |
| /* depset::cluster - entity number (on entities) |
| depset::section - cluster number */ |
| /* We'd better have written as many sections and found as many |
| namespaces as we predicted. */ |
| gcc_assert (counts[MSC_sec_hwm] == to->get_section_limit () |
| && spaces.length () == counts[MSC_namespaces]); |
| |
| /* Write the entitites. None happens if we contain namespaces or |
| nothing. */ |
| config.num_entities = counts[MSC_entities]; |
| if (counts[MSC_entities]) |
| write_entities (to, sccs, counts[MSC_entities], &crc); |
| |
| /* Write the namespaces. */ |
| if (counts[MSC_namespaces]) |
| write_namespaces (to, spaces, counts[MSC_namespaces], &crc); |
| |
| /* Write the bindings themselves. */ |
| counts[MSC_bindings] = write_bindings (to, sccs, &crc); |
| |
| /* Write the unnamed. */ |
| counts[MSC_pendings] = write_pendings (to, sccs, table, &crc); |
| |
| /* Write the import table. */ |
| if (config.num_imports > 1) |
| write_imports (to, &crc); |
| |
| /* Write elided partition table. */ |
| if (config.num_partitions) |
| write_partitions (to, config.num_partitions, &crc); |
| |
| /* Write the line maps. */ |
| write_ordinary_maps (to, map_info, &config, config.num_partitions, &crc); |
| write_macro_maps (to, map_info, &config, &crc); |
| |
| if (is_header ()) |
| { |
| counts[MSC_macros] = write_macros (to, reader, &crc); |
| counts[MSC_inits] = write_inits (to, table, &crc); |
| } |
| |
| unsigned clusters = counts[MSC_sec_hwm] - counts[MSC_sec_lwm]; |
| dump () && dump ("Wrote %u clusters, average %u bytes/cluster", |
| clusters, (bytes + clusters / 2) / (clusters + !clusters)); |
| |
| write_counts (to, counts, &crc); |
| |
| /* And finish up. */ |
| write_config (to, config, crc); |
| |
| spaces.release (); |
| sccs.release (); |
| |
| vec_free (ool); |
| |
| /* Human-readable info. */ |
| write_readme (to, reader, config.dialect_str, extensions); |
| |
| // FIXME:QOI: Have a command line switch to control more detailed |
| // information (which might leak data you do not want to leak). |
| // Perhaps (some of) the write_readme contents should also be |
| // so-controlled. |
| if (false) |
| write_env (to); |
| |
| trees_out::instrument (); |
| dump () && dump ("Wrote %u sections", to->get_section_limit ()); |
| } |
| |
| /* Initial read of a CMI. Checks config, loads up imports and line |
| maps. */ |
| |
| bool |
| module_state::read_initial (cpp_reader *reader) |
| { |
| module_state_config config; |
| bool ok = true; |
| |
| if (ok && !from ()->begin (loc)) |
| ok = false; |
| |
| if (ok && !read_config (config)) |
| ok = false; |
| |
| bool have_locs = ok && read_prepare_maps (&config); |
| |
| /* Ordinary maps before the imports. */ |
| if (have_locs && !read_ordinary_maps ()) |
| ok = false; |
| |
| /* Allocate the REMAP vector. */ |
| slurp->alloc_remap (config.num_imports); |
| |
| if (ok) |
| { |
| /* Read the import table. Decrement current to stop this CMI |
| from being evicted during the import. */ |
| slurp->current--; |
| if (config.num_imports > 1 && !read_imports (reader, line_table)) |
| ok = false; |
| slurp->current++; |
| } |
| |
| /* Read the elided partition table, if we're the primary partition. */ |
| if (ok && config.num_partitions && is_module () |
| && !read_partitions (config.num_partitions)) |
| ok = false; |
| |
| /* Determine the module's number. */ |
| gcc_checking_assert (mod == MODULE_UNKNOWN); |
| gcc_checking_assert (this != (*modules)[0]); |
| |
| { |
| /* Allocate space in the entities array now -- that array must be |
| monotionically in step with the modules array. */ |
| entity_lwm = vec_safe_length (entity_ary); |
| entity_num = config.num_entities; |
| gcc_checking_assert (modules->length () == 1 |
| || modules->last ()->entity_lwm <= entity_lwm); |
| vec_safe_reserve (entity_ary, config.num_entities); |
| |
| binding_slot slot; |
| slot.u.binding = NULL_TREE; |
| for (unsigned count = config.num_entities; count--;) |
| entity_ary->quick_push (slot); |
| } |
| |
| /* We'll run out of other resources before we run out of module |
| indices. */ |
| mod = modules->length (); |
| vec_safe_push (modules, this); |
| |
| /* We always import and export ourselves. */ |
| bitmap_set_bit (imports, mod); |
| bitmap_set_bit (exports, mod); |
| |
| if (ok) |
| (*slurp->remap)[0] = mod << 1; |
| dump () && dump ("Assigning %M module number %u", this, mod); |
| |
| /* We should not have been frozen during the importing done by |
| read_config. */ |
| gcc_assert (!from ()->is_frozen ()); |
| |
| /* Macro maps after the imports. */ |
| if (ok && have_locs && !read_macro_maps ()) |
| ok = false; |
| |
| gcc_assert (slurp->current == ~0u); |
| return ok; |
| } |
| |
| /* Read a preprocessor state. */ |
| |
| bool |
| module_state::read_preprocessor (bool outermost) |
| { |
| gcc_checking_assert (is_header () && slurp |
| && slurp->remap_module (0) == mod); |
| |
| if (loadedness == ML_PREPROCESSOR) |
| return !(from () && from ()->get_error ()); |
| |
| bool ok = true; |
| |
| /* Read direct header imports. */ |
| unsigned len = slurp->remap->length (); |
| for (unsigned ix = 1; ok && ix != len; ix++) |
| { |
| unsigned map = (*slurp->remap)[ix]; |
| if (map & 1) |
| { |
| module_state *import = (*modules)[map >> 1]; |
| if (import->is_header ()) |
| { |
| ok = import->read_preprocessor (false); |
| bitmap_ior_into (slurp->headers, import->slurp->headers); |
| } |
| } |
| } |
| |
| /* Record as a direct header. */ |
| if (ok) |
| bitmap_set_bit (slurp->headers, mod); |
| |
| if (ok && !read_macros ()) |
| ok = false; |
| |
| loadedness = ML_PREPROCESSOR; |
| announce ("macros"); |
| |
| if (flag_preprocess_only) |
| /* We're done with the string table. */ |
| from ()->release (); |
| |
| return check_read (outermost, ok); |
| } |
| |
| /* Read language state. */ |
| |
| bool |
| module_state::read_language (bool outermost) |
| { |
| gcc_checking_assert (!lazy_snum); |
| |
| if (loadedness == ML_LANGUAGE) |
| return !(slurp && from () && from ()->get_error ()); |
| |
| gcc_checking_assert (slurp && slurp->current == ~0u |
| && slurp->remap_module (0) == mod); |
| |
| bool ok = true; |
| |
| /* Read direct imports. */ |
| unsigned len = slurp->remap->length (); |
| for (unsigned ix = 1; ok && ix != len; ix++) |
| { |
| unsigned map = (*slurp->remap)[ix]; |
| if (map & 1) |
| { |
| module_state *import = (*modules)[map >> 1]; |
| if (!import->read_language (false)) |
| ok = false; |
| } |
| } |
| |
| unsigned counts[MSC_HWM]; |
| |
| if (ok && !read_counts (counts)) |
| ok = false; |
| |
| function_depth++; /* Prevent unexpected GCs. */ |
| |
| if (ok && counts[MSC_entities] != entity_num) |
| ok = false; |
| if (ok && counts[MSC_entities] |
| && !read_entities (counts[MSC_entities], |
| counts[MSC_sec_lwm], counts[MSC_sec_hwm])) |
| ok = false; |
| |
| /* Read the namespace hierarchy. */ |
| if (ok && counts[MSC_namespaces] |
| && !read_namespaces (counts[MSC_namespaces])) |
| ok = false; |
| |
| if (ok && !read_bindings (counts[MSC_bindings], |
| counts[MSC_sec_lwm], counts[MSC_sec_hwm])) |
| ok = false; |
| |
| /* And unnamed. */ |
| if (ok && counts[MSC_pendings] && !read_pendings (counts[MSC_pendings])) |
| ok = false; |
| |
| if (ok) |
| { |
| slurp->remaining = counts[MSC_sec_hwm] - counts[MSC_sec_lwm]; |
| available_clusters += counts[MSC_sec_hwm] - counts[MSC_sec_lwm]; |
| } |
| |
| if (!flag_module_lazy |
| || (is_partition () |
| && module_interface_p () |
| && !module_partition_p ())) |
| { |
| /* Read the sections in forward order, so that dependencies are read |
| first. See note about tarjan_connect. */ |
| ggc_collect (); |
| |
| lazy_snum = ~0u; |
| |
| unsigned hwm = counts[MSC_sec_hwm]; |
| for (unsigned ix = counts[MSC_sec_lwm]; ok && ix != hwm; ix++) |
| if (!load_section (ix, NULL)) |
| { |
| ok = false; |
| break; |
| } |
| lazy_snum = 0; |
| post_load_processing (); |
| |
| ggc_collect (); |
| |
| if (ok && CHECKING_P) |
| for (unsigned ix = 0; ix != entity_num; ix++) |
| gcc_assert (!(*entity_ary)[ix + entity_lwm].is_lazy ()); |
| } |
| |
| // If the import is a header-unit, we need to register initializers |
| // of any static objects it contains (looking at you _Ioinit). |
| // Notice, the ordering of these initializers will be that of a |
| // dynamic initializer at this point in the current TU. (Other |
| // instances of these objects in other TUs will be initialized as |
| // part of that TU's global initializers.) |
| if (ok && counts[MSC_inits] && !read_inits (counts[MSC_inits])) |
| ok = false; |
| |
| function_depth--; |
| |
| announce (flag_module_lazy ? "lazy" : "imported"); |
| loadedness = ML_LANGUAGE; |
| |
| gcc_assert (slurp->current == ~0u); |
| |
| /* We're done with the string table. */ |
| from ()->release (); |
| |
| return check_read (outermost, ok); |
| } |
| |
| bool |
| module_state::maybe_defrost () |
| { |
| bool ok = true; |
| if (from ()->is_frozen ()) |
| { |
| if (lazy_open >= lazy_limit) |
| freeze_an_elf (); |
| dump () && dump ("Defrosting '%s'", filename); |
| ok = from ()->defrost (maybe_add_cmi_prefix (filename)); |
| lazy_open++; |
| } |
| |
| return ok; |
| } |
| |
| /* Load section SNUM, dealing with laziness. It doesn't matter if we |
| have multiple concurrent loads, because we do not use TREE_VISITED |
| when reading back in. */ |
| |
| bool |
| module_state::load_section (unsigned snum, binding_slot *mslot) |
| { |
| if (from ()->get_error ()) |
| return false; |
| |
| if (snum >= slurp->current) |
| from ()->set_error (elf::E_BAD_LAZY); |
| else if (maybe_defrost ()) |
| { |
| unsigned old_current = slurp->current; |
| slurp->current = snum; |
| slurp->lru = 0; /* Do not swap out. */ |
| slurp->remaining--; |
| read_cluster (snum); |
| slurp->lru = ++lazy_lru; |
| slurp->current = old_current; |
| } |
| |
| if (mslot && mslot->is_lazy ()) |
| { |
| /* Oops, the section didn't set this slot. */ |
| from ()->set_error (elf::E_BAD_DATA); |
| *mslot = NULL_TREE; |
| } |
| |
| bool ok = !from ()->get_error (); |
| if (!ok) |
| { |
| error_at (loc, "failed to read compiled module cluster %u: %s", |
| snum, from ()->get_error (filename)); |
| note_cmi_name (); |
| } |
| |
| maybe_completed_reading (); |
| |
| return ok; |
| } |
| |
| void |
| module_state::maybe_completed_reading () |
| { |
| if (loadedness == ML_LANGUAGE && slurp->current == ~0u && !slurp->remaining) |
| { |
| lazy_open--; |
| /* We no longer need the macros, all tokenizing has been done. */ |
| slurp->release_macros (); |
| |
| from ()->end (); |
| slurp->close (); |
| slurped (); |
| } |
| } |
| |
| /* After a reading operation, make sure things are still ok. If not, |
| emit an error and clean up. */ |
| |
| bool |
| module_state::check_read (bool outermost, bool ok) |
| { |
| gcc_checking_assert (!outermost || slurp->current == ~0u); |
| |
| if (!ok) |
| from ()->set_error (); |
| |
| if (int e = from ()->get_error ()) |
| { |
| error_at (loc, "failed to read compiled module: %s", |
| from ()->get_error (filename)); |
| note_cmi_name (); |
| |
| if (e == EMFILE |
| || e == ENFILE |
| #if MAPPED_READING |
| || e == ENOMEM |
| #endif |
| || false) |
| inform (loc, "consider using %<-fno-module-lazy%>," |
| " increasing %<-param-lazy-modules=%u%> value," |
| " or increasing the per-process file descriptor limit", |
| param_lazy_modules); |
| else if (e == ENOENT) |
| inform (loc, "imports must be built before being imported"); |
| |
| if (outermost) |
| fatal_error (loc, "returning to the gate for a mechanical issue"); |
| |
| ok = false; |
| } |
| |
| maybe_completed_reading (); |
| |
| return ok; |
| } |
| |
| /* Return the IDENTIFIER_NODE naming module IX. This is the name |
| including dots. */ |
| |
| char const * |
| module_name (unsigned ix, bool header_ok) |
| { |
| if (modules) |
| { |
| module_state *imp = (*modules)[ix]; |
| |
| if (ix && !imp->name) |
| imp = imp->parent; |
| |
| if (header_ok || !imp->is_header ()) |
| return imp->get_flatname (); |
| } |
| |
| return NULL; |
| } |
| |
| /* Return the bitmap describing what modules are imported. Remember, |
| we always import ourselves. */ |
| |
| bitmap |
| get_import_bitmap () |
| { |
| return (*modules)[0]->imports; |
| } |
| |
| /* Return the visible imports and path of instantiation for an |
| instantiation at TINST. If TINST is nullptr, we're not in an |
| instantiation, and thus will return the visible imports of the |
| current TU (and NULL *PATH_MAP_P). We cache the information on |
| the tinst level itself. */ |
| |
| static bitmap |
| path_of_instantiation (tinst_level *tinst, bitmap *path_map_p) |
| { |
| gcc_checking_assert (modules_p ()); |
| |
| if (!tinst) |
| { |
| /* Not inside an instantiation, just the regular case. */ |
| *path_map_p = nullptr; |
| return get_import_bitmap (); |
| } |
| |
| if (!tinst->path) |
| { |
| /* Calculate. */ |
| bitmap visible = path_of_instantiation (tinst->next, path_map_p); |
| bitmap path_map = *path_map_p; |
| |
| if (!path_map) |
| { |
| path_map = BITMAP_GGC_ALLOC (); |
| bitmap_set_bit (path_map, 0); |
| } |
| |
| tree decl = tinst->tldcl; |
| if (TREE_CODE (decl) == TREE_LIST) |
| decl = TREE_PURPOSE (decl); |
| if (TYPE_P (decl)) |
| decl = TYPE_NAME (decl); |
| |
| if (unsigned mod = get_originating_module (decl)) |
| if (!bitmap_bit_p (path_map, mod)) |
| { |
| /* This is brand new information! */ |
| bitmap new_path = BITMAP_GGC_ALLOC (); |
| bitmap_copy (new_path, path_map); |
| bitmap_set_bit (new_path, mod); |
| path_map = new_path; |
| |
| bitmap imports = (*modules)[mod]->imports; |
| if (bitmap_intersect_compl_p (imports, visible)) |
| { |
| /* IMPORTS contains additional modules to VISIBLE. */ |
| bitmap new_visible = BITMAP_GGC_ALLOC (); |
| |
| bitmap_ior (new_visible, visible, imports); |
| visible = new_visible; |
| } |
| } |
| |
| tinst->path = path_map; |
| tinst->visible = visible; |
| } |
| |
| *path_map_p = tinst->path; |
| return tinst->visible; |
| } |
| |
| /* Return the bitmap describing what modules are visible along the |
| path of instantiation. If we're not an instantiation, this will be |
| the visible imports of the TU. *PATH_MAP_P is filled in with the |
| modules owning the instantiation path -- we see the module-linkage |
| entities of those modules. */ |
| |
| bitmap |
| visible_instantiation_path (bitmap *path_map_p) |
| { |
| if (!modules_p ()) |
| return NULL; |
| |
| return path_of_instantiation (current_instantiation (), path_map_p); |
| } |
| |
| /* We've just directly imported IMPORT. Update our import/export |
| bitmaps. IS_EXPORT is true if we're reexporting the OTHER. */ |
| |
| void |
| module_state::set_import (module_state const *import, bool is_export) |
| { |
| gcc_checking_assert (this != import); |
| |
| /* We see IMPORT's exports (which includes IMPORT). If IMPORT is |
| the primary interface or a partition we'll see its imports. */ |
| bitmap_ior_into (imports, import->is_module () || import->is_partition () |
| ? import->imports : import->exports); |
| |
| if (is_export) |
| /* We'll export OTHER's exports. */ |
| bitmap_ior_into (exports, import->exports); |
| } |
| |
| /* Return the declaring entity of DECL. That is the decl determining |
| how to decorate DECL with module information. Returns NULL_TREE if |
| it's the global module. */ |
| |
| tree |
| get_originating_module_decl (tree decl) |
| { |
| /* An enumeration constant. */ |
| if (TREE_CODE (decl) == CONST_DECL |
| && DECL_CONTEXT (decl) |
| && (TREE_CODE (DECL_CONTEXT (decl)) == ENUMERAL_TYPE)) |
| decl = TYPE_NAME (DECL_CONTEXT (decl)); |
| else if (TREE_CODE (decl) == FIELD_DECL |
| || TREE_CODE (decl) == USING_DECL) |
| { |
| decl = DECL_CONTEXT (decl); |
| if (TREE_CODE (decl) != FUNCTION_DECL) |
| decl = TYPE_NAME (decl); |
| } |
| |
| gcc_checking_assert (TREE_CODE (decl) == TEMPLATE_DECL |
| || TREE_CODE (decl) == FUNCTION_DECL |
| || TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == CONCEPT_DECL |
| || TREE_CODE (decl) == NAMESPACE_DECL); |
| |
| for (;;) |
| { |
| /* Uninstantiated template friends are owned by the befriending |
| class -- not their context. */ |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) |
| decl = TYPE_NAME (DECL_CHAIN (decl)); |
| |
| int use; |
| if (tree ti = node_template_info (decl, use)) |
| { |
| decl = TI_TEMPLATE (ti); |
| if (TREE_CODE (decl) != TEMPLATE_DECL) |
| { |
| /* A friend template specialization. */ |
| gcc_checking_assert (OVL_P (decl)); |
| return global_namespace; |
| } |
| } |
| else |
| { |
| tree ctx = CP_DECL_CONTEXT (decl); |
| if (TREE_CODE (ctx) == NAMESPACE_DECL) |
| break; |
| |
| if (TYPE_P (ctx)) |
| { |
| ctx = TYPE_NAME (ctx); |
| if (!ctx) |
| { |
| /* Some kind of internal type. */ |
| gcc_checking_assert (DECL_ARTIFICIAL (decl)); |
| return global_namespace; |
| } |
| } |
| decl = ctx; |
| } |
| } |
| |
| return decl; |
| } |
| |
| int |
| get_originating_module (tree decl, bool for_mangle) |
| { |
| tree owner = get_originating_module_decl (decl); |
| tree not_tmpl = STRIP_TEMPLATE (owner); |
| |
| if (!DECL_LANG_SPECIFIC (not_tmpl)) |
| return for_mangle ? -1 : 0; |
| |
| if (for_mangle |
| && (DECL_MODULE_EXPORT_P (owner) || !DECL_MODULE_PURVIEW_P (not_tmpl))) |
| return -1; |
| |
| if (!DECL_MODULE_IMPORT_P (not_tmpl)) |
| return 0; |
| |
| return get_importing_module (owner); |
| } |
| |
| unsigned |
| get_importing_module (tree decl, bool flexible) |
| { |
| unsigned index = import_entity_index (decl, flexible); |
| if (index == ~(~0u >> 1)) |
| return -1; |
| module_state *module = import_entity_module (index); |
| |
| return module->mod; |
| } |
| |
| /* Is it permissible to redeclare DECL. */ |
| |
| bool |
| module_may_redeclare (tree decl) |
| { |
| module_state *me = (*modules)[0]; |
| module_state *them = me; |
| tree not_tmpl = STRIP_TEMPLATE (decl); |
| if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl)) |
| { |
| /* We can be given the TEMPLATE_RESULT. We want the |
| TEMPLATE_DECL. */ |
| int use_tpl = -1; |
| if (tree ti = node_template_info (decl, use_tpl)) |
| { |
| tree tmpl = TI_TEMPLATE (ti); |
| if (use_tpl == 2) |
| { |
| /* A partial specialization. Find that specialization's |
| template_decl. */ |
| for (tree list = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); |
| list; list = TREE_CHAIN (list)) |
| if (DECL_TEMPLATE_RESULT (TREE_VALUE (list)) == decl) |
| { |
| decl = TREE_VALUE (list); |
| break; |
| } |
| } |
| else if (DECL_TEMPLATE_RESULT (tmpl) == decl) |
| decl = tmpl; |
| } |
| unsigned index = import_entity_index (decl); |
| them = import_entity_module (index); |
| } |
| |
| if (them->is_header ()) |
| { |
| if (!header_module_p ()) |
| return !module_purview_p (); |
| |
| if (DECL_SOURCE_LOCATION (decl) == BUILTINS_LOCATION) |
| /* This is a builtin, being declared in header-unit. We |
| now need to mark it as an export. */ |
| DECL_MODULE_EXPORT_P (decl) = true; |
| |
| /* If it came from a header, it's in the global module. */ |
| return true; |
| } |
| |
| if (me == them) |
| return ((DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_PURVIEW_P (not_tmpl)) |
| == module_purview_p ()); |
| |
| if (!me->name) |
| me = me->parent; |
| |
| /* We can't have found a GMF entity from a named module. */ |
| gcc_checking_assert (DECL_LANG_SPECIFIC (not_tmpl) |
| && DECL_MODULE_PURVIEW_P (not_tmpl)); |
| |
| return me && get_primary (them) == get_primary (me); |
| } |
| |
| /* DECL is being created by this TU. Record it came from here. We |
| record module purview, so we can see if partial or explicit |
| specialization needs to be written out, even though its purviewness |
| comes from the most general template. */ |
| |
| void |
| set_instantiating_module (tree decl) |
| { |
| gcc_assert (TREE_CODE (decl) == FUNCTION_DECL |
| || TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == CONCEPT_DECL |
| || TREE_CODE (decl) == TEMPLATE_DECL |
| || (TREE_CODE (decl) == NAMESPACE_DECL |
| && DECL_NAMESPACE_ALIAS (decl))); |
| |
| if (!modules_p ()) |
| return; |
| |
| decl = STRIP_TEMPLATE (decl); |
| |
| if (!DECL_LANG_SPECIFIC (decl) && module_purview_p ()) |
| retrofit_lang_decl (decl); |
| |
| if (DECL_LANG_SPECIFIC (decl)) |
| { |
| DECL_MODULE_PURVIEW_P (decl) = module_purview_p (); |
| /* If this was imported, we'll still be in the entity_hash. */ |
| DECL_MODULE_IMPORT_P (decl) = false; |
| } |
| } |
| |
| /* If DECL is a class member, whose class is not defined in this TU |
| (it was imported), remember this decl. */ |
| |
| void |
| set_defining_module (tree decl) |
| { |
| gcc_checking_assert (!DECL_LANG_SPECIFIC (decl) |
| || !DECL_MODULE_IMPORT_P (decl)); |
| |
| if (module_has_cmi_p ()) |
| { |
| tree ctx = DECL_CONTEXT (decl); |
| if (ctx |
| && (TREE_CODE (ctx) == RECORD_TYPE || TREE_CODE (ctx) == UNION_TYPE) |
| && DECL_LANG_SPECIFIC (TYPE_NAME (ctx)) |
| && DECL_MODULE_IMPORT_P (TYPE_NAME (ctx))) |
| { |
| /* This entity's context is from an import. We may need to |
| record this entity to make sure we emit it in the CMI. |
| Template specializations are in the template hash tables, |
| so we don't need to record them here as well. */ |
| int use_tpl = -1; |
| tree ti = node_template_info (decl, use_tpl); |
| if (use_tpl <= 0) |
| { |
| if (ti) |
| { |
| gcc_checking_assert (!use_tpl); |
| /* Get to the TEMPLATE_DECL. */ |
| decl = TI_TEMPLATE (ti); |
| } |
| |
| /* Record it on the class_members list. */ |
| vec_safe_push (class_members, decl); |
| } |
| } |
| else if (DECL_IMPLICIT_TYPEDEF_P (decl) |
| && CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl))) |
| /* This is a partial or explicit specialization. */ |
| vec_safe_push (partial_specializations, decl); |
| } |
| } |
| |
| void |
| set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNUSED) |
| { |
| set_instantiating_module (decl); |
| |
| if (TREE_CODE (CP_DECL_CONTEXT (decl)) != NAMESPACE_DECL) |
| return; |
| |
| gcc_checking_assert (friend_p || decl == get_originating_module_decl (decl)); |
| |
| if (!module_exporting_p ()) |
| return; |
| |
| // FIXME: Check ill-formed linkage |
| DECL_MODULE_EXPORT_P (decl) = true; |
| } |
| |
| /* DECL is attached to ROOT for odr purposes. */ |
| |
| void |
| maybe_attach_decl (tree ctx, tree decl) |
| { |
| if (!modules_p ()) |
| return; |
| |
| // FIXME: For now just deal with lambdas attached to var decls. |
| // This might be sufficient? |
| if (TREE_CODE (ctx) != VAR_DECL) |
| return; |
| |
| gcc_checking_assert (DECL_NAMESPACE_SCOPE_P (ctx)); |
| |
| if (!attached_table) |
| attached_table = new attached_map_t (EXPERIMENT (1, 400)); |
| |
| auto &vec = attached_table->get_or_insert (ctx); |
| if (!vec.length ()) |
| { |
| retrofit_lang_decl (ctx); |
| DECL_MODULE_ATTACHMENTS_P (ctx) = true; |
| } |
| vec.safe_push (decl); |
| } |
| |
| /* Create the flat name string. It is simplest to have it handy. */ |
| |
| void |
| module_state::set_flatname () |
| { |
| gcc_checking_assert (!flatname); |
| if (parent) |
| { |
| auto_vec<tree,5> ids; |
| size_t len = 0; |
| char const *primary = NULL; |
| size_t pfx_len = 0; |
| |
| for (module_state *probe = this; |
| probe; |
| probe = probe->parent) |
| if (is_partition () && !probe->is_partition ()) |
| { |
| primary = probe->get_flatname (); |
| pfx_len = strlen (primary); |
| break; |
| } |
| else |
| { |
| ids.safe_push (probe->name); |
| len += IDENTIFIER_LENGTH (probe->name) + 1; |
| } |
| |
| char *flat = XNEWVEC (char, pfx_len + len + is_partition ()); |
| flatname = flat; |
| |
| if (primary) |
| { |
| memcpy (flat, primary, pfx_len); |
| flat += pfx_len; |
| *flat++ = ':'; |
| } |
| |
| for (unsigned len = 0; ids.length ();) |
| { |
| if (len) |
| flat[len++] = '.'; |
| tree elt = ids.pop (); |
| unsigned l = IDENTIFIER_LENGTH (elt); |
| memcpy (flat + len, IDENTIFIER_POINTER (elt), l + 1); |
| len += l; |
| } |
| } |
| else if (is_header ()) |
| flatname = TREE_STRING_POINTER (name); |
| else |
| flatname = IDENTIFIER_POINTER (name); |
| } |
| |
| /* Read the CMI file for a module. */ |
| |
| bool |
| module_state::do_import (cpp_reader *reader, bool outermost) |
| { |
| gcc_assert (global_namespace == current_scope () && loadedness == ML_NONE); |
| |
| loc = linemap_module_loc (line_table, loc, get_flatname ()); |
| |
| if (lazy_open >= lazy_limit) |
| freeze_an_elf (); |
| |
| int fd = -1; |
| int e = ENOENT; |
| if (filename) |
| { |
| const char *file = maybe_add_cmi_prefix (filename); |
| dump () && dump ("CMI is %s", file); |
| if (note_module_cmi_yes || inform_cmi_p) |
| inform (loc, "reading CMI %qs", file); |
| fd = open (file, O_RDONLY | O_CLOEXEC | O_BINARY); |
| e = errno; |
| } |
| |
| gcc_checking_assert (!slurp); |
| slurp = new slurping (new elf_in (fd, e)); |
| |
| bool ok = true; |
| if (!from ()->get_error ()) |
| { |
| announce ("importing"); |
| loadedness = ML_CONFIG; |
| lazy_open++; |
| ok = read_initial (reader); |
| slurp->lru = ++lazy_lru; |
| } |
| |
| gcc_assert (slurp->current == ~0u); |
| |
| return check_read (outermost, ok); |
| } |
| |
| /* Attempt to increase the file descriptor limit. */ |
| |
| static bool |
| try_increase_lazy (unsigned want) |
| { |
| gcc_checking_assert (lazy_open >= lazy_limit); |
| |
| /* If we're increasing, saturate at hard limit. */ |
| if (want > lazy_hard_limit && lazy_limit < lazy_hard_limit) |
| want = lazy_hard_limit; |
| |
| #if HAVE_SETRLIMIT |
| if ((!lazy_limit || !param_lazy_modules) |
| && lazy_hard_limit |
| && want <= lazy_hard_limit) |
| { |
| struct rlimit rlimit; |
| rlimit.rlim_cur = want + LAZY_HEADROOM; |
| rlimit.rlim_max = lazy_hard_limit + LAZY_HEADROOM; |
| if (!setrlimit (RLIMIT_NOFILE, &rlimit)) |
| lazy_limit = want; |
| } |
| #endif |
| |
| return lazy_open < lazy_limit; |
| } |
| |
| /* Pick a victim module to freeze its reader. */ |
| |
| void |
| module_state::freeze_an_elf () |
| { |
| if (try_increase_lazy (lazy_open * 2)) |
| return; |
| |
| module_state *victim = NULL; |
| for (unsigned ix = modules->length (); ix--;) |
| { |
| module_state *candidate = (*modules)[ix]; |
| if (candidate && candidate->slurp && candidate->slurp->lru |
| && candidate->from ()->is_freezable () |
| && (!victim || victim->slurp->lru > candidate->slurp->lru)) |
| victim = candidate; |
| } |
| |
| if (victim) |
| { |
| dump () && dump ("Freezing '%s'", victim->filename); |
| if (victim->slurp->macro_defs.size) |
| /* Save the macro definitions to a buffer. */ |
| victim->from ()->preserve (victim->slurp->macro_defs); |
| if (victim->slurp->macro_tbl.size) |
| /* Save the macro definitions to a buffer. */ |
| victim->from ()->preserve (victim->slurp->macro_tbl); |
| victim->from ()->freeze (); |
| lazy_open--; |
| } |
| else |
| dump () && dump ("No module available for freezing"); |
| } |
| |
| /* Load the lazy slot *MSLOT, INDEX'th slot of the module. */ |
| |
| bool |
| module_state::lazy_load (unsigned index, binding_slot *mslot) |
| { |
| unsigned n = dump.push (this); |
| |
| gcc_checking_assert (function_depth); |
| |
| unsigned cookie = mslot->get_lazy (); |
| unsigned snum = cookie >> 2; |
| dump () && dump ("Loading entity %M[%u] section:%u", this, index, snum); |
| |
| bool ok = load_section (snum, mslot); |
| |
| dump.pop (n); |
| |
| return ok; |
| } |
| |
| /* Load MOD's binding for NS::ID into *MSLOT. *MSLOT contains the |
| lazy cookie. OUTER is true if this is the outermost lazy, (used |
| for diagnostics). */ |
| |
| void |
| lazy_load_binding (unsigned mod, tree ns, tree id, binding_slot *mslot) |
| { |
| int count = errorcount + warningcount; |
| |
| timevar_start (TV_MODULE_IMPORT); |
| |
| /* Stop GC happening, even in outermost loads (because our caller |
| could well be building up a lookup set). */ |
| function_depth++; |
| |
| gcc_checking_assert (mod); |
| module_state *module = (*modules)[mod]; |
| unsigned n = dump.push (module); |
| |
| unsigned snum = mslot->get_lazy (); |
| dump () && dump ("Lazily binding %P@%N section:%u", ns, id, |
| module->name, snum); |
| |
| bool ok = !recursive_lazy (snum); |
| if (ok) |
| { |
| ok = module->load_section (snum, mslot); |
| lazy_snum = 0; |
| post_load_processing (); |
| } |
| |
| dump.pop (n); |
| |
| function_depth--; |
| |
| timevar_stop (TV_MODULE_IMPORT); |
| |
| if (!ok) |
| fatal_error (input_location, |
| module->is_header () |
| ? G_("failed to load binding %<%E%s%E%>") |
| : G_("failed to load binding %<%E%s%E@%s%>"), |
| ns, &"::"[ns == global_namespace ? 2 : 0], id, |
| module->get_flatname ()); |
| |
| if (count != errorcount + warningcount) |
| inform (input_location, |
| module->is_header () |
| ? G_("during load of binding %<%E%s%E%>") |
| : G_("during load of binding %<%E%s%E@%s%>"), |
| ns, &"::"[ns == global_namespace ? 2 : 0], id, |
| module->get_flatname ()); |
| } |
| |
| /* Load any pending entities keyed to the top-key of DECL. */ |
| |
| void |
| lazy_load_pendings (tree decl) |
| { |
| tree key_decl; |
| pending_key key; |
| key.ns = find_pending_key (decl, &key_decl); |
| key.id = DECL_NAME (key_decl); |
| |
| auto *pending_vec = pending_table ? pending_table->get (key) : nullptr; |
| if (!pending_vec) |
| return; |
| |
| int count = errorcount + warningcount; |
| |
| timevar_start (TV_MODULE_IMPORT); |
| bool ok = !recursive_lazy (); |
| if (ok) |
| { |
| function_depth++; /* Prevent GC */ |
| unsigned n = dump.push (NULL); |
| dump () && dump ("Reading %u pending entities keyed to %P", |
| pending_vec->length (), key.ns, key.id); |
| for (unsigned ix = pending_vec->length (); ix--;) |
| { |
| unsigned index = (*pending_vec)[ix]; |
| binding_slot *slot = &(*entity_ary)[index]; |
| |
| if (slot->is_lazy ()) |
| { |
| module_state *import = import_entity_module (index); |
| if (!import->lazy_load (index - import->entity_lwm, slot)) |
| ok = false; |
| } |
| else if (dump ()) |
| { |
| module_state *import = import_entity_module (index); |
| dump () && dump ("Entity %M[%u] already loaded", |
| import, index - import->entity_lwm); |
| } |
| } |
| |
| pending_table->remove (key); |
| dump.pop (n); |
| lazy_snum = 0; |
| post_load_processing (); |
| function_depth--; |
| } |
| |
| timevar_stop (TV_MODULE_IMPORT); |
| |
| if (!ok) |
| fatal_error (input_location, "failed to load pendings for %<%E%s%E%>", |
| key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id); |
| |
| if (count != errorcount + warningcount) |
| inform (input_location, "during load of pendings for %<%E%s%E%>", |
| key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id); |
| } |
| |
| static void |
| direct_import (module_state *import, cpp_reader *reader) |
| { |
| timevar_start (TV_MODULE_IMPORT); |
| unsigned n = dump.push (import); |
| |
| gcc_checking_assert (import->is_direct () && import->has_location ()); |
| if (import->loadedness == ML_NONE) |
| if (!import->do_import (reader, true)) |
| gcc_unreachable (); |
| |
| if (import->loadedness < ML_LANGUAGE) |
| { |
| if (!attached_table) |
| attached_table = new attached_map_t (EXPERIMENT (1, 400)); |
| import->read_language (true); |
| } |
| |
| (*modules)[0]->set_import (import, import->exported_p); |
| |
| dump.pop (n); |
| timevar_stop (TV_MODULE_IMPORT); |
| } |
| |
| /* Import module IMPORT. */ |
| |
| void |
| import_module (module_state *import, location_t from_loc, bool exporting_p, |
| tree, cpp_reader *reader) |
| { |
| if (!import->check_not_purview (from_loc)) |
| return; |
| |
| if (!import->is_header () && current_lang_depth ()) |
| /* Only header units should appear inside language |
| specifications. The std doesn't specify this, but I think |
| that's an error in resolving US 033, because language linkage |
| is also our escape clause to getting things into the global |
| module, so we don't want to confuse things by having to think |
| about whether 'extern "C++" { import foo; }' puts foo's |
| contents into the global module all of a sudden. */ |
| warning (0, "import of named module %qs inside language-linkage block", |
| import->get_flatname ()); |
| |
| if (exporting_p || module_exporting_p ()) |
| import->exported_p = true; |
| |
| if (import->loadedness != ML_NONE) |
| { |
| from_loc = ordinary_loc_of (line_table, from_loc); |
| linemap_module_reparent (line_table, import->loc, from_loc); |
| } |
| gcc_checking_assert (!import->module_p); |
| gcc_checking_assert (import->is_direct () && import->has_location ()); |
| |
| direct_import (import, reader); |
| } |
| |
| /* Declare the name of the current module to be NAME. EXPORTING_p is |
| true if this TU is the exporting module unit. */ |
| |
| void |
| declare_module (module_state *module, location_t from_loc, bool exporting_p, |
| tree, cpp_reader *reader) |
| { |
| gcc_assert (global_namespace == current_scope ()); |
| |
| module_state *current = (*modules)[0]; |
| if (module_purview_p () || module->loadedness > ML_CONFIG) |
| { |
| error_at (from_loc, module_purview_p () |
| ? G_("module already declared") |
| : G_("module already imported")); |
| if (module_purview_p ()) |
| module = current; |
| inform (module->loc, module_purview_p () |
| ? G_("module %qs declared here") |
| : G_("module %qs imported here"), |
| module->get_flatname ()); |
| return; |
| } |
| |
| gcc_checking_assert (module->module_p); |
| gcc_checking_assert (module->is_direct () && module->has_location ()); |
| |
| /* Yer a module, 'arry. */ |
| module_kind &= ~MK_GLOBAL; |
| module_kind |= MK_MODULE; |
| |
| if (module->is_partition () || exporting_p) |
| { |
| gcc_checking_assert (module->get_flatname ()); |
| |
| if (module->is_partition ()) |
| module_kind |= MK_PARTITION; |
| |
| if (exporting_p) |
| { |
| module->interface_p = true; |
| module_kind |= MK_INTERFACE; |
| } |
| |
| if (module->is_header ()) |
| module_kind |= MK_GLOBAL | MK_EXPORTING; |
| |
| /* Copy the importing information we may have already done. We |
| do not need to separate out the imports that only happen in |
| the GMF, inspite of what the literal wording of the std |
| might imply. See p2191, the core list had a discussion |
| where the module implementors agreed that the GMF of a named |
| module is invisible to importers. */ |
| module->imports = current->imports; |
| |
| module->mod = 0; |
| (*modules)[0] = module; |
| } |
| else |
| { |
| module->interface_p = true; |
| current->parent = module; /* So mangler knows module identity. */ |
| direct_import (module, reader); |
| } |
| } |
| |
| /* +1, we're the primary or a partition. Therefore emitting a |
| globally-callable idemportent initializer function. |
| -1, we have direct imports. Therefore emitting calls to their |
| initializers. */ |
| |
| int |
| module_initializer_kind () |
| { |
| int result = 0; |
| |
| if (module_has_cmi_p () && !header_module_p ()) |
| result = +1; |
| else if (num_init_calls_needed) |
| result = -1; |
| |
| return result; |
| } |
| |
| /* Emit calls to each direct import's global initializer. Including |
| direct imports of directly imported header units. The initializers |
| of (static) entities in header units will be called by their |
| importing modules (for the instance contained within that), or by |
| the current TU (for the instances we've brought in). Of course |
| such header unit behaviour is evil, but iostream went through that |
| door some time ago. */ |
| |
| void |
| module_add_import_initializers () |
| { |
| unsigned calls = 0; |
| if (modules) |
| { |
| tree fntype = build_function_type (void_type_node, void_list_node); |
| releasing_vec args; // There are no args |
| |
| for (unsigned ix = modules->length (); --ix;) |
| { |
| module_state *import = (*modules)[ix]; |
| if (import->call_init_p) |
| { |
| tree name = mangle_module_global_init (ix); |
| tree fndecl = build_lang_decl (FUNCTION_DECL, name, fntype); |
| |
| DECL_CONTEXT (fndecl) = FROB_CONTEXT (global_namespace); |
| SET_DECL_ASSEMBLER_NAME (fndecl, name); |
| TREE_PUBLIC (fndecl) = true; |
| determine_visibility (fndecl); |
| |
| tree call = cp_build_function_call_vec (fndecl, &args, |
| tf_warning_or_error); |
| finish_expr_stmt (call); |
| |
| calls++; |
| } |
| } |
| } |
| |
| gcc_checking_assert (calls == num_init_calls_needed); |
| } |
| |
| /* NAME & LEN are a preprocessed header name, possibly including the |
| surrounding "" or <> characters. Return the raw string name of the |
| module to which it refers. This will be an absolute path, or begin |
| with ./, so it is immediately distinguishable from a (non-header |
| unit) module name. If READER is non-null, ask the preprocessor to |
| locate the header to which it refers using the appropriate include |
| path. Note that we do never do \ processing of the string, as that |
| matches the preprocessor's behaviour. */ |
| |
| static const char * |
| canonicalize_header_name (cpp_reader *reader, location_t loc, bool unquoted, |
| const char *str, size_t &len_r) |
| { |
| size_t len = len_r; |
| static char *buf = 0; |
| static size_t alloc = 0; |
| |
| if (!unquoted) |
| { |
| gcc_checking_assert (len >= 2 |
| && ((reader && str[0] == '<' && str[len-1] == '>') |
| || (str[0] == '"' && str[len-1] == '"'))); |
| str += 1; |
| len -= 2; |
| } |
| |
| if (reader) |
| { |
| gcc_assert (!unquoted); |
| |
| if (len >= alloc) |
| { |
| alloc = len + 1; |
| buf = XRESIZEVEC (char, buf, alloc); |
| } |
| memcpy (buf, str, len); |
| buf[len] = 0; |
| |
| if (const char *hdr |
| = cpp_probe_header_unit (reader, buf, str[-1] == '<', loc)) |
| { |
| len = strlen (hdr); |
| str = hdr; |
| } |
| else |
| str = buf; |
| } |
| |
| if (!(str[0] == '.' ? IS_DIR_SEPARATOR (str[1]) : IS_ABSOLUTE_PATH (str))) |
| { |
| /* Prepend './' */ |
| if (len + 3 > alloc) |
| { |
| alloc = len + 3; |
| buf = XRESIZEVEC (char, buf, alloc); |
| } |
| |
| buf[0] = '.'; |
| buf[1] = DIR_SEPARATOR; |
| memmove (buf + 2, str, len); |
| len += 2; |
| buf[len] = 0; |
| str = buf; |
| } |
| |
| len_r = len; |
| return str; |
| } |
| |
| /* Set the CMI name from a cody packet. Issue an error if |
| ill-formed. */ |
| |
| void module_state::set_filename (const Cody::Packet &packet) |
| { |
| gcc_checking_assert (!filename); |
| if (packet.GetCode () == Cody::Client::PC_PATHNAME) |
| filename = xstrdup (packet.GetString ().c_str ()); |
| else |
| { |
| gcc_checking_assert (packet.GetCode () == Cody::Client::PC_ERROR); |
| error_at (loc, "unknown Compiled Module Interface: %s", |
| packet.GetString ().c_str ()); |
| } |
| } |
| |
| /* Figure out whether to treat HEADER as an include or an import. */ |
| |
| static char * |
| maybe_translate_include (cpp_reader *reader, line_maps *lmaps, location_t loc, |
| const char *path) |
| { |
| if (!modules_p ()) |
| { |
| /* Turn off. */ |
| cpp_get_callbacks (reader)->translate_include = NULL; |
| return nullptr; |
| } |
| |
| if (!spans.init_p ()) |
| /* Before the main file, don't divert. */ |
| return nullptr; |
| |
| dump.push (NULL); |
| |
| dump () && dump ("Checking include translation '%s'", path); |
| auto *mapper = get_mapper (cpp_main_loc (reader)); |
| |
| size_t len = strlen (path); |
| path = canonicalize_header_name (NULL, loc, true, path, len); |
| auto packet = mapper->IncludeTranslate (path, Cody::Flags::None, len); |
| int xlate = false; |
| if (packet.GetCode () == Cody::Client::PC_BOOL) |
| xlate = -int (packet.GetInteger ()); |
| else if (packet.GetCode () == Cody::Client::PC_PATHNAME) |
| { |
| /* Record the CMI name for when we do the import. */ |
| module_state *import = get_module (build_string (len, path)); |
| import->set_filename (packet); |
| xlate = +1; |
| } |
| else |
| { |
| gcc_checking_assert (packet.GetCode () == Cody::Client::PC_ERROR); |
| error_at (loc, "cannot determine %<#include%> translation of %s: %s", |
| path, packet.GetString ().c_str ()); |
| } |
| |
| bool note = false; |
| if (note_include_translate_yes && xlate > 1) |
| note = true; |
| else if (note_include_translate_no && xlate == 0) |
| note = true; |
| else if (note_includes) |
| /* We do not expect the note_includes vector to be large, so O(N) |
| iteration. */ |
| for (unsigned ix = note_includes->length (); !note && ix--;) |
| if (!strcmp ((*note_includes)[ix], path)) |
| note = true; |
| |
| if (note) |
| inform (loc, xlate |
| ? G_("include %qs translated to import") |
| : G_("include %qs processed textually") , path); |
| |
| dump () && dump (xlate ? "Translating include to import" |
| : "Keeping include as include"); |
| dump.pop (0); |
| |
| if (!(xlate > 0)) |
| return nullptr; |
| |
| /* Create the translation text. */ |
| loc = ordinary_loc_of (lmaps, loc); |
| const line_map_ordinary *map |
| = linemap_check_ordinary (linemap_lookup (lmaps, loc)); |
| unsigned col = SOURCE_COLUMN (map, loc); |
| col -= (col != 0); /* Columns are 1-based. */ |
| |
| unsigned alloc = len + col + 60; |
| char *res = XNEWVEC (char, alloc); |
| |
| strcpy (res, "__import"); |
| unsigned actual = 8; |
| if (col > actual) |
| { |
| /* Pad out so the filename appears at the same position. */ |
| memset (res + actual, ' ', col - actual); |
| actual = col; |
| } |
| /* No need to encode characters, that's not how header names are |
| handled. */ |
| actual += snprintf (res + actual, alloc - actual, |
| "\"%s\" [[__translated]];\n", path); |
| gcc_checking_assert (actual < alloc); |
| |
| /* cpplib will delete the buffer. */ |
| return res; |
| } |
| |
| static void |
| begin_header_unit (cpp_reader *reader) |
| { |
| /* Set the module header name from the main_input_filename. */ |
| const char *main = main_input_filename; |
| size_t len = strlen (main); |
| main = canonicalize_header_name (NULL, 0, true, main, len); |
| module_state *module = get_module (build_string (len, main)); |
| |
| preprocess_module (module, cpp_main_loc (reader), false, false, true, reader); |
| } |
| |
| /* We've just properly entered the main source file. I.e. after the |
| command line, builtins and forced headers. Record the line map and |
| location of this map. Note we may be called more than once. The |
| first call sticks. */ |
| |
| void |
| module_begin_main_file (cpp_reader *reader, line_maps *lmaps, |
| const line_map_ordinary *map) |
| { |
| gcc_checking_assert (lmaps == line_table); |
| if (modules_p () && !spans.init_p ()) |
| { |
| unsigned n = dump.push (NULL); |
| spans.init (lmaps, map); |
| dump.pop (n); |
| if (flag_header_unit && !cpp_get_options (reader)->preprocessed) |
| { |
| /* Tell the preprocessor this is an include file. */ |
| cpp_retrofit_as_include (reader); |
| begin_header_unit (reader); |
| } |
| } |
| } |
| |
| /* Process the pending_import queue, making sure we know the |
| filenames. */ |
| |
| static void |
| name_pending_imports (cpp_reader *reader) |
| { |
| auto *mapper = get_mapper (cpp_main_loc (reader)); |
| |
| if (!vec_safe_length (pending_imports)) |
| /* Not doing anything. */ |
| return; |
| |
| timevar_start (TV_MODULE_MAPPER); |
| |
| auto n = dump.push (NULL); |
| dump () && dump ("Resolving direct import names"); |
| bool want_deps = (bool (mapper->get_flags () & Cody::Flags::NameOnly) |
| || cpp_get_deps (reader)); |
| bool any = false; |
| |
| for (unsigned ix = 0; ix != pending_imports->length (); ix++) |
| { |
| module_state *module = (*pending_imports)[ix]; |
| gcc_checking_assert (module->is_direct ()); |
| if (!module->filename && !module->visited_p) |
| { |
| bool export_p = (module->module_p |
| && (module->is_partition () || module->exported_p)); |
| |
| Cody::Flags flags = Cody::Flags::None; |
| if (flag_preprocess_only |
| && !(module->is_header () && !export_p)) |
| { |
| if (!want_deps) |
| continue; |
| flags = Cody::Flags::NameOnly; |
| } |
| |
| if (!any) |
| { |
| any = true; |
| mapper->Cork (); |
| } |
| if (export_p) |
| mapper->ModuleExport (module->get_flatname (), flags); |
| else |
| mapper->ModuleImport (module->get_flatname (), flags); |
| module->visited_p = true; |
| } |
| } |
| |
| if (any) |
| { |
| auto response = mapper->Uncork (); |
| auto r_iter = response.begin (); |
| for (unsigned ix = 0; ix != pending_imports->length (); ix++) |
| { |
| module_state *module = (*pending_imports)[ix]; |
| if (module->visited_p) |
| { |
| module->visited_p = false; |
| gcc_checking_assert (!module->filename); |
| |
| module->set_filename (*r_iter); |
| ++r_iter; |
| } |
| } |
| } |
| |
| dump.pop (n); |
| |
| timevar_stop (TV_MODULE_MAPPER); |
| } |
| |
| /* We've just lexed a module-specific control line for MODULE. Mark |
| the module as a direct import, and possibly load up its macro |
| state. Returns the primary module, if this is a module |
| declaration. */ |
| /* Perhaps we should offer a preprocessing mode where we read the |
| directives from the header unit, rather than require the header's |
| CMI. */ |
| |
| module_state * |
| preprocess_module (module_state *module, location_t from_loc, |
| bool in_purview, bool is_import, bool is_export, |
| cpp_reader *reader) |
| { |
| if (!is_import) |
| { |
| if (module->loc) |
| /* It's already been mentioned, so ignore its module-ness. */ |
| is_import = true; |
| else |
| { |
| /* Record it is the module. */ |
| module->module_p = true; |
| if (is_export) |
| { |
| module->exported_p = true; |
| module->interface_p = true; |
| } |
| } |
| } |
| |
| if (module->directness < MD_DIRECT + in_purview) |
| { |
| /* Mark as a direct import. */ |
| module->directness = module_directness (MD_DIRECT + in_purview); |
| |
| /* Set the location to be most informative for users. */ |
| from_loc = ordinary_loc_of (line_table, from_loc); |
| if (module->loadedness != ML_NONE) |
| linemap_module_reparent (line_table, module->loc, from_loc); |
| else |
| { |
| module->loc = from_loc; |
| if (!module->flatname) |
| module->set_flatname (); |
| } |
| } |
| |
| auto desired = ML_CONFIG; |
| if (is_import |
| && module->is_header () |
| && (!cpp_get_options (reader)->preprocessed |
| || cpp_get_options (reader)->directives_only)) |
| /* We need preprocessor state now. */ |
| desired = ML_PREPROCESSOR; |
| |
| if (!is_import || module->loadedness < desired) |
| { |
| vec_safe_push (pending_imports, module); |
| |
| if (desired == ML_PREPROCESSOR) |
| { |
| unsigned n = dump.push (NULL); |
| |
| dump () && dump ("Reading %M preprocessor state", module); |
| name_pending_imports (reader); |
| |
| /* Preserve the state of the line-map. */ |
| unsigned pre_hwm = LINEMAPS_ORDINARY_USED (line_table); |
| |
| /* We only need to close the span, if we're going to emit a |
| CMI. But that's a little tricky -- our token scanner |
| needs to be smarter -- and this isn't much state. |
| Remember, we've not parsed anything at this point, so |
| our module state flags are inadequate. */ |
| spans.maybe_init (); |
| spans.close (); |
| |
| timevar_start (TV_MODULE_IMPORT); |
| |
| /* Load the config of each pending import -- we must assign |
| module numbers monotonically. */ |
| for (unsigned ix = 0; ix != pending_imports->length (); ix++) |
| { |
| auto *import = (*pending_imports)[ix]; |
| if (!(import->module_p |
| && (import->is_partition () || import->exported_p)) |
| && import->loadedness == ML_NONE |
| && (import->is_header () || !flag_preprocess_only)) |
| { |
| unsigned n = dump.push (import); |
| import->do_import (reader, true); |
| dump.pop (n); |
| } |
| } |
| vec_free (pending_imports); |
| |
| /* Restore the line-map state. */ |
| spans.open (linemap_module_restore (line_table, pre_hwm)); |
| |
| /* Now read the preprocessor state of this particular |
| import. */ |
| if (module->loadedness == ML_CONFIG |
| && module->read_preprocessor (true)) |
| module->import_macros (); |
| |
| timevar_stop (TV_MODULE_IMPORT); |
| |
| dump.pop (n); |
| } |
| } |
| |
| return is_import ? NULL : get_primary (module); |
| } |
| |
| /* We've completed phase-4 translation. Emit any dependency |
| information for the not-yet-loaded direct imports, and fill in |
| their file names. We'll have already loaded up the direct header |
| unit wavefront. */ |
| |
| void |
| preprocessed_module (cpp_reader *reader) |
| { |
| unsigned n = dump.push (NULL); |
| |
| dump () && dump ("Completed phase-4 (tokenization) processing"); |
| |
| name_pending_imports (reader); |
| vec_free (pending_imports); |
| |
| spans.maybe_init (); |
| spans.close (); |
| |
| using iterator = hash_table<module_state_hash>::iterator; |
| if (mkdeps *deps = cpp_get_deps (reader)) |
| { |
| /* Walk the module hash, informing the dependency machinery. */ |
| iterator end = modules_hash->end (); |
| for (iterator iter = modules_hash->begin (); iter != end; ++iter) |
| { |
| module_state *module = *iter; |
| |
| if (module->is_direct ()) |
| { |
| if (module->is_module () |
| && (module->is_interface () || module->is_partition ())) |
| deps_add_module_target (deps, module->get_flatname (), |
| maybe_add_cmi_prefix (module->filename), |
| module->is_header()); |
| else |
| deps_add_module_dep (deps, module->get_flatname ()); |
| } |
| } |
| } |
| |
| if (flag_header_unit && !flag_preprocess_only) |
| { |
| /* Find the main module -- remember, it's not yet in the module |
| array. */ |
| iterator end = modules_hash->end (); |
| for (iterator iter = modules_hash->begin (); iter != end; ++iter) |
| { |
| module_state *module = *iter; |
| if (module->is_module ()) |
| { |
| declare_module (module, cpp_main_loc (reader), true, NULL, reader); |
| break; |
| } |
| } |
| } |
| |
| dump.pop (n); |
| } |
| |
| /* VAL is a global tree, add it to the global vec if it is |
| interesting. Add some of its targets, if they too are |
| interesting. We do not add identifiers, as they can be re-found |
| via the identifier hash table. There is a cost to the number of |
| global trees. */ |
| |
| static int |
| maybe_add_global (tree val, unsigned &crc) |
| { |
| int v = 0; |
| |
| if (val && !(identifier_p (val) || TREE_VISITED (val))) |
| { |
| TREE_VISITED (val) = true; |
| crc = crc32_unsigned (crc, fixed_trees->length ()); |
| vec_safe_push (fixed_trees, val); |
| v++; |
| |
| if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPED)) |
| v += maybe_add_global (TREE_TYPE (val), crc); |
| if (CODE_CONTAINS_STRUCT (TREE_CODE (val), TS_TYPE_COMMON)) |
| v += maybe_add_global (TYPE_NAME (val), crc); |
| } |
| |
| return v; |
| } |
| |
| /* Initialize module state. Create the hash table, determine the |
| global trees. Create the module for current TU. */ |
| |
| void |
| init_modules (cpp_reader *reader) |
| { |
| /* PCH should not be reachable because of lang-specs, but the |
| user could have overriden that. */ |
| if (pch_file) |
| fatal_error (input_location, |
| "C++ modules are incompatible with precompiled headers"); |
| |
| if (cpp_get_options (reader)->traditional) |
| fatal_error (input_location, |
| "C++ modules are incompatible with traditional preprocessing"); |
| |
| if (flag_preprocess_only) |
| { |
| cpp_options *cpp_opts = cpp_get_options (reader); |
| if (flag_no_output |
| || (cpp_opts->deps.style != DEPS_NONE |
| && !cpp_opts->deps.need_preprocessor_output)) |
| { |
| warning (0, flag_dump_macros == 'M' |
| ? G_("macro debug output may be incomplete with modules") |
| : G_("module dependencies require preprocessing")); |
| if (cpp_opts->deps.style != DEPS_NONE) |
| inform (input_location, "you should use the %<-%s%> option", |
| cpp_opts->deps.style == DEPS_SYSTEM ? "MD" : "MMD"); |
| } |
| } |
| |
| /* :: is always exported. */ |
| DECL_MODULE_EXPORT_P (global_namespace) = true; |
| |
| modules_hash = hash_table<module_state_hash>::create_ggc (31); |
| vec_safe_reserve (modules, 20); |
| |
| /* Create module for current TU. */ |
| module_state *current |
| = new (ggc_alloc<module_state> ()) module_state (NULL_TREE, NULL, false); |
| current->mod = 0; |
| bitmap_set_bit (current->imports, 0); |
| modules->quick_push (current); |
| |
| gcc_checking_assert (!fixed_trees); |
| |
| headers = BITMAP_GGC_ALLOC (); |
| |
| if (note_includes) |
| /* Canonicalize header names. */ |
| for (unsigned ix = 0; ix != note_includes->length (); ix++) |
| { |
| const char *hdr = (*note_includes)[ix]; |
| size_t len = strlen (hdr); |
| |
| bool system = hdr[0] == '<'; |
| bool user = hdr[0] == '"'; |
| bool delimed = system || user; |
| |
| if (len <= (delimed ? 2 : 0) |
| || (delimed && hdr[len-1] != (system ? '>' : '"'))) |
| error ("invalid header name %qs", hdr); |
| |
| hdr = canonicalize_header_name (delimed ? reader : NULL, |
| 0, !delimed, hdr, len); |
| char *path = XNEWVEC (char, len + 1); |
| memcpy (path, hdr, len); |
| path[len] = 0; |
| |
| (*note_includes)[ix] = path; |
| } |
| |
| if (note_cmis) |
| /* Canonicalize & mark module names. */ |
| for (unsigned ix = 0; ix != note_cmis->length (); ix++) |
| { |
| const char *name = (*note_cmis)[ix]; |
| size_t len = strlen (name); |
| |
| bool is_system = name[0] == '<'; |
| bool is_user = name[0] == '"'; |
| bool is_pathname = false; |
| if (!(is_system || is_user)) |
| for (unsigned ix = len; !is_pathname && ix--;) |
| is_pathname = IS_DIR_SEPARATOR (name[ix]); |
| if (is_system || is_user || is_pathname) |
| { |
| if (len <= (is_pathname ? 0 : 2) |
| || (!is_pathname && name[len-1] != (is_system ? '>' : '"'))) |
| { |
| error ("invalid header name %qs", name); |
| continue; |
| } |
| else |
| name = canonicalize_header_name (is_pathname ? nullptr : reader, |
| 0, is_pathname, name, len); |
| } |
| if (auto module = get_module (name)) |
| module->inform_cmi_p = 1; |
| else |
| error ("invalid module name %qs", name); |
| } |
| |
| dump.push (NULL); |
| |
| /* Determine lazy handle bound. */ |
| { |
| unsigned limit = 1000; |
| #if HAVE_GETRLIMIT |
| struct rlimit rlimit; |
| if (!getrlimit (RLIMIT_NOFILE, &rlimit)) |
| { |
| lazy_hard_limit = (rlimit.rlim_max < 1000000 |
| ? unsigned (rlimit.rlim_max) : 1000000); |
| lazy_hard_limit = (lazy_hard_limit > LAZY_HEADROOM |
| ? lazy_hard_limit - LAZY_HEADROOM : 0); |
| if (rlimit.rlim_cur < limit) |
| limit = unsigned (rlimit.rlim_cur); |
| } |
| #endif |
| limit = limit > LAZY_HEADROOM ? limit - LAZY_HEADROOM : 1; |
| |
| if (unsigned parm = param_lazy_modules) |
| { |
| if (parm <= limit || !lazy_hard_limit || !try_increase_lazy (parm)) |
| lazy_limit = parm; |
| } |
| else |
| lazy_limit = limit; |
| } |
| |
| if (dump ()) |
| { |
| verstr_t ver; |
| version2string (MODULE_VERSION, ver); |
| dump ("Source: %s", main_input_filename); |
| dump ("Compiler: %s", version_string); |
| dump ("Modules: %s", ver); |
| dump ("Checking: %s", |
| #if CHECKING_P |
| "checking" |
| #elif ENABLE_ASSERT_CHECKING |
| "asserting" |
| #else |
| "release" |
| #endif |
| ); |
| dump ("Compiled by: " |
| #ifdef __GNUC__ |
| "GCC %d.%d, %s", __GNUC__, __GNUC_MINOR__, |
| #ifdef __OPTIMIZE__ |
| "optimizing" |
| #else |
| "not optimizing" |
| #endif |
| #else |
| "not GCC" |
| #endif |
| ); |
| dump ("Reading: %s", MAPPED_READING ? "mmap" : "fileio"); |
| dump ("Writing: %s", MAPPED_WRITING ? "mmap" : "fileio"); |
| dump ("Lazy limit: %u", lazy_limit); |
| dump ("Lazy hard limit: %u", lazy_hard_limit); |
| dump (""); |
| } |
| |
| /* Construct the global tree array. This is an array of unique |
| global trees (& types). Do this now, rather than lazily, as |
| some global trees are lazily created and we don't want that to |
| mess with our syndrome of fixed trees. */ |
| unsigned crc = 0; |
| vec_alloc (fixed_trees, 200); |
| |
| dump () && dump ("+Creating globals"); |
| /* Insert the TRANSLATION_UNIT_DECL. */ |
| TREE_VISITED (DECL_CONTEXT (global_namespace)) = true; |
| fixed_trees->quick_push (DECL_CONTEXT (global_namespace)); |
| for (unsigned jx = 0; global_tree_arys[jx].first; jx++) |
| { |
| const tree *ptr = global_tree_arys[jx].first; |
| unsigned limit = global_tree_arys[jx].second; |
| |
| for (unsigned ix = 0; ix != limit; ix++, ptr++) |
| { |
| !(ix & 31) && dump ("") && dump ("+\t%u:%u:", jx, ix); |
| unsigned v = maybe_add_global (*ptr, crc); |
| dump () && dump ("+%u", v); |
| } |
| } |
| global_crc = crc32_unsigned (crc, fixed_trees->length ()); |
| dump ("") && dump ("Created %u unique globals, crc=%x", |
| fixed_trees->length (), global_crc); |
| for (unsigned ix = fixed_trees->length (); ix--;) |
| TREE_VISITED ((*fixed_trees)[ix]) = false; |
| |
| dump.pop (0); |
| |
| if (!flag_module_lazy) |
| /* Get the mapper now, if we're not being lazy. */ |
| get_mapper (cpp_main_loc (reader)); |
| |
| if (!flag_preprocess_only) |
| { |
| pending_table = new pending_map_t (EXPERIMENT (1, 400)); |
| entity_map = new entity_map_t (EXPERIMENT (1, 400)); |
| vec_safe_reserve (entity_ary, EXPERIMENT (1, 400)); |
| } |
| |
| #if CHECKING_P |
| note_defs = note_defs_table_t::create_ggc (1000); |
| #endif |
| |
| if (flag_header_unit && cpp_get_options (reader)->preprocessed) |
| begin_header_unit (reader); |
| |
| /* Collect here to make sure things are tagged correctly (when |
| aggressively GC'd). */ |
| ggc_collect (); |
| } |
| |
| /* If NODE is a deferred macro, load it. */ |
| |
| static int |
| load_macros (cpp_reader *reader, cpp_hashnode *node, void *) |
| { |
| location_t main_loc |
| = MAP_START_LOCATION (LINEMAPS_ORDINARY_MAP_AT (line_table, 0)); |
| |
| if (cpp_user_macro_p (node) |
| && !node->value.macro) |
| { |
| cpp_macro *macro = cpp_get_deferred_macro (reader, node, main_loc); |
| dump () && dump ("Loaded macro #%s %I", |
| macro ? "define" : "undef", identifier (node)); |
| } |
| |
| return 1; |
| } |
| |
| /* At the end of tokenizing, we no longer need the macro tables of |
| imports. But the user might have requested some checking. */ |
| |
| void |
| maybe_check_all_macros (cpp_reader *reader) |
| { |
| if (!warn_imported_macros) |
| return; |
| |
| /* Force loading of any remaining deferred macros. This will |
| produce diagnostics if they are ill-formed. */ |
| unsigned n = dump.push (NULL); |
| cpp_forall_identifiers (reader, load_macros, NULL); |
| dump.pop (n); |
| } |
| |
| /* Write the CMI, if we're a module interface. */ |
| |
| void |
| finish_module_processing (cpp_reader *reader) |
| { |
| if (header_module_p ()) |
| module_kind &= ~MK_EXPORTING; |
| |
| if (!modules || !(*modules)[0]->name) |
| { |
| if (flag_module_only) |
| warning (0, "%<-fmodule-only%> used for non-interface"); |
| } |
| else if (!flag_syntax_only) |
| { |
| int fd = -1; |
| int e = ENOENT; |
| |
| timevar_start (TV_MODULE_EXPORT); |
| |
| /* Force a valid but empty line map at the end. This simplifies |
| the line table preparation and writing logic. */ |
| linemap_add (line_table, LC_ENTER, false, "", 0); |
| |
| /* We write to a tmpname, and then atomically rename. */ |
| const char *path = NULL; |
| char *tmp_name = NULL; |
| module_state *state = (*modules)[0]; |
| |
| unsigned n = dump.push (state); |
| state->announce ("creating"); |
| if (state->filename) |
| { |
| size_t len = 0; |
| path = maybe_add_cmi_prefix (state->filename, &len); |
| tmp_name = XNEWVEC (char, len + 3); |
| memcpy (tmp_name, path, len); |
| strcpy (&tmp_name[len], "~"); |
| |
| if (!errorcount) |
| for (unsigned again = 2; ; again--) |
| { |
| fd = open (tmp_name, |
| O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, |
| S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); |
| e = errno; |
| if (fd >= 0 || !again || e != ENOENT) |
| break; |
| create_dirs (tmp_name); |
| } |
| if (note_module_cmi_yes || state->inform_cmi_p) |
| inform (state->loc, "writing CMI %qs", path); |
| dump () && dump ("CMI is %s", path); |
| } |
| |
| if (errorcount) |
| warning_at (state->loc, 0, "not writing module %qs due to errors", |
| state->get_flatname ()); |
| else |
| { |
| elf_out to (fd, e); |
| if (to.begin ()) |
| { |
| auto loc = input_location; |
| /* So crashes finger-point the module decl. */ |
| input_location = state->loc; |
| state->write (&to, reader); |
| input_location = loc; |
| } |
| if (to.end ()) |
| { |
| /* Some OS's do not replace NEWNAME if it already |
| exists. This'll have a race condition in erroneous |
| concurrent builds. */ |
| unlink (path); |
| if (rename (tmp_name, path)) |
| { |
| dump () && dump ("Rename ('%s','%s') errno=%u", errno); |
| to.set_error (errno); |
| } |
| } |
| |
| if (to.get_error ()) |
| { |
| error_at (state->loc, "failed to write compiled module: %s", |
| to.get_error (state->filename)); |
| state->note_cmi_name (); |
| } |
| } |
| |
| if (!errorcount) |
| { |
| auto *mapper = get_mapper (cpp_main_loc (reader)); |
| |
| mapper->ModuleCompiled (state->get_flatname ()); |
| } |
| else if (path) |
| { |
| /* We failed, attempt to erase all evidence we even tried. */ |
| unlink (tmp_name); |
| unlink (path); |
| XDELETEVEC (tmp_name); |
| } |
| |
| dump.pop (n); |
| timevar_stop (TV_MODULE_EXPORT); |
| |
| ggc_collect (); |
| } |
| |
| if (modules) |
| { |
| unsigned n = dump.push (NULL); |
| dump () && dump ("Imported %u modules", modules->length () - 1); |
| dump () && dump ("Containing %u clusters", available_clusters); |
| dump () && dump ("Loaded %u clusters (%u%%)", loaded_clusters, |
| (loaded_clusters * 100 + available_clusters / 2) / |
| (available_clusters + !available_clusters)); |
| dump.pop (n); |
| } |
| |
| if (modules && !header_module_p ()) |
| { |
| /* Determine call_init_p. We need the same bitmap allocation |
| scheme as for the imports member. */ |
| function_depth++; /* Disable GC. */ |
| bitmap indirect_imports (BITMAP_GGC_ALLOC ()); |
| |
| /* Because indirect imports are before their direct import, and |
| we're scanning the array backwards, we only need one pass! */ |
| for (unsigned ix = modules->length (); --ix;) |
| { |
| module_state *import = (*modules)[ix]; |
| |
| if (!import->is_header () |
| && !bitmap_bit_p (indirect_imports, ix)) |
| { |
| /* Everything this imports is therefore indirectly |
| imported. */ |
| bitmap_ior_into (indirect_imports, import->imports); |
| /* We don't have to worry about the self-import bit, |
| because of the single pass. */ |
| |
| import->call_init_p = true; |
| num_init_calls_needed++; |
| } |
| } |
| function_depth--; |
| } |
| } |
| |
| void |
| fini_modules () |
| { |
| /* We're done with the macro tables now. */ |
| vec_free (macro_exports); |
| vec_free (macro_imports); |
| headers = NULL; |
| |
| /* We're now done with everything but the module names. */ |
| set_cmi_repo (NULL); |
| if (mapper) |
| { |
| timevar_start (TV_MODULE_MAPPER); |
| module_client::close_module_client (0, mapper); |
| mapper = nullptr; |
| timevar_stop (TV_MODULE_MAPPER); |
| } |
| module_state_config::release (); |
| |
| #if CHECKING_P |
| note_defs = NULL; |
| #endif |
| |
| if (modules) |
| for (unsigned ix = modules->length (); --ix;) |
| if (module_state *state = (*modules)[ix]) |
| state->release (); |
| |
| /* No need to lookup modules anymore. */ |
| modules_hash = NULL; |
| |
| /* Or entity array. We still need the entity map to find import numbers. */ |
| vec_free (entity_ary); |
| entity_ary = NULL; |
| |
| /* Or remember any pending entities. */ |
| delete pending_table; |
| pending_table = NULL; |
| |
| /* Or any attachments -- Let it go! */ |
| delete attached_table; |
| attached_table = NULL; |
| |
| /* Allow a GC, we've possibly made much data unreachable. */ |
| ggc_collect (); |
| } |
| |
| /* If CODE is a module option, handle it & return true. Otherwise |
| return false. For unknown reasons I cannot get the option |
| generation machinery to set fmodule-mapper or -fmodule-header to |
| make a string type option variable. */ |
| |
| bool |
| handle_module_option (unsigned code, const char *str, int) |
| { |
| auto hdr = CMS_header; |
| |
| switch (opt_code (code)) |
| { |
| case OPT_fmodule_mapper_: |
| module_mapper_name = str; |
| return true; |
| |
| case OPT_fmodule_header_: |
| { |
| if (!strcmp (str, "user")) |
| hdr = CMS_user; |
| else if (!strcmp (str, "system")) |
| hdr = CMS_system; |
| else |
| error ("unknown header kind %qs", str); |
| } |
| /* Fallthrough. */ |
| |
| case OPT_fmodule_header: |
| flag_header_unit = hdr; |
| flag_modules = 1; |
| return true; |
| |
| case OPT_flang_info_include_translate_: |
| vec_safe_push (note_includes, str); |
| return true; |
| |
| case OPT_flang_info_module_cmi_: |
| vec_safe_push (note_cmis, str); |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* Set preprocessor callbacks and options for modules. */ |
| |
| void |
| module_preprocess_options (cpp_reader *reader) |
| { |
| gcc_checking_assert (!lang_hooks.preprocess_undef); |
| if (modules_p ()) |
| { |
| auto *cb = cpp_get_callbacks (reader); |
| |
| cb->translate_include = maybe_translate_include; |
| cb->user_deferred_macro = module_state::deferred_macro; |
| if (flag_header_unit) |
| { |
| /* If the preprocessor hook is already in use, that |
| implementation will call the undef langhook. */ |
| if (cb->undef) |
| lang_hooks.preprocess_undef = module_state::undef_macro; |
| else |
| cb->undef = module_state::undef_macro; |
| } |
| auto *opt = cpp_get_options (reader); |
| opt->module_directives = true; |
| opt->main_search = cpp_main_search (flag_header_unit); |
| } |
| } |
| |
| #include "gt-cp-module.h" |