From: Patrick Palka <ppalka@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org, jason@redhat.com, nathan@acm.org
Subject: Re: [PATCH] c++/modules: optimize tree flag streaming
Date: Tue, 27 Feb 2024 12:57:21 -0500 (EST) [thread overview]
Message-ID: <c10ff8aa-8ae3-2c9e-9f25-b0e8a049d013@idea> (raw)
In-Reply-To: <34c807f1-ec6e-af76-abc9-473458da14f7@idea>
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.
> ---
> 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
>
>
next prev parent reply other threads:[~2024-02-27 17:57 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-15 18:37 Patrick Palka
2024-02-16 9:34 ` Richard Biener
2024-02-16 15:06 ` Patrick Palka
2024-02-27 17:57 ` Patrick Palka [this message]
2024-03-26 14:25 ` Patrick Palka
2024-04-09 20:28 ` Patrick Palka
2024-04-10 1:27 ` Jason Merrill
2024-04-10 15:25 ` Patrick Palka
2024-04-10 15:26 ` Patrick Palka
2024-04-10 16:33 ` Jason Merrill
2024-04-13 19:40 ` Iain Sandoe
2024-04-13 19:54 ` Patrick Palka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c10ff8aa-8ae3-2c9e-9f25-b0e8a049d013@idea \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
--cc=nathan@acm.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).