On Thu, Feb 15, 2024 at 7:38 PM Patrick Palka <ppa...@redhat.com> wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look > OK for trunk?
Btw, there's the "bitpack" streaming support in data-streamer.h also added for exactly the same reason, it's likely not easily re-usable but this kind of approach is indeed important for performance. The whole "data-streamer" was supposed to be re-usable though. Richard. > -- >8 -- > > One would expect consecutive calls to bytes_in/out::b for streaming > adjacent bits, as we do for tree flag streaming, to at least be > optimized by the compiler into individual bit operations using > statically known bit positions (and ideally merged into larger sized > reads/writes). > > Unfortunately this doesn't happen because the compiler has trouble > tracking the values of this->bit_pos and this->bit_val across such > calls, likely because the compiler doesn't know 'this' and so it's > treated as global memory. This means for each consecutive bit stream > operation, bit_pos and bit_val are loaded from memory, checked if > buffering is needed, and finally the bit is extracted from bit_val > according to the (unknown) bit_pos, even though relative to the previous > operation (if we didn't need to buffer) bit_val is unchanged and bit_pos > is just 1 larger. This ends up being quite slow, with tree_node_bools > taking 10% of time when streaming in parts of the std module. > > This patch optimizes this by making tracking of bit_pos and bit_val > easier for the compiler. Rather than bit_pos and bit_val being members > of the (effectively global) bytes_in/out objects, this patch factors out > the bit streaming code/state into separate classes bits_in/out that get > constructed locally as needed for bit streaming. Since these objects > are now clearly local, the compiler can more easily track their values. > > And since bit streaming is intended to be batched it's natural for these > new classes to be RAII-enabled such that the bit stream is flushed upon > destruction. > > In order to make the most of this improved tracking of bit position, > this patch changes parts where we conditionally stream a tree flag > to unconditionally stream (the flag or a dummy value). That way > the number of bits streamed and the respective bit positions are as > statically known as reasonably possible. In lang_decl_bools and > lang_type_bools we flush the current bit buffer at the start so that > subsequent bit positions are statically known. And in core bools, we > can add explicit early exits utilizing invariants that the compiler > can't figure out itself (e.g. a tree code can't have both TS_TYPE_COMMON > and TS_DECL_COMMON, and if a tree code doesn't have TS_DECL_COMMON then > it doesn't have TS_DECL_WITH_VIS). Finally if we're streaming fewer > than 4 bits, it's more space efficient to stream them as individual > bytes rather than as packed bits (due to the 32-bit buffer). > > This patch also moves the definitions of the relevant streaming classes > into anonymous namespaces so that the compiler can make more informed > decisions about inlining their member functions. > > After this patch, compile time for a simple Hello World using the std > module is reduced by 7% with a release compiler. The on-disk size of > the std module increases by 0.7% (presumably due to the extra flushing > done in lang_decl_bools and lang_type_bools). > > The bit stream out performance isn't improved as much as the stream in > due to the spans/lengths instrumentation performed on stream out (which > probably should be e.g. removed for release builds?) > > gcc/cp/ChangeLog: > > * module.cc > (class data): Enclose in an anonymous namespace. > (data::calc_crc): Moved from bytes::calc_crc. > (class bytes): Remove. Move bit_flush to namespace scope. > (class bytes_in): Enclose in an anonymous namespace. Inherit > directly from data and adjust accordingly. Move b and bflush > members to bits_in. > (class bytes_out): As above. Remove is_set static data member. > (bit_flush): Moved from class bytes. > (struct bits_in): Define. > (struct bits_out): Define. > (bytes_out::bflush): Moved to bits_out/in. > (bytes_in::bflush): Likewise > (bytes_in::bfill): Removed. > (bytes_out::b): Moved to bits_out/in. > (bytes_in::b): Likewise. > (class trees_in): Enclose in an anonymous namespace. > (class trees_out): Enclose in an anonymous namespace. > (trees_out::core_bools): Add bits_out/in parameter and use it. > Unconditionally stream a bit for public_flag. Add early exits > as appropriate. > (trees_out::core_bools): Likewise. > (trees_out::lang_decl_bools): Add bits_out/in parameter and use > it. Flush the current bit buffer at the start. Unconditionally > stream a bit for module_keyed_decls_p. > (trees_in::lang_decl_bools): Likewise. > (trees_out::lang_type_bools): Add bits_out/in parameter and use > it. Flush the current bit buffer at the start. > (trees_in::lang_type_bools): Likewise. > (trees_out::tree_node_bools): Construct a bits_out object and > use/pass it. > (trees_in::tree_node_bools): Likewise. > (trees_out::decl_value): Stream stray bit values as bytes. > (trees_in::decl_value): Likewise. > (module_state::write_define): Likewise. > (module_state::read_define): Likewise. > --- > gcc/cp/module.cc | 401 ++++++++++++++++++++++++++--------------------- > 1 file changed, 219 insertions(+), 182 deletions(-) > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index 0291d456ff5..9477e4f2e35 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 - scalar reader > + bytes_out - scalar writer > + > + bits_in - bit stream reader > + bits_out - bit stream writer > > elf - ELROND format > elf_in : elf - ELROND reader > @@ -346,6 +348,7 @@ typedef hash_map<void *,signed,ptr_int_traits> > 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: > @@ -572,13 +543,6 @@ public: > 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. */ > @@ -590,6 +554,7 @@ public: > const void *buf (size_t); /* Read a fixed-length buffer. */ > cpp_hashnode *cpp_node (); /* Read a cpp node. */ > }; > +} // anon namespace > > /* Verify the buffer's CRC is correct. */ > > @@ -610,8 +575,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. */ > @@ -658,10 +624,6 @@ public: > 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. */ > @@ -694,13 +656,127 @@ protected: > /* Instrumentation. */ > static unsigned spans[4]; > static unsigned lengths[4]; > - static int is_set; > + friend struct bits_out; > +}; > +} // 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; > +} > + > +/* RAII-enabled bit stream reader. 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. */ > + > +namespace { > +struct 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; > + } > }; > +} // anon namespace > + > +/* RAII-enabled bit stream writer, counterpart to bits_in. */ > + > +namespace { > +struct 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); > + } > + } > +}; > +} // anon namespace > > /* 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 +799,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 > @@ -2846,6 +2855,7 @@ struct post_process_data { > read trees with TREE_VISITED. Thus it's quite safe to have > multiple concurrent readers. Which is good, because lazy > loading. */ > +namespace { > class trees_in : public bytes_in { > typedef bytes_in parent; > > @@ -2870,15 +2880,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); > @@ -2965,6 +2975,7 @@ private: > private: > void assert_definition (tree, bool installing); > }; > +} // anon namespace > > trees_in::trees_in (module_state *state) > :parent (), state (state), unused (0) > @@ -2982,6 +2993,7 @@ trees_in::~trees_in () > } > > /* Tree stream writer. */ > +namespace { > class trees_out : public bytes_out { > typedef bytes_out parent; > > @@ -3043,11 +3055,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); > @@ -3126,6 +3138,7 @@ private: > static unsigned back_ref_count; > static unsigned null_count; > }; > +} // anon namespace > > /* Instrumentation counters. */ > unsigned trees_out::tree_val_count; > @@ -5292,9 +5305,9 @@ 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)) > tree_code code = TREE_CODE (t); > > WB (t->base.side_effects_flag); > @@ -5314,6 +5327,8 @@ trees_out::core_bools (tree t) > if (TREE_CODE_CLASS (code) != tcc_type) > /* This is TYPE_CACHED_VALUES_P for types. */ > WB (t->base.public_flag); > + else > + WB (false); > WB (t->base.private_flag); > WB (t->base.protected_flag); > WB (t->base.deprecated_flag); > @@ -5327,7 +5342,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); > @@ -5374,6 +5389,7 @@ trees_out::core_bools (tree t) > WB (t->type_common.lang_flag_5); > WB (t->type_common.lang_flag_6); > WB (t->type_common.typeless_storage); > + return; > } > > if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) > @@ -5439,6 +5455,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)) > { > @@ -5459,6 +5477,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)) > { > @@ -5485,13 +5505,16 @@ trees_out::core_bools (tree t) > WB ((kind >> 0) & 1); > WB ((kind >> 1) & 1); > } > + else > + return; > #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 ()) > + > tree_code code = TREE_CODE (t); > > RB (t->base.side_effects_flag); > @@ -5507,6 +5530,8 @@ trees_in::core_bools (tree t) > RB (t->base.static_flag); > if (TREE_CODE_CLASS (code) != tcc_type) > RB (t->base.public_flag); > + else > + bits.b (); > RB (t->base.private_flag); > RB (t->base.protected_flag); > RB (t->base.deprecated_flag); > @@ -5520,7 +5545,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); > @@ -5554,6 +5579,7 @@ trees_in::core_bools (tree t) > RB (t->type_common.lang_flag_5); > RB (t->type_common.lang_flag_6); > RB (t->type_common.typeless_storage); > + goto done; > } > > if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) > @@ -5584,6 +5610,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)) > { > @@ -5604,6 +5632,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)) > { > @@ -5627,20 +5657,22 @@ 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 > +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); > @@ -5664,6 +5696,8 @@ trees_out::lang_decl_bools (tree t) > WB (lang->u.base.module_attach_p); > if (VAR_OR_FUNCTION_DECL_P (t)) > WB (lang->u.base.module_keyed_decls_p); > + else > + WB (false); > switch (lang->u.base.selector) > { > default: > @@ -5714,15 +5748,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); > @@ -5738,6 +5773,8 @@ trees_in::lang_decl_bools (tree t) > RB (lang->u.base.module_attach_p); > if (VAR_OR_FUNCTION_DECL_P (t)) > RB (lang->u.base.module_keyed_decls_p); > + else > + bits.b (); > switch (lang->u.base.selector) > { > default: > @@ -5786,11 +5823,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); > @@ -5852,11 +5890,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); > @@ -5864,8 +5903,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); > @@ -5877,8 +5916,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); > @@ -7106,18 +7145,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 (*this); > + 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; > > @@ -7128,9 +7168,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; > > @@ -7138,34 +7178,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 (*this); > + 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; > > @@ -7173,7 +7214,7 @@ trees_in::tree_node_bools (tree t) > break; > } > > - bflush (); > + bits.bflush (); > if (!ok || get_overrun ()) > return false; > > @@ -7699,8 +7740,7 @@ trees_out::decl_value (tree decl, depset *dep) > 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; > > @@ -7709,9 +7749,9 @@ trees_out::decl_value (tree decl, depset *dep) > && DECL_MODULE_ATTACH_P (not_tmpl)) > is_attached = true; > > - b (is_attached); > + c (is_attached); > } > - b (dep && dep->has_defn ()); > + c (dep && dep->has_defn ()); > } > tree_node_bools (decl); > } > @@ -7982,10 +8022,9 @@ trees_in::decl_value () > if (mk != MK_unique) > { > if (!(mk & MK_template_mask) && !state->is_header ()) > - /* See note in trees_out about where this bool is sequenced. */ > - is_attached = b (); > + is_attached = c (); > > - has_defn = b (); > + has_defn = c (); > } > > if (!tree_node_bools (decl)) > @@ -16682,10 +16721,9 @@ 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 (); > + sec.c (macro->fun_like); > + sec.c (macro->variadic); > + sec.c (macro->syshdr); > > write_location (sec, macro->line); > if (macro->fun_like) > @@ -16780,10 +16818,9 @@ 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 (); > + macro->fun_like = sec.c (); > + macro->variadic = sec.c (); > + macro->syshdr = sec.c (); > > macro->line = read_location (sec); > > -- > 2.44.0.rc1.15.g4fc51f00ef >