Bug 1591359 - Part 1: Separate HuffmanTableValue& array into TableStatus array and uninitialized GenericHuffmanTable array. r=Yoric

Differential Revision: https://phabricator.services.mozilla.com/D50597

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tooru Fujisawa 2019-11-05 15:44:06 +00:00
Родитель ed4b10d09c
Коммит 222d884cf6
2 изменённых файлов: 162 добавлений и 111 удалений

Просмотреть файл

@ -469,23 +469,26 @@ class HuffmanPreludeReader {
// Enqueue an entry to the stack.
MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
const List& list) {
auto& table = dictionary_.tableForListLength(list.contents_);
if (table.is<HuffmanTableUnreachable>()) {
auto index = list.contents_;
auto& status = dictionary_.listLengthStatus(index);
if (status == HuffmanDictionary::TableStatus::Unreachable) {
// Spec:
// 2. Add the field to the set of visited fields
table = {mozilla::VariantType<HuffmanTableInitializing>{}};
status = HuffmanDictionary::TableStatus::Initializing;
auto& table = dictionary_.tableForListLength(index);
// Read the lengths immediately.
MOZ_TRY((readTable<List>(table, list)));
MOZ_TRY((readTable<List>(table, status, list)));
}
// Spec:
// 3. If the field has a FrozenArray type
// a. Determine if the array type is always empty
// b. If so, stop
auto& lengthTable = table.as<GenericHuffmanTable>();
auto& table = dictionary_.tableForListLength(index);
bool empty = true;
for (auto iter : lengthTable) {
for (auto iter : table) {
if (iter->toListLength() > 0) {
empty = false;
break;
@ -520,15 +523,16 @@ class HuffmanPreludeReader {
MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
const Interface& interface) {
// Note: In this case, for compatibility, we do *not* check whether
// the interface has already been visited.
auto& table = dictionary_.tableForField(identity);
if (table.is<HuffmanTableUnreachable>()) {
auto& status = dictionary_.fieldStatus(identity);
if (status == HuffmanDictionary::TableStatus::Unreachable) {
// Effectively, an `Interface` is a sum with a single entry.
GenericHuffmanTable sum;
MOZ_TRY(sum.initWithSingleValue(
auto& table = dictionary_.tableForField(identity);
status = HuffmanDictionary::TableStatus::Ready;
new (mozilla::KnownNotNull, &table) GenericHuffmanTable();
MOZ_TRY(table.initWithSingleValue(
cx_, BinASTSymbol::fromKind(BinASTKind(interface.kind_))));
table = {mozilla::VariantType<GenericHuffmanTable>{}, std::move(sum)};
}
// Spec:
@ -543,15 +547,15 @@ class HuffmanPreludeReader {
const Entry& entry) {
// Spec:
// 1. If the field is in the set of visited contexts, stop.
auto& table = dictionary_.tableForField(identity);
if (!table.is<HuffmanTableUnreachable>()) {
auto& status = dictionary_.fieldStatus(identity);
if (status != HuffmanDictionary::TableStatus::Unreachable) {
// Entry already initialized/initializing.
return Ok();
}
// Spec:
// 2. Add the field to the set of visited fields
table = {mozilla::VariantType<HuffmanTableInitializing>{}};
status = HuffmanDictionary::TableStatus::Initializing;
// Spec:
// 5. Otherwise, push the field onto the stack
@ -812,45 +816,45 @@ class HuffmanPreludeReader {
}
// Single-argument version: lookup the table using `dictionary_.tableForField`
// then proceed as two-arguments version.
// then proceed as three-arguments version.
template <typename Entry>
MOZ_MUST_USE JS::Result<Ok> readTable(Entry entry) {
auto& table = dictionary_.tableForField(entry.identity_);
return readTable<Entry>(table, entry);
}
// Two-arguments version: pass table explicitly. Generally called from single-
// argument version, but may be called manually, e.g. for list lengths, as
// their tables don't appear in `dictionary_.tableForField`.
template <typename Entry>
MOZ_MUST_USE JS::Result<Ok> readTable(HuffmanTableValue& table, Entry entry) {
if (MOZ_UNLIKELY(!table.template is<HuffmanTableInitializing>())) {
auto index = entry.identity_;
auto& status = dictionary_.fieldStatus(index);
if (MOZ_UNLIKELY(status != HuffmanDictionary::TableStatus::Initializing)) {
// We're attempting to re-read a table that has already been read.
// FIXME: Shouldn't this be a MOZ_CRASH?
return raiseDuplicateTableError(entry.identity_);
return raiseDuplicateTableError(index);
}
auto& table = dictionary_.tableForField(index);
return readTable<Entry>(table, status, entry);
}
// Three-arguments version: pass table explicitly. Generally called from
// single-argument version, but may be called manually, e.g. for list
// lengths, as their tables don't appear in `dictionary_.tableForField`.
template <typename Entry>
MOZ_MUST_USE JS::Result<Ok> readTable(GenericHuffmanTable& table,
HuffmanDictionary::TableStatus& status,
Entry entry) {
uint8_t headerByte;
MOZ_TRY_VAR(headerByte, reader_.readByte<Compression::No>());
switch (headerByte) {
case TableHeader::SingleValue: {
// Construct in-place.
table = {mozilla::VariantType<GenericHuffmanTable>{}};
auto& tableRef = table.template as<GenericHuffmanTable>();
case TableHeader::SingleValue:
new (mozilla::KnownNotNull, &table) GenericHuffmanTable();
status = HuffmanDictionary::TableStatus::Ready;
// The table contains a single value.
MOZ_TRY((readSingleValueTable<Entry>(tableRef, entry)));
MOZ_TRY((readSingleValueTable<Entry>(table, entry)));
return Ok();
}
case TableHeader::MultipleValues: {
// Table contains multiple values.
// Construct in-place.
table = {mozilla::VariantType<GenericHuffmanTable>{}};
auto& tableRef = table.template as<GenericHuffmanTable>();
case TableHeader::MultipleValues:
new (mozilla::KnownNotNull, &table) GenericHuffmanTable();
status = HuffmanDictionary::TableStatus::Ready;
MOZ_TRY((readMultipleValuesTable<Entry>(tableRef, entry)));
// Table contains multiple values.
MOZ_TRY((readMultipleValuesTable<Entry>(table, entry)));
return Ok();
}
case TableHeader::Unreachable:
// Table is unreachable, nothing to do.
return Ok();
@ -913,12 +917,14 @@ class HuffmanPreludeReader {
// interface.
// FIXME: readTable could return a reference to the table, eliminating an
// array lookup.
const auto& table = owner.dictionary_.tableForField(entry.identity_);
if (table.is<HuffmanTableUnreachable>()) {
auto index = entry.identity_;
auto& status = owner.dictionary_.fieldStatus(index);
if (status == HuffmanDictionary::TableStatus::Unreachable) {
return Ok();
}
const auto& tableRef = table.as<GenericHuffmanTable>();
if (!tableRef.isMaybeInterfaceAlwaysNull()) {
const auto& table = owner.dictionary_.tableForField(index);
if (!table.isMaybeInterfaceAlwaysNull()) {
MOZ_TRY(owner.pushFields(entry.kind_));
}
return Ok();
@ -934,15 +940,15 @@ class HuffmanPreludeReader {
// FIXME: readTable could return a reference to the table, eliminating an
// array lookup.
const auto& table = owner.dictionary_.tableForField(entry.identity_);
if (table.is<HuffmanTableInitializing>()) {
auto index = entry.identity_;
auto& status = owner.dictionary_.fieldStatus(index);
if (status == HuffmanDictionary::TableStatus::Initializing) {
return Ok();
}
const auto& tableRef = table.as<GenericHuffmanTable>();
for (auto iter : tableRef) {
MOZ_TRY(owner.pushValue(entry.identity_,
Interface(entry.identity_, iter->toKind())));
const auto& table = owner.dictionary_.tableForField(index);
for (auto iter : table) {
MOZ_TRY(owner.pushValue(index, Interface(index, iter->toKind())));
}
return Ok();
}
@ -957,16 +963,15 @@ class HuffmanPreludeReader {
// Now, walk the table to enqueue each value if necessary.
// FIXME: readTable could return a reference to the table, eliminating an
// array lookup.
const auto& table = owner.dictionary_.tableForField(entry.identity_);
if (table.is<HuffmanTableUnreachable>()) {
auto index = entry.identity_;
auto& status = owner.dictionary_.fieldStatus(index);
if (status == HuffmanDictionary::TableStatus::Unreachable) {
return Ok();
}
const auto& tableRef = table.as<GenericHuffmanTable>();
for (auto iter : tableRef) {
MOZ_TRY(owner.pushValue(entry.identity_,
Interface(entry.identity_, iter->toKind())));
const auto& table = owner.dictionary_.tableForField(index);
for (auto iter : table) {
MOZ_TRY(owner.pushValue(index, Interface(index, iter->toKind())));
}
return Ok();
}
@ -1331,15 +1336,13 @@ struct ExtractBinASTInterfaceAndFieldMatcher {
JS::Result<BinASTKind> BinASTTokenReaderContext::readTagFromTable(
const BinASTInterfaceAndField& identity) {
// Extract the table.
const auto& table =
dictionary_.tableForField(NormalizedInterfaceAndField(identity));
auto index = NormalizedInterfaceAndField(identity);
const auto& table = dictionary_.tableForField(index);
BINJS_MOZ_TRY_DECL(bits_,
(bitBuffer.getHuffmanLookup<Compression::No>(*this)));
const auto& specialized = table.as<GenericHuffmanTable>();
// We're entering either a single interface or a sum.
const auto result = specialized.lookup(bits_);
const auto result = table.lookup(bits_);
if (MOZ_UNLIKELY(!result.isFound())) {
return raiseInvalidValue();
}
@ -1349,17 +1352,21 @@ JS::Result<BinASTKind> BinASTTokenReaderContext::readTagFromTable(
JS::Result<BinASTSymbol> BinASTTokenReaderContext::readFieldFromTable(
const BinASTInterfaceAndField& identity) {
// Extract the table.
const auto& table =
dictionary_.tableForField(NormalizedInterfaceAndField(identity));
if (MOZ_UNLIKELY(!table.is<GenericHuffmanTable>())) {
auto index = NormalizedInterfaceAndField(identity);
auto& status = dictionary_.fieldStatus(index);
if (status != HuffmanDictionary::TableStatus::Ready) {
return raiseNotInPrelude();
}
const auto& table = dictionary_.tableForField(index);
BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
const auto result = table.as<GenericHuffmanTable>().lookup(bits_);
const auto result = table.lookup(bits_);
if (MOZ_UNLIKELY(!result.isFound())) {
return raiseInvalidValue();
}
bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
return result.value();
}
@ -1490,8 +1497,7 @@ JS::Result<Ok> BinASTTokenReaderContext::enterList(uint32_t& items,
const auto identity = context.content_;
const auto& table = dictionary_.tableForListLength(identity);
BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
const auto& tableForLookup = table.as<GenericHuffmanTable>();
const auto result = tableForLookup.lookup(bits_);
const auto result = table.lookup(bits_);
if (MOZ_UNLIKELY(!result.isFound())) {
return raiseInvalidValue();
}
@ -2779,19 +2785,36 @@ HuffmanPreludeReader::readSingleValueTable<UnsignedLong>(
return Ok();
}
HuffmanDictionary::HuffmanDictionary()
: fields_(BINAST_PARAM_NUMBER_OF_INTERFACE_AND_FIELD(
mozilla::AsVariant(HuffmanTableUnreachable()))),
listLengths_(BINAST_PARAM_NUMBER_OF_LIST_TYPES(
mozilla::AsVariant(HuffmanTableUnreachable()))) {}
HuffmanTableValue& HuffmanDictionary::tableForField(
NormalizedInterfaceAndField index) {
return fields_[static_cast<size_t>(index.identity_)];
HuffmanDictionary::~HuffmanDictionary() {
for (size_t i = 0; i < BINAST_INTERFACE_AND_FIELD_LIMIT; i++) {
if (fieldStatus(i) == TableStatus::Ready) {
tableForField(i).~GenericHuffmanTable();
}
}
for (size_t i = 0; i < BINAST_NUMBER_OF_LIST_TYPES; i++) {
if (listLengthStatus(i) == TableStatus::Ready) {
tableForListLength(i).~GenericHuffmanTable();
}
}
}
HuffmanTableValue& HuffmanDictionary::tableForListLength(BinASTList list) {
return listLengths_[static_cast<size_t>(list)];
HuffmanDictionary::TableStatus& HuffmanDictionary::fieldStatus(
NormalizedInterfaceAndField index) {
return fieldStatus(static_cast<size_t>(index.identity_));
}
GenericHuffmanTable& HuffmanDictionary::tableForField(
NormalizedInterfaceAndField index) {
return tableForField(static_cast<size_t>(index.identity_));
}
HuffmanDictionary::TableStatus& HuffmanDictionary::listLengthStatus(
BinASTList list) {
return listLengthStatus(static_cast<size_t>(list));
}
GenericHuffmanTable& HuffmanDictionary::tableForListLength(BinASTList list) {
return tableForListLength(static_cast<size_t>(list));
}
uint32_t HuffmanLookup::leadingBits(const uint8_t aBitLength) const {

Просмотреть файл

@ -250,11 +250,6 @@ class HuffmanLookupResult {
}
};
// The default inline buffer length for instances of HuffmanTableValue.
// Specific type (e.g. booleans) will override this to provide something
// more suited to their type.
const size_t HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH = 8;
// A flag that determines only whether a value is `null`.
// Used for optional interface.
enum class Nullable {
@ -778,12 +773,6 @@ using TwoLookupsHuffmanTable =
using ThreeLookupsHuffmanTable =
MultiLookupHuffmanTable<TwoLookupsHuffmanTable, 6>;
// An empty Huffman table. Attempting to get a value from this table is a syntax
// error. This is the default value for `HuffmanTableValue` and represents all
// states that may not be reached.
//
// Part of variants `HuffmanTableValue` and
// `GenericHuffmanTable::implementation`.
struct HuffmanTableUnreachable {};
// Generic implementation of Huffman tables.
@ -875,20 +864,6 @@ struct GenericHuffmanTable {
implementation_;
};
// While reading the Huffman prelude, whenever we first encounter a
// `HuffmanTableUnreachable`, we replace it with a `HuffmanTableInitializing`
// to mark that we should not attempt to read/initialize it again.
//
// Attempting to get a value from this table is an internal error.
//
// Part of variants `HuffmanTableValue`.
struct HuffmanTableInitializing {};
// A single Huffman table, used for values.
using HuffmanTableValue =
mozilla::Variant<HuffmanTableUnreachable, // Default value.
HuffmanTableInitializing, GenericHuffmanTable>;
// A Huffman dictionary for the current file.
//
// A Huffman dictionary consists in a (contiguous) set of Huffman tables
@ -896,12 +871,50 @@ using HuffmanTableValue =
// to predict list lengths.
class HuffmanDictionary {
public:
HuffmanDictionary();
HuffmanDictionary() {}
~HuffmanDictionary();
HuffmanTableValue& tableForField(NormalizedInterfaceAndField index);
HuffmanTableValue& tableForListLength(BinASTList list);
// While reading the Huffman prelude, whenever we first encounter a
// table with `Unreachable` status, we set its status with a `Initializing`
// to mark that we should not attempt to read/initialize it again.
// Once the table is initialized, it becomes `Ready`.
enum class TableStatus : uint8_t {
Unreachable,
Initializing,
Ready,
};
TableStatus& fieldStatus(NormalizedInterfaceAndField index);
GenericHuffmanTable& tableForField(NormalizedInterfaceAndField index);
TableStatus& listLengthStatus(BinASTList list);
GenericHuffmanTable& tableForListLength(BinASTList list);
private:
// For the following purpose, tables are stored as an array of status
// and a uninitialized buffer to store an array of tables.
//
// * In most case a single BinAST file doesn't use all tables
// * GenericHuffmanTable constructor/destructor costs are not negligible,
// and we don't want to call them for unused tables
// * Initializing status for whether the table is used or not takes
// less time if they're stored in contiguous memory, instead of
// placed before each table (using `Variant` or `Maybe`)
//
// Tables with `Ready` status are destructed in HuffmanDictionary destructor.
TableStatus fieldStatus_[BINAST_INTERFACE_AND_FIELD_LIMIT] = {
TableStatus::Unreachable};
TableStatus listLengthStatus_[BINAST_NUMBER_OF_LIST_TYPES] = {
TableStatus::Unreachable};
TableStatus& fieldStatus(size_t i) {
return fieldStatus_[i];
}
TableStatus& listLengthStatus(size_t i) {
return listLengthStatus_[i];
}
// Huffman tables for `(Interface, Field)` pairs, used to decode the value of
// `Interface::Field`. Some tables may be `HuffmanTableUnreacheable`
// if they represent fields of interfaces that actually do not show up
@ -909,7 +922,18 @@ class HuffmanDictionary {
//
// The mapping from `(Interface, Field) -> index` is extracted statically from
// the webidl specs.
mozilla::Array<HuffmanTableValue, BINAST_INTERFACE_AND_FIELD_LIMIT> fields_;
//
// Semantically this is `GenericHuffmanTable fields_[...]`, but items are
// constructed lazily.
alignas(GenericHuffmanTable) char fields_[sizeof(GenericHuffmanTable) *
BINAST_INTERFACE_AND_FIELD_LIMIT];
GenericHuffmanTable& tableForField(size_t i) {
return (reinterpret_cast<GenericHuffmanTable*>(fields_))[i];
}
GenericHuffmanTable& tableForListLength(size_t i) {
return (reinterpret_cast<GenericHuffmanTable*>(listLengths_))[i];
}
// Huffman tables for list lengths. Some tables may be
// `HuffmanTableUnreacheable` if they represent lists that actually do not
@ -917,7 +941,11 @@ class HuffmanDictionary {
//
// The mapping from `List -> index` is extracted statically from the webidl
// specs.
mozilla::Array<HuffmanTableValue, BINAST_NUMBER_OF_LIST_TYPES> listLengths_;
//
// Semantically this is `GenericHuffmanTable listLengths_[...]`, but items are
// constructed lazily.
alignas(GenericHuffmanTable) char listLengths_[sizeof(GenericHuffmanTable) *
BINAST_NUMBER_OF_LIST_TYPES];
};
/**