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
/* Variable length buffer. */
+namespace {
class data {
public:
class allocator {
return res;
}
+ unsigned calc_crc (unsigned) const;
+
public:
void unuse (unsigned count)
{
public:
static allocator simple_memory;
};
+} // anon namespace
/* The simple data allocator. */
data::allocator data::simple_memory;
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);
}
/* 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. */
if (offset > size)
set_overrun ();
pos = offset;
- bit_pos = bit_val = 0;
}
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. */
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. */
/* 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. */
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. */
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;
/* 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. */
}
}
-/* 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
/* 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;
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);
private:
void assert_definition (tree, bool installing);
};
+} // anon namespace
trees_in::trees_in (module_state *state)
:parent (), state (state), unused (0)
}
/* Tree stream writer. */
+namespace {
class trees_out : public bytes_out {
typedef bytes_out parent;
}
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);
static unsigned back_ref_count;
static unsigned null_count;
};
+} // anon namespace
/* Instrumentation counters. */
unsigned trees_out::tree_val_count;
/* 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);
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);
case TARGET_MEM_REF:
case TREE_VEC:
/* These use different base.u fields. */
- break;
+ return;
default:
WB (t->base.u.bits.lang_flag_0);
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);
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);
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))
{
WB (t->decl_with_vis.final);
WB (t->decl_with_vis.regdecl_flag);
}
+ else
+ return;
if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
{
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);
/* 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);
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);
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);
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);
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))
{
RB (t->decl_with_vis.final);
RB (t->decl_with_vis.regdecl_flag);
}
+ else
+ goto done;
if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
{
/* 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);
}
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);
}
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);
}
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);
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);
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);
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;
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;
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;
break;
}
- bflush ();
+ bits.bflush ();
if (!ok || get_overrun ())
return false;
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;
&& 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);
}
{
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))
{
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)
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);