mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 1559963 - Wasm: Update wasmTextToBinary for bulk-memory#98. r=bbouvier
Issue: https://github.com/WebAssembly/bulk-memory-operations/issues/98 This commit updates the encoding of element segments to the latest bulk-memory proposal. This is backwards compatible with the MVP, but a breaking change from the previously implemented bulk-memory spec. The following semantic differences are made with the new encoding. 1. The introduction of 'Declared' segments * Declared segments allow a Wasm module to forward declare which functions are aliasable by ref.func. See reference-types#31 for more information. 2. Whether an element expression or function indices are encoded now depends on an independent flag from the 'kind' of an element segment. 3. The definition kind or element expression type is now explicitly encoded in the element segment. Differential Revision: https://phabricator.services.mozilla.com/D40582 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
da9af79283
commit
f6fe16593e
@ -1271,21 +1271,29 @@ struct AstNullValue {};
|
||||
typedef Variant<AstRef, AstNullValue> AstElem;
|
||||
typedef AstVector<AstElem> AstElemVector;
|
||||
|
||||
enum class AstElemSegmentKind {
|
||||
Active,
|
||||
Passive,
|
||||
Declared,
|
||||
};
|
||||
|
||||
class AstElemSegment : public AstNode {
|
||||
AstElemSegmentKind kind_;
|
||||
AstRef targetTable_;
|
||||
AstExpr* offsetIfActive_;
|
||||
AstElemVector elems_;
|
||||
|
||||
public:
|
||||
AstElemSegment(AstRef targetTable, AstExpr* offsetIfActive,
|
||||
AstElemVector&& elems)
|
||||
: targetTable_(targetTable),
|
||||
AstElemSegment(AstElemSegmentKind kind, AstRef targetTable,
|
||||
AstExpr* offsetIfActive, AstElemVector&& elems)
|
||||
: kind_(kind),
|
||||
targetTable_(targetTable),
|
||||
offsetIfActive_(offsetIfActive),
|
||||
elems_(std::move(elems)) {}
|
||||
|
||||
AstElemSegmentKind kind() const { return kind_; }
|
||||
AstRef targetTable() const { return targetTable_; }
|
||||
AstRef& targetTableRef() { return targetTable_; }
|
||||
bool isPassive() const { return offsetIfActive_ == nullptr; }
|
||||
AstExpr* offsetIfActive() const { return offsetIfActive_; }
|
||||
AstElemVector& elems() { return elems_; }
|
||||
const AstElemVector& elems() const { return elems_; }
|
||||
|
@ -75,6 +75,7 @@ class WasmToken {
|
||||
Data,
|
||||
DataCount,
|
||||
DataDrop,
|
||||
Declared,
|
||||
Drop,
|
||||
Elem,
|
||||
Else,
|
||||
@ -348,6 +349,7 @@ class WasmToken {
|
||||
case CloseParen:
|
||||
case Data:
|
||||
case DataCount:
|
||||
case Declared:
|
||||
case Elem:
|
||||
case Else:
|
||||
case EndOfFile:
|
||||
@ -986,6 +988,9 @@ WasmToken WasmTokenStream::next() {
|
||||
}
|
||||
return WasmToken(WasmToken::Data, begin, cur_);
|
||||
}
|
||||
if (consume(u"declared")) {
|
||||
return WasmToken(WasmToken::Declared, begin, cur_);
|
||||
}
|
||||
if (consume(u"drop")) {
|
||||
return WasmToken(WasmToken::Drop, begin, cur_);
|
||||
}
|
||||
@ -4444,28 +4449,21 @@ static AstExpr* ParseInitializerConstExpression(WasmParseContext& c) {
|
||||
return initExpr;
|
||||
}
|
||||
|
||||
static bool ParseInitializerExpressionOrPassive(WasmParseContext& c,
|
||||
AstExpr** maybeInitExpr) {
|
||||
if (c.ts.getIf(WasmToken::Passive)) {
|
||||
*maybeInitExpr = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static AstExpr* ParseInitializerExpression(WasmParseContext& c) {
|
||||
if (!c.ts.match(WasmToken::OpenParen, c.error)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstExpr* initExpr = ParseExprInsideParens(c);
|
||||
if (!initExpr) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*maybeInitExpr = initExpr;
|
||||
return true;
|
||||
return initExpr;
|
||||
}
|
||||
|
||||
static AstDataSegment* ParseDataSegment(WasmParseContext& c) {
|
||||
@ -4473,9 +4471,12 @@ static AstDataSegment* ParseDataSegment(WasmParseContext& c) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstExpr* offsetIfActive;
|
||||
if (!ParseInitializerExpressionOrPassive(c, &offsetIfActive)) {
|
||||
return nullptr;
|
||||
AstExpr* offsetIfActive = nullptr;
|
||||
if (!c.ts.getIf(WasmToken::Passive)) {
|
||||
offsetIfActive = ParseInitializerExpression(c);
|
||||
if (!offsetIfActive) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AstNameVector fragments(c.lifo);
|
||||
@ -4970,10 +4971,18 @@ static bool ParseTable(WasmParseContext& c, WasmToken token,
|
||||
|
||||
AstElemVector elems(c.lifo);
|
||||
|
||||
AstRef elem;
|
||||
while (c.ts.getIfRef(&elem)) {
|
||||
if (!elems.append(AstElem(elem))) {
|
||||
return false;
|
||||
while (true) {
|
||||
AstRef elem;
|
||||
if (c.ts.getIfRef(&elem)) {
|
||||
if (!elems.append(AstElem(elem))) {
|
||||
return false;
|
||||
}
|
||||
} else if (c.ts.getIf(WasmToken::RefNull)) {
|
||||
if (!elems.append(AstElem(AstNullValue()))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4997,26 +5006,38 @@ static bool ParseTable(WasmParseContext& c, WasmToken token,
|
||||
return false;
|
||||
}
|
||||
|
||||
AstElemSegment* segment =
|
||||
new (c.lifo) AstElemSegment(AstRef(name), zero, std::move(elems));
|
||||
AstElemSegment* segment = new (c.lifo) AstElemSegment(
|
||||
AstElemSegmentKind::Active, AstRef(name), zero, std::move(elems));
|
||||
return segment && module->append(segment);
|
||||
}
|
||||
|
||||
static AstElemSegment* ParseElemSegment(WasmParseContext& c) {
|
||||
// (elem table-name init-expr fnref...)
|
||||
// (elem init-expr fnref...)
|
||||
// (elem table-name init-expr (fnref|ref.null)...)
|
||||
// (elem init-expr (fnref|ref.null)...)
|
||||
// (elem passive (fnref|ref.null)...)
|
||||
// (elem declared fnref...)
|
||||
|
||||
AstRef targetTable = AstRef(0);
|
||||
bool hasTableName = c.ts.getIfRef(&targetTable);
|
||||
|
||||
AstExpr* offsetIfActive;
|
||||
if (!ParseInitializerExpressionOrPassive(c, &offsetIfActive)) {
|
||||
return nullptr;
|
||||
AstElemSegmentKind kind;
|
||||
AstExpr* offsetIfActive = nullptr;
|
||||
|
||||
if (c.ts.getIf(WasmToken::Passive)) {
|
||||
kind = AstElemSegmentKind::Passive;
|
||||
} else if (c.ts.getIf(WasmToken::Declared)) {
|
||||
kind = AstElemSegmentKind::Declared;
|
||||
} else {
|
||||
kind = AstElemSegmentKind::Active;
|
||||
offsetIfActive = ParseInitializerExpression(c);
|
||||
if (!offsetIfActive) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTableName && !offsetIfActive) {
|
||||
c.ts.generateError(c.ts.peek(), "passive segment must not have a table",
|
||||
if (hasTableName && kind != AstElemSegmentKind::Active) {
|
||||
c.ts.generateError(c.ts.peek(),
|
||||
"passive or declared segment must not have a table",
|
||||
c.error);
|
||||
return nullptr;
|
||||
}
|
||||
@ -5031,7 +5052,8 @@ static AstElemSegment* ParseElemSegment(WasmParseContext& c) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!offsetIfActive && c.ts.getIf(WasmToken::RefNull)) {
|
||||
if (kind != AstElemSegmentKind::Declared &&
|
||||
c.ts.getIf(WasmToken::RefNull)) {
|
||||
if (!elems.append(AstElem(AstNullValue()))) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -5041,7 +5063,7 @@ static AstElemSegment* ParseElemSegment(WasmParseContext& c) {
|
||||
}
|
||||
|
||||
return new (c.lifo)
|
||||
AstElemSegment(targetTable, offsetIfActive, std::move(elems));
|
||||
AstElemSegment(kind, targetTable, offsetIfActive, std::move(elems));
|
||||
}
|
||||
|
||||
static bool ParseGlobal(WasmParseContext& c, AstModule* module) {
|
||||
@ -7133,12 +7155,12 @@ static bool EncodeCodeSection(Encoder& e, Uint32Vector* offsets,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EncodeDestinationOffsetOrFlags(Encoder& e, uint32_t index,
|
||||
AstExpr* offsetIfActive) {
|
||||
static bool EncodeDataInitializerKind(Encoder& e, uint32_t index,
|
||||
AstExpr* offsetIfActive) {
|
||||
if (offsetIfActive) {
|
||||
// In the MVP, the following VarU32 is the table or linear memory index
|
||||
// and it must be zero. In the bulk-mem-ops proposal, it is repurposed
|
||||
// as a flag field, and if the index is not zero it must be present.
|
||||
// In the MVP, the following VarU32 is the linear memory index and it must
|
||||
// be zero. In the bulk-mem-ops proposal, it is repurposed as a flag
|
||||
// field, and if the index is not zero it must be present.
|
||||
if (index) {
|
||||
if (!e.writeVarU32(uint32_t(DataSegmentKind::ActiveWithIndex)) ||
|
||||
!e.writeVarU32(index)) {
|
||||
@ -7165,7 +7187,7 @@ static bool EncodeDestinationOffsetOrFlags(Encoder& e, uint32_t index,
|
||||
}
|
||||
|
||||
static bool EncodeDataSegment(Encoder& e, const AstDataSegment& segment) {
|
||||
if (!EncodeDestinationOffsetOrFlags(e, 0, segment.offsetIfActive())) {
|
||||
if (!EncodeDataInitializerKind(e, 0, segment.offsetIfActive())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -7235,13 +7257,70 @@ static bool EncodeDataCountSection(Encoder& e, AstModule& module) {
|
||||
}
|
||||
|
||||
static bool EncodeElemSegment(Encoder& e, AstElemSegment& segment) {
|
||||
if (!EncodeDestinationOffsetOrFlags(e, segment.targetTable().index(),
|
||||
segment.offsetIfActive())) {
|
||||
// There are three bits that control the encoding of an element segment for
|
||||
// up to eight possible encodings. We try to select the encoding for an
|
||||
// element segment that takes the least amount of space, which depends on
|
||||
// whether there are null references in the segment.
|
||||
bool hasRefNull = false;
|
||||
for (const AstElem& elem : segment.elems()) {
|
||||
if (elem.is<AstNullValue>()) {
|
||||
hasRefNull = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Select the encoding that takes the least amount of space
|
||||
ElemSegmentKind kind;
|
||||
switch (segment.kind()) {
|
||||
case AstElemSegmentKind::Active: {
|
||||
kind = segment.targetTable().index() ? ElemSegmentKind::ActiveWithIndex
|
||||
: ElemSegmentKind::Active;
|
||||
break;
|
||||
}
|
||||
case AstElemSegmentKind::Passive: {
|
||||
kind = ElemSegmentKind::Passive;
|
||||
break;
|
||||
}
|
||||
case AstElemSegmentKind::Declared: {
|
||||
kind = ElemSegmentKind::Declared;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ElemSegmentPayload payload = hasRefNull ? ElemSegmentPayload::ElemExpression
|
||||
: ElemSegmentPayload::ExternIndex;
|
||||
|
||||
// Write the flags field
|
||||
if (!e.writeVarU32(ElemSegmentFlags(kind, payload).encoded())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (segment.isPassive()) {
|
||||
if (!e.writeFixedU8(uint8_t(TypeCode::FuncRef))) {
|
||||
// Write the table index if it is not zero
|
||||
if (kind == ElemSegmentKind::ActiveWithIndex &&
|
||||
!e.writeVarU32(segment.targetTable().index())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kind == ElemSegmentKind::Active ||
|
||||
kind == ElemSegmentKind::ActiveWithIndex) {
|
||||
// Write the offset expression
|
||||
if (!EncodeExpr(e, *segment.offsetIfActive())) {
|
||||
return false;
|
||||
}
|
||||
if (!e.writeOp(Op::End)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// An active element segment without explicit index uses the original MVP
|
||||
// encoding, which doesn't include an explicit type or definition kind
|
||||
if (kind != ElemSegmentKind::Active) {
|
||||
// Write the type or definition kind
|
||||
if (payload == ElemSegmentPayload::ElemExpression &&
|
||||
!e.writeFixedU8(uint8_t(TypeCode::FuncRef))) {
|
||||
return false;
|
||||
}
|
||||
if (payload == ElemSegmentPayload::ExternIndex &&
|
||||
!e.writeFixedU8(uint8_t(DefinitionKind::Function))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -7253,19 +7332,19 @@ static bool EncodeElemSegment(Encoder& e, AstElemSegment& segment) {
|
||||
for (const AstElem& elem : segment.elems()) {
|
||||
if (elem.is<AstRef>()) {
|
||||
const AstRef& ref = elem.as<AstRef>();
|
||||
// Passive segments have an initializer expression, for now restricted to
|
||||
// a function index.
|
||||
if (segment.isPassive() && !e.writeFixedU8(uint8_t(Op::RefFunc))) {
|
||||
if (payload == ElemSegmentPayload::ElemExpression &&
|
||||
!e.writeFixedU8(uint8_t(Op::RefFunc))) {
|
||||
return false;
|
||||
}
|
||||
if (!e.writeVarU32(ref.index())) {
|
||||
return false;
|
||||
}
|
||||
if (segment.isPassive() && !e.writeFixedU8(uint8_t(Op::End))) {
|
||||
if (payload == ElemSegmentPayload::ElemExpression &&
|
||||
!e.writeFixedU8(uint8_t(Op::End))) {
|
||||
return false;
|
||||
}
|
||||
} else if (elem.is<AstNullValue>()) {
|
||||
MOZ_ASSERT(segment.isPassive());
|
||||
MOZ_ASSERT(payload == ElemSegmentPayload::ElemExpression);
|
||||
if (!e.writeFixedU8(uint8_t(Op::RefNull)) ||
|
||||
!e.writeFixedU8(uint8_t(Op::End))) {
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user