Commit e1b82b2d authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[parser] Use Varint encoding for Uint32 preparse data items

Many values stored in the preparse data for the skippable functions
fit in one byte most of the time. The varint encoding uses a single
continue bit per byte to tell whether there is a following byte.

Change-Id: Ia0a622ba42a338fc91eea1e0c1a72d2582d9f867
Reviewed-on: https://chromium-review.googlesource.com/c/1400842
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58972}
parent 752882fd
......@@ -91,6 +91,23 @@ class BaseConsumedPreparseData : public ConsumedPreparseData {
return result;
}
int32_t ReadVarint32() {
DCHECK(HasRemainingBytes(kVarintMinSize));
DCHECK_EQ(data_.get(index_++), kVarintMinSize);
int32_t value = 0;
bool has_another_byte;
unsigned shift = 0;
do {
uint8_t byte = data_.get(index_++);
value |= static_cast<int32_t>(byte & 0x7F) << shift;
shift += 7;
has_another_byte = byte & 0x80;
} while (has_another_byte);
DCHECK_EQ(data_.get(index_++), kVarintEndMarker);
stored_quarters_ = 0;
return value;
}
uint8_t ReadUint8() {
DCHECK(has_data_);
DCHECK(HasRemainingBytes(kUint8Size));
......
......@@ -20,9 +20,9 @@ namespace internal {
namespace {
class ScopeCallsSloppyEvalField : public BitField<bool, 0, 1> {};
class ScopeCallsSloppyEvalField : public BitField8<bool, 0, 1> {};
class InnerScopeCallsEvalField
: public BitField<bool, ScopeCallsSloppyEvalField::kNext, 1> {};
: public BitField8<bool, ScopeCallsSloppyEvalField::kNext, 1> {};
class VariableMaybeAssignedField : public BitField8<bool, 0, 1> {};
class VariableContextAllocatedField
......@@ -113,12 +113,10 @@ PreparseDataBuilder::DataGatheringScope::~DataGatheringScope() {
preparser_->set_preparse_data_builder(parent);
}
#ifdef DEBUG
void PreparseDataBuilder::ByteData::WriteUint32(uint32_t data) {
DCHECK(!is_finalized_);
#ifdef DEBUG
// Save expected item size in debug mode.
byte_data_->push_back(kUint32Size);
#endif
byte_data_->push_back(data & 0xFF);
byte_data_->push_back((data >> 8) & 0xFF);
byte_data_->push_back((data >> 16) & 0xFF);
......@@ -126,7 +124,6 @@ void PreparseDataBuilder::ByteData::WriteUint32(uint32_t data) {
free_quarters_in_last_byte_ = 0;
}
#ifdef DEBUG
void PreparseDataBuilder::ByteData::SaveCurrentSizeAtFirstUint32() {
CHECK(!is_finalized_);
uint32_t data = static_cast<uint32_t>(byte_data_->size());
......@@ -147,6 +144,26 @@ int PreparseDataBuilder::ByteData::length() const {
}
#endif
void PreparseDataBuilder::ByteData::WriteVarint32(uint32_t data) {
#ifdef DEBUG
// Save expected item size in debug mode.
byte_data_->push_back(kVarintMinSize);
#endif
// See ValueSerializer::WriteVarint.
do {
uint8_t next_byte = (data & 0x7F);
data >>= 7;
// Add continue bit.
if (data) next_byte |= 0x80;
byte_data_->push_back(next_byte & 0xFF);
} while (data);
#ifdef DEBUG
// Save a varint marker in debug mode.
byte_data_->push_back(kVarintEndMarker);
#endif
free_quarters_in_last_byte_ = 0;
}
void PreparseDataBuilder::ByteData::WriteUint8(uint8_t data) {
DCHECK(!is_finalized_);
#ifdef DEBUG
......@@ -240,15 +257,15 @@ bool PreparseDataBuilder::SaveDataForSkippableFunction(
// Start position is used for a sanity check when consuming the data, we could
// remove it in the future if we're very pressed for space but it's been good
// at catching bugs in the wild so far.
byte_data_.WriteUint32(function_scope->start_position());
byte_data_.WriteUint32(function_scope->end_position());
byte_data_.WriteVarint32(function_scope->start_position());
byte_data_.WriteVarint32(function_scope->end_position());
bool has_data = builder->has_data_;
uint32_t has_data_and_num_parameters =
HasDataField::encode(has_data) |
NumberOfParametersField::encode(function_scope->num_parameters());
byte_data_.WriteUint32(has_data_and_num_parameters);
byte_data_.WriteUint32(builder->num_inner_functions_);
byte_data_.WriteVarint32(has_data_and_num_parameters);
byte_data_.WriteVarint32(builder->num_inner_functions_);
uint8_t language_and_super =
LanguageField::encode(function_scope->language_mode()) |
......@@ -276,10 +293,9 @@ void PreparseDataBuilder::SaveScopeAllocationData(DeclarationScope* scope,
}
#ifdef DEBUG
// function data items, kSkippableFunctionDataSize each.
// function data items, kSkippableMinFunctionDataSize each.
CHECK_GE(byte_data_.length(), kPlaceholderSize);
CHECK_LE(byte_data_.length(), std::numeric_limits<uint32_t>::max());
CHECK_EQ(byte_data_.length() % kSkippableFunctionDataSize, kPlaceholderSize);
byte_data_.SaveCurrentSizeAtFirstUint32();
// For a data integrity check, write a value between data about skipped inner
......@@ -489,17 +505,18 @@ BaseConsumedPreparseData<Data>::GetDataForSkippableFunction(
// The skippable function *must* be the next function in the data. Use the
// start position as a sanity check.
typename ByteData::ReadingScope reading_scope(this);
CHECK(scope_data_->HasRemainingBytes(ByteData::kSkippableFunctionDataSize));
int start_position_from_data = scope_data_->ReadUint32();
CHECK(scope_data_->HasRemainingBytes(
PreparseByteDataConstants::kSkippableFunctionMinDataSize));
int start_position_from_data = scope_data_->ReadVarint32();
CHECK_EQ(start_position, start_position_from_data);
*end_position = scope_data_->ReadUint32();
*end_position = scope_data_->ReadVarint32();
DCHECK_GT(*end_position, start_position);
uint32_t has_data_and_num_parameters = scope_data_->ReadUint32();
uint32_t has_data_and_num_parameters = scope_data_->ReadVarint32();
bool has_data = HasDataField::decode(has_data_and_num_parameters);
*num_parameters =
NumberOfParametersField::decode(has_data_and_num_parameters);
*num_inner_functions = scope_data_->ReadUint32();
*num_inner_functions = scope_data_->ReadVarint32();
uint8_t language_and_super = scope_data_->ReadQuarter();
*language_mode = LanguageMode(LanguageField::decode(language_and_super));
......
......@@ -68,17 +68,20 @@ struct PreparseByteDataConstants {
static constexpr int kMagicValue = 0xC0DE0DE;
static constexpr size_t kUint32Size = 5;
static constexpr size_t kVarintMinSize = 3;
static constexpr size_t kVarintEndMarker = 0xF1;
static constexpr size_t kUint8Size = 2;
static constexpr size_t kQuarterMarker = 0;
static constexpr size_t kQuarterMarker = 0xF2;
static constexpr size_t kPlaceholderSize = kUint32Size;
#else
static constexpr size_t kUint32Size = 4;
static constexpr size_t kVarintMinSize = 1;
static constexpr size_t kUint8Size = 1;
static constexpr size_t kPlaceholderSize = 0;
#endif
static const size_t kSkippableFunctionDataSize =
4 * kUint32Size + 1 * kUint8Size;
static const size_t kSkippableFunctionMinDataSize =
4 * kVarintMinSize + 1 * kUint8Size;
};
class PreparseDataBuilder : public ZoneObject,
......@@ -122,11 +125,12 @@ class PreparseDataBuilder : public ZoneObject,
Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length);
ZonePreparseData* CopyToZone(Zone* zone, int children_length);
void WriteUint32(uint32_t data);
void WriteVarint32(uint32_t data);
void WriteUint8(uint8_t data);
void WriteQuarter(uint8_t data);
#ifdef DEBUG
void WriteUint32(uint32_t data);
// For overwriting previously written data at position 0.
void SaveCurrentSizeAtFirstUint32();
int length() const;
......
......@@ -805,11 +805,15 @@ TEST(ProducingAndConsumingByteData) {
i::PreparseDataBuilder::ByteData bytes;
bytes.Start(&buffer);
// Write some data.
#ifdef DEBUG
bytes.WriteUint32(1983); // This will be overwritten.
bytes.WriteUint32(2147483647);
#else
bytes.WriteVarint32(1983);
#endif
bytes.WriteVarint32(2147483647);
bytes.WriteUint8(4);
bytes.WriteUint8(255);
bytes.WriteUint32(0);
bytes.WriteVarint32(0);
bytes.WriteUint8(0);
#ifdef DEBUG
bytes.SaveCurrentSizeAtFirstUint32();
......@@ -825,17 +829,26 @@ TEST(ProducingAndConsumingByteData) {
bytes.WriteQuarter(1);
bytes.WriteQuarter(0);
bytes.WriteUint8(50);
bytes.WriteQuarter(0);
bytes.WriteQuarter(1);
bytes.WriteQuarter(2);
bytes.WriteQuarter(3);
bytes.WriteVarint32(50);
// End with a lonely quarter.
bytes.WriteQuarter(0);
bytes.WriteQuarter(1);
bytes.WriteQuarter(2);
bytes.WriteUint32(50);
bytes.WriteVarint32(0xff);
// End with a lonely quarter.
bytes.WriteQuarter(2);
#ifdef DEBUG
CHECK_EQ(buffer.size(), 38);
CHECK_EQ(buffer.size(), 42);
#else
CHECK_EQ(buffer.size(), 25);
CHECK_EQ(buffer.size(), 21);
#endif
// Copy buffer for sanity checks later-on.
......@@ -862,24 +875,33 @@ TEST(ProducingAndConsumingByteData) {
#ifdef DEBUG
CHECK_EQ(bytes_for_reading.ReadUint32(), saved_size);
#else
CHECK_EQ(bytes_for_reading.ReadUint32(), 1983);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 1983);
#endif
CHECK_EQ(bytes_for_reading.ReadUint32(), 2147483647);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 2147483647);
CHECK_EQ(bytes_for_reading.ReadUint8(), 4);
CHECK_EQ(bytes_for_reading.ReadUint8(), 255);
CHECK_EQ(bytes_for_reading.ReadUint32(), 0);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 100);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 3);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadUint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 3);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 0xff);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
// We should have consumed all data at this point.
CHECK(!bytes_for_reading.HasRemainingBytes(1));
......@@ -901,24 +923,33 @@ TEST(ProducingAndConsumingByteData) {
#ifdef DEBUG
CHECK_EQ(bytes_for_reading.ReadUint32(), saved_size);
#else
CHECK_EQ(bytes_for_reading.ReadUint32(), 1983);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 1983);
#endif
CHECK_EQ(bytes_for_reading.ReadUint32(), 2147483647);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 2147483647);
CHECK_EQ(bytes_for_reading.ReadUint8(), 4);
CHECK_EQ(bytes_for_reading.ReadUint8(), 255);
CHECK_EQ(bytes_for_reading.ReadUint32(), 0);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 100);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 3);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadUint8(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadUint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 3);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 0);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 1);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadVarint32(), 0xff);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
// We should have consumed all data at this point.
CHECK(!bytes_for_reading.HasRemainingBytes(1));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment