diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 9917a6abe4f..bbed82652d4 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -153,9 +153,11 @@ Classes used: data - buffer - bytes - data streamer - bytes_in : bytes - scalar reader - bytes_out : bytes - scalar writer + bytes_in : data - scalar reader + bytes_out : data - scalar writer + + bytes_in::bits_in - bit stream reader + bytes_out::bits_out - bit stream writer elf - ELROND format elf_in : elf - ELROND reader @@ -346,6 +348,7 @@ typedef hash_map ptr_int_hash_map; /* Variable length buffer. */ +namespace { class data { public: class allocator { @@ -393,6 +396,8 @@ protected: return res; } + unsigned calc_crc (unsigned) const; + public: void unuse (unsigned count) { @@ -402,6 +407,7 @@ public: public: static allocator simple_memory; }; +} // anon namespace /* The simple data allocator. */ data::allocator data::simple_memory; @@ -447,46 +453,11 @@ 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 +data::calc_crc (unsigned l) const { return crc32 (0, (unsigned char *)buffer + 4, l - 4); } @@ -495,8 +466,9 @@ class elf_in; /* Byte stream reader. */ -class bytes_in : public bytes { - typedef bytes parent; +namespace { +class bytes_in : public data { + typedef data parent; protected: bool overrun; /* Sticky read-too-much flag. */ @@ -531,7 +503,6 @@ public: if (offset > size) set_overrun (); pos = offset; - bit_pos = bit_val = 0; } public: @@ -573,14 +544,7 @@ 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 c () ATTRIBUTE_UNUSED; /* Read a char. */ int i (); /* Read a signed int. */ unsigned u (); /* Read an unsigned int. */ size_t z (); /* Read a size_t. */ @@ -589,7 +553,11 @@ public: 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. */ + + struct bits_in; + bits_in stream_bits (); }; +} // anon namespace /* Verify the buffer's CRC is correct. */ @@ -610,8 +578,9 @@ class elf_out; /* Byte stream writer. */ -class bytes_out : public bytes { - typedef bytes parent; +namespace { +class bytes_out : public data { + typedef data parent; public: allocator *memory; /* Obtainer of memory. */ @@ -659,11 +628,7 @@ 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 c (unsigned char) ATTRIBUTE_UNUSED; /* Write unsigned char. */ void i (int); /* Write signed int. */ void u (unsigned); /* Write unsigned int. */ void z (size_t s); /* Write size_t. */ @@ -681,6 +646,9 @@ public: void buf (const void *, size_t); /* Write fixed length buffer. */ void *buf (size_t); /* Create a writable buffer */ + struct bits_out; + bits_out stream_bits (); + public: /* Format a NUL-terminated raw string. */ void printf (const char *, ...) ATTRIBUTE_PRINTF_2; @@ -694,13 +662,143 @@ protected: /* Instrumentation. */ static unsigned spans[4]; static unsigned lengths[4]; - static int is_set; }; +} // anon namespace + +/* Finish bit packet. Rewind the bytes not used. */ + +static unsigned +bit_flush (data& bits, uint32_t& bit_val, unsigned& bit_pos) +{ + gcc_assert (bit_pos); + unsigned bytes = (bit_pos + 7) / 8; + bits.unuse (4 - bytes); + bit_pos = 0; + bit_val = 0; + return bytes; +} + +/* Bit stream reader (RAII-enabled). Bools are packed into bytes. You + cannot mix bools and non-bools. Use bflush to flush the current stream + of bools on demand. Upon destruction bflush is called. + + 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. + + Both bits_in and bits_out maintain the necessary state for bit packing, + and since these objects are locally constructed the compiler can more + easily track their state across consecutive reads/writes and optimize + away redundant buffering checks. */ + +struct bytes_in::bits_in { + bytes_in& in; + uint32_t bit_val = 0; + unsigned bit_pos = 0; + + bits_in (bytes_in& in) + : in (in) + { } + + ~bits_in () + { + bflush (); + } + + bits_in(const bits_in&) = delete; + bits_in& operator=(const bits_in&) = delete; + + /* Completed a block of bools. */ + void bflush () + { + if (bit_pos) + bit_flush (in, bit_val, bit_pos); + } + + /* Read one bit. */ + bool b () + { + if (!bit_pos) + bit_val = in.u32 (); + bool x = (bit_val >> bit_pos) & 1; + bit_pos = (bit_pos + 1) % 32; + return x; + } +}; + +/* Factory function for bits_in. */ + +bytes_in::bits_in +bytes_in::stream_bits () +{ + return bits_in (*this); +} + +/* Bit stream writer (RAII-enabled), counterpart to bits_in. */ + +struct bytes_out::bits_out { + bytes_out& out; + uint32_t bit_val = 0; + unsigned bit_pos = 0; + char is_set = -1; + + bits_out (bytes_out& out) + : out (out) + { } + + ~bits_out () + { + bflush (); + } + + bits_out(const bits_out&) = delete; + bits_out& operator=(const bits_out&) = delete; + + /* Completed a block of bools. */ + void bflush () + { + if (bit_pos) + { + out.u32 (bit_val); + out.lengths[2] += bit_flush (out, bit_val, bit_pos); + } + out.spans[2]++; + is_set = -1; + } + + /* Write one bit. + + It may be worth optimizing for most bools being zero. Some kind of + run-length encoding? */ + void b (bool x) + { + if (is_set != x) + { + is_set = x; + out.spans[x]++; + } + out.lengths[x]++; + bit_val |= unsigned (x) << bit_pos++; + if (bit_pos == 32) + { + out.u32 (bit_val); + out.lengths[2] += bit_flush (out, bit_val, bit_pos); + } + } +}; + +/* Factory function for bits_out. */ + +bytes_out::bits_out +bytes_out::stream_bits () +{ + return bits_out (*this); +} /* 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. */ @@ -723,73 +821,6 @@ bytes_out::set_crc (unsigned *crc_ptr) } } -/* 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 @@ -2831,7 +2862,12 @@ struct post_process_data { /* 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. */ + loading. + + It's important that trees_in/out have internal linkage so that the + compiler knows core_bools, lang_type_bools and lang_decl_bools have + only a single caller (tree_node_bools) and inlines them appropriately. */ +namespace { class trees_in : public bytes_in { typedef bytes_in parent; @@ -2856,15 +2892,15 @@ private: public: /* Needed for binfo writing */ - bool core_bools (tree); + bool core_bools (tree, bits_in&); private: /* Stream tree_core, lang_decl_specific and lang_type_specific bits. */ bool core_vals (tree); - bool lang_type_bools (tree); + bool lang_type_bools (tree, bits_in&); bool lang_type_vals (tree); - bool lang_decl_bools (tree); + bool lang_decl_bools (tree, bits_in&); bool lang_decl_vals (tree); bool lang_vals (tree); bool tree_node_bools (tree); @@ -2952,6 +2988,7 @@ private: private: void assert_definition (tree, bool installing); }; +} // anon namespace trees_in::trees_in (module_state *state) :parent (), state (state), unused (0) @@ -2969,6 +3006,7 @@ trees_in::~trees_in () } /* Tree stream writer. */ +namespace { class trees_out : public bytes_out { typedef bytes_out parent; @@ -3030,11 +3068,11 @@ public: } private: - void core_bools (tree); + void core_bools (tree, bits_out&); void core_vals (tree); - void lang_type_bools (tree); + void lang_type_bools (tree, bits_out&); void lang_type_vals (tree); - void lang_decl_bools (tree); + void lang_decl_bools (tree, bits_out&); void lang_decl_vals (tree); void lang_vals (tree); void tree_node_bools (tree); @@ -3114,6 +3152,7 @@ private: static unsigned back_ref_count; static unsigned null_count; }; +} // anon namespace /* Instrumentation counters. */ unsigned trees_out::tree_val_count; @@ -5267,9 +5306,13 @@ trees_in::start (unsigned code) /* Read & write the core boolean flags. */ void -trees_out::core_bools (tree t) +trees_out::core_bools (tree t, bits_out& bits) { -#define WB(X) (b (X)) +#define WB(X) (bits.b (X)) +/* Stream X if COND holds, and if !COND stream a dummy value so that the + overall number of bits streamed is independent of the runtime value + of COND, which allows the compiler to better optimize this function. */ +#define WB_IF(COND, X) WB ((COND) ? (X) : false) tree_code code = TREE_CODE (t); WB (t->base.side_effects_flag); @@ -5286,9 +5329,8 @@ trees_out::core_bools (tree t) 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); + /* This is TYPE_CACHED_VALUES_P for types. */ + WB_IF (TREE_CODE_CLASS (code) != tcc_type, t->base.public_flag); WB (t->base.private_flag); WB (t->base.protected_flag); WB (t->base.deprecated_flag); @@ -5302,7 +5344,7 @@ trees_out::core_bools (tree t) case TARGET_MEM_REF: case TREE_VEC: /* These use different base.u fields. */ - break; + return; default: WB (t->base.u.bits.lang_flag_0); @@ -5335,7 +5377,7 @@ trees_out::core_bools (tree t) break; } - if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) + if (TREE_CODE_CLASS (code) == tcc_type) { WB (t->type_common.no_force_blk_flag); WB (t->type_common.needs_constructing_flag); @@ -5352,6 +5394,9 @@ trees_out::core_bools (tree t) WB (t->type_common.typeless_storage); } + if (TREE_CODE_CLASS (code) != tcc_declaration) + return; + if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) { WB (t->decl_common.nonlocal_flag); @@ -5425,6 +5470,8 @@ trees_out::core_bools (tree t) WB (t->decl_common.decl_nonshareable_flag); WB (t->decl_common.decl_not_flexarray); } + else + return; if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) { @@ -5445,6 +5492,8 @@ trees_out::core_bools (tree t) WB (t->decl_with_vis.final); WB (t->decl_with_vis.regdecl_flag); } + else + return; if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL)) { @@ -5471,13 +5520,17 @@ trees_out::core_bools (tree t) WB ((kind >> 0) & 1); WB ((kind >> 1) & 1); } +#undef WB_IF #undef WB } bool -trees_in::core_bools (tree t) +trees_in::core_bools (tree t, bits_in& bits) { -#define RB(X) ((X) = b ()) +#define RB(X) ((X) = bits.b ()) +/* See the comment for WB_IF in trees_out::core_bools. */ +#define RB_IF(COND, X) ((COND) ? RB (X) : bits.b ()) + tree_code code = TREE_CODE (t); RB (t->base.side_effects_flag); @@ -5491,8 +5544,7 @@ trees_in::core_bools (tree t) /* 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_IF (TREE_CODE_CLASS (code) != tcc_type, t->base.public_flag); RB (t->base.private_flag); RB (t->base.protected_flag); RB (t->base.deprecated_flag); @@ -5506,7 +5558,7 @@ trees_in::core_bools (tree t) case TARGET_MEM_REF: case TREE_VEC: /* These use different base.u fields. */ - break; + goto done; default: RB (t->base.u.bits.lang_flag_0); @@ -5526,7 +5578,7 @@ trees_in::core_bools (tree t) break; } - if (CODE_CONTAINS_STRUCT (code, TS_TYPE_COMMON)) + if (TREE_CODE_CLASS (code) == tcc_type) { RB (t->type_common.no_force_blk_flag); RB (t->type_common.needs_constructing_flag); @@ -5543,6 +5595,9 @@ trees_in::core_bools (tree t) RB (t->type_common.typeless_storage); } + if (TREE_CODE_CLASS (code) != tcc_declaration) + goto done; + if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) { RB (t->decl_common.nonlocal_flag); @@ -5571,6 +5626,8 @@ trees_in::core_bools (tree t) RB (t->decl_common.decl_nonshareable_flag); RB (t->decl_common.decl_not_flexarray); } + else + goto done; if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) { @@ -5591,6 +5648,8 @@ trees_in::core_bools (tree t) RB (t->decl_with_vis.final); RB (t->decl_with_vis.regdecl_flag); } + else + goto done; if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL)) { @@ -5614,20 +5673,23 @@ trees_in::core_bools (tree t) /* decl_type is a (misnamed) 2 bit discriminator. */ unsigned kind = 0; - kind |= unsigned (b ()) << 0; - kind |= unsigned (b ()) << 1; + kind |= unsigned (bits.b ()) << 0; + kind |= unsigned (bits.b ()) << 1; t->function_decl.decl_type = function_decl_type (kind); } +#undef RB_IF #undef RB +done: return !get_overrun (); } void -trees_out::lang_decl_bools (tree t) +trees_out::lang_decl_bools (tree t, bits_out& bits) { -#define WB(X) (b (X)) +#define WB(X) (bits.b (X)) const struct lang_decl *lang = DECL_LANG_SPECIFIC (t); + bits.bflush (); WB (lang->u.base.language == lang_cplusplus); WB ((lang->u.base.use_template >> 0) & 1); WB ((lang->u.base.use_template >> 1) & 1); @@ -5700,15 +5762,16 @@ trees_out::lang_decl_bools (tree t) } bool -trees_in::lang_decl_bools (tree t) +trees_in::lang_decl_bools (tree t, bits_in& bits) { -#define RB(X) ((X) = b ()) +#define RB(X) ((X) = bits.b ()) struct lang_decl *lang = DECL_LANG_SPECIFIC (t); - lang->u.base.language = b () ? lang_cplusplus : lang_c; + bits.bflush (); + lang->u.base.language = bits.b () ? lang_cplusplus : lang_c; unsigned v; - v = b () << 0; - v |= b () << 1; + v = bits.b () << 0; + v |= bits.b () << 1; lang->u.base.use_template = v; /* lang->u.base.not_really_extern is not streamed. */ RB (lang->u.base.initialized_in_class); @@ -5771,11 +5834,12 @@ trees_in::lang_decl_bools (tree t) } void -trees_out::lang_type_bools (tree t) +trees_out::lang_type_bools (tree t, bits_out& bits) { -#define WB(X) (b (X)) +#define WB(X) (bits.b (X)) const struct lang_type *lang = TYPE_LANG_SPECIFIC (t); + bits.bflush (); WB (lang->has_type_conversion); WB (lang->has_copy_ctor); WB (lang->has_default_ctor); @@ -5837,11 +5901,12 @@ trees_out::lang_type_bools (tree t) } bool -trees_in::lang_type_bools (tree t) +trees_in::lang_type_bools (tree t, bits_in& bits) { -#define RB(X) ((X) = b ()) +#define RB(X) ((X) = bits.b ()) struct lang_type *lang = TYPE_LANG_SPECIFIC (t); + bits.bflush (); RB (lang->has_type_conversion); RB (lang->has_copy_ctor); RB (lang->has_default_ctor); @@ -5849,8 +5914,8 @@ trees_in::lang_type_bools (tree t) RB (lang->ref_needs_init); RB (lang->has_const_copy_assign); unsigned v; - v = b () << 0; - v |= b () << 1; + v = bits.b () << 0; + v |= bits.b () << 1; lang->use_template = v; RB (lang->has_mutable); @@ -5862,8 +5927,8 @@ trees_in::lang_type_bools (tree t) RB (lang->has_new); RB (lang->has_array_new); - v = b () << 0; - v |= b () << 1; + v = bits.b () << 0; + v |= bits.b () << 1; lang->gets_delete = v; RB (lang->interface_only); RB (lang->interface_unknown); @@ -7134,18 +7199,19 @@ trees_out::tree_node_bools (tree t) gcc_checking_assert (TREE_CODE (t) != NAMESPACE_DECL || DECL_NAMESPACE_ALIAS (t)); - core_bools (t); + bits_out bits = stream_bits (); + core_bools (t, bits); switch (TREE_CODE_CLASS (TREE_CODE (t))) { case tcc_declaration: { bool specific = DECL_LANG_SPECIFIC (t) != NULL; - b (specific); + bits.b (specific); if (specific && VAR_P (t)) - b (DECL_DECOMPOSITION_P (t)); + bits.b (DECL_DECOMPOSITION_P (t)); if (specific) - lang_decl_bools (t); + lang_decl_bools (t, bits); } break; @@ -7156,9 +7222,9 @@ trees_out::tree_node_bools (tree t) gcc_assert (TYPE_LANG_SPECIFIC (t) == TYPE_LANG_SPECIFIC (TYPE_MAIN_VARIANT (t))); - b (specific); + bits.b (specific); if (specific) - lang_type_bools (t); + lang_type_bools (t, bits); } break; @@ -7166,34 +7232,35 @@ trees_out::tree_node_bools (tree t) break; } - bflush (); + bits.bflush (); } bool trees_in::tree_node_bools (tree t) { - bool ok = core_bools (t); + bits_in bits = stream_bits (); + bool ok = core_bools (t, bits); if (ok) switch (TREE_CODE_CLASS (TREE_CODE (t))) { case tcc_declaration: - if (b ()) + if (bits.b ()) { - bool decomp = VAR_P (t) && b (); + bool decomp = VAR_P (t) && bits.b (); ok = maybe_add_lang_decl_raw (t, decomp); if (ok) - ok = lang_decl_bools (t); - } + ok = lang_decl_bools (t, bits); + } break; case tcc_type: - if (b ()) + if (bits.b ()) { ok = maybe_add_lang_type_raw (t); if (ok) - ok = lang_type_bools (t); + ok = lang_type_bools (t, bits); } break; @@ -7201,7 +7268,7 @@ trees_in::tree_node_bools (tree t) break; } - bflush (); + bits.bflush (); if (!ok || get_overrun ()) return false; @@ -7737,11 +7804,11 @@ trees_out::decl_value (tree decl, depset *dep) if (mk != MK_unique) { + bits_out bits = stream_bits (); 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. */ + or a module entity. */ tree o = get_originating_module_decl (decl); bool is_attached = false; @@ -7750,9 +7817,9 @@ trees_out::decl_value (tree decl, depset *dep) && DECL_MODULE_ATTACH_P (not_tmpl)) is_attached = true; - b (is_attached); + bits.b (is_attached); } - b (dep && dep->has_defn ()); + bits.b (dep && dep->has_defn ()); } tree_node_bools (decl); } @@ -8021,11 +8088,11 @@ trees_in::decl_value () { if (mk != MK_unique) { + bits_in bits = stream_bits (); if (!(mk & MK_template_mask) && !state->is_header ()) - /* See note in trees_out about where this bool is sequenced. */ - is_attached = b (); + is_attached = bits.b (); - has_defn = b (); + has_defn = bits.b (); } if (!tree_node_bools (decl)) @@ -16794,10 +16861,11 @@ module_state::write_define (bytes_out &sec, const cpp_macro *macro) { sec.u (macro->count); - sec.b (macro->fun_like); - sec.b (macro->variadic); - sec.b (macro->syshdr); - sec.bflush (); + bytes_out::bits_out bits = sec.stream_bits (); + bits.b (macro->fun_like); + bits.b (macro->variadic); + bits.b (macro->syshdr); + bits.bflush (); write_location (sec, macro->line); if (macro->fun_like) @@ -16892,10 +16960,11 @@ module_state::read_define (bytes_in &sec, cpp_reader *reader) const macro->kind = cmk_macro; macro->imported_p = true; - macro->fun_like = sec.b (); - macro->variadic = sec.b (); - macro->syshdr = sec.b (); - sec.bflush (); + bytes_in::bits_in bits = sec.stream_bits (); + macro->fun_like = bits.b (); + macro->variadic = bits.b (); + macro->syshdr = bits.b (); + bits.bflush (); macro->line = read_location (sec);