On Tue, 27 Feb 2024, Patrick Palka wrote: > On Fri, 16 Feb 2024, Patrick Palka wrote: > > > On Thu, 15 Feb 2024, Patrick Palka wrote: > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look > > > OK for trunk? > > > > > > -- >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). > > > > Oops, this last sentence is wrong. Although the size of the bit buffer > > is 32 bits, upon flushing we rewind unused bytes within the buffer, > > which means streaming 2-8 bits ends up using only one byte not all four. > > So v2 below undoes this pessimization. > > > > > 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 on-disk std module now only grows 0.4% instead of 0.7%. > > > > > > > > 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?) > > > > -- >8 -- > > > > gcc/cp/ChangeLog: > > > > * module.cc: Update comment about classes defined. > > (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): Likewise. > > (trees_in::decl_value): Likewise. > > (module_state::write_define): Likewise. > > (module_state::read_define): Likewise. > > Ping.
Ping. > > > --- > > gcc/cp/module.cc | 418 ++++++++++++++++++++++++++--------------------- > > 1 file changed, 231 insertions(+), 187 deletions(-) > > > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > > index 0291d456ff5..2ecee007e8a 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: > > @@ -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. */ > > @@ -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. */ > > @@ -659,11 +625,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. */ > > @@ -694,13 +656,126 @@ 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; > > +} > > + > > +/* 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. */ > > + > > +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 > > + > > +/* Bit stream writer (RAII-enabled), 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 +798,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 +2854,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 +2879,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 +2974,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 +2992,7 @@ trees_in::~trees_in () > > } > > > > /* Tree stream writer. */ > > +namespace { > > class trees_out : public bytes_out { > > typedef bytes_out parent; > > > > @@ -3043,11 +3054,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 +3137,7 @@ private: > > static unsigned back_ref_count; > > static unsigned null_count; > > }; > > +} // anon namespace > > > > /* Instrumentation counters. */ > > unsigned trees_out::tree_val_count; > > @@ -5292,9 +5304,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 +5326,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 +5341,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); > > @@ -5359,7 +5373,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); > > @@ -5374,7 +5388,10 @@ 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; > > } > > + else if (TREE_CODE_CLASS (code) != tcc_declaration) > > + return; > > > > if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) > > { > > @@ -5439,6 +5456,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 +5478,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)) > > { > > @@ -5489,9 +5510,10 @@ trees_out::core_bools (tree t) > > } > > > > 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 +5529,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 +5544,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); > > @@ -5539,7 +5563,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); > > @@ -5554,7 +5578,10 @@ 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; > > } > > + else if (TREE_CODE_CLASS (code) != tcc_declaration) > > + goto done; > > > > if (CODE_CONTAINS_STRUCT (code, TS_DECL_COMMON)) > > { > > @@ -5584,6 +5611,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 +5633,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 +5658,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 +5697,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 +5749,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 +5774,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 +5824,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 +5891,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 +5904,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 +5917,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 +7146,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 +7169,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 +7179,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 +7215,7 @@ trees_in::tree_node_bools (tree t) > > break; > > } > > > > - bflush (); > > + bits.bflush (); > > if (!ok || get_overrun ()) > > return false; > > > > @@ -7696,11 +7738,11 @@ trees_out::decl_value (tree decl, depset *dep) > > > > if (mk != MK_unique) > > { > > + bits_out bits (*this); > > 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 +7751,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); > > } > > @@ -7981,11 +8023,11 @@ trees_in::decl_value () > > { > > if (mk != MK_unique) > > { > > + bits_in bits (*this); > > 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)) > > @@ -16682,10 +16724,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 (); > > + bits_out bits (sec); > > + bits.b (macro->fun_like); > > + bits.b (macro->variadic); > > + bits.b (macro->syshdr); > > + bits.bflush (); > > > > write_location (sec, macro->line); > > if (macro->fun_like) > > @@ -16780,10 +16823,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 (); > > + bits_in bits (sec); > > + macro->fun_like = bits.b (); > > + macro->variadic = bits.b (); > > + macro->syshdr = bits.b (); > > + bits.bflush (); > > > > macro->line = read_location (sec); > > > > -- > > 2.44.0.rc1.15.g4fc51f00ef > > > > >