[wasm][bulk] Update the element segment decoding to the new spec changes

The element segment encoding in the bulk memory proposal changed
recently. With this CL the V8 implementation gets up to date again.

R=thibaudm@chromium.org

Bug: v8:9658
Change-Id: I4f45d04369400356a6f3aaed9570c7870f5f97bd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1778022
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63836}
This commit is contained in:
Andreas Haas 2019-09-16 17:40:20 +02:00 committed by Commit Bot
parent e92e8871dd
commit d8b0c1e3e7
7 changed files with 171 additions and 30 deletions

View File

@ -797,9 +797,11 @@ class ModuleDecoderImpl : public Decoder {
const byte* pos = pc();
bool is_active;
bool functions_as_elements;
uint32_t table_index;
WasmInitExpr offset;
consume_segment_header("table index", &is_active, &table_index, &offset);
consume_element_segment_header(&is_active, &functions_as_elements,
&table_index, &offset);
if (failed()) return;
if (is_active) {
@ -814,12 +816,6 @@ class ModuleDecoderImpl : public Decoder {
table_index);
break;
}
} else {
ValueType type = consume_reference_type();
if (!ValueTypes::IsSubType(kWasmFuncRef, type)) {
error(pc_ - 1, "invalid element segment type");
break;
}
}
uint32_t num_elem =
@ -832,8 +828,8 @@ class ModuleDecoderImpl : public Decoder {
WasmElemSegment* init = &module_->elem_segments.back();
for (uint32_t j = 0; j < num_elem; j++) {
uint32_t index = is_active ? consume_element_func_index()
: consume_passive_element();
uint32_t index = functions_as_elements ? consume_element_expr()
: consume_element_func_index();
if (failed()) break;
init->entries.push_back(index);
}
@ -910,8 +906,7 @@ class ModuleDecoderImpl : public Decoder {
bool is_active;
uint32_t memory_index;
WasmInitExpr dest_addr;
consume_segment_header("memory index", &is_active, &memory_index,
&dest_addr);
consume_data_segment_header(&is_active, &memory_index, &dest_addr);
if (failed()) break;
if (is_active && memory_index != 0) {
@ -1681,8 +1676,103 @@ class ModuleDecoderImpl : public Decoder {
return attribute;
}
void consume_segment_header(const char* name, bool* is_active,
uint32_t* index, WasmInitExpr* offset) {
void consume_element_segment_header(bool* is_active,
bool* functions_as_elements,
uint32_t* table_index,
WasmInitExpr* offset) {
const byte* pos = pc();
uint8_t flag;
if (enabled_features_.bulk_memory || enabled_features_.anyref) {
flag = consume_u8("flag");
} else {
uint32_t table_index = consume_u32v("table index");
// The only valid flag value without bulk_memory or anyref is '0'.
if (table_index != 0) {
error(
"Element segments with table indices require "
"--experimental-wasm-bulk-memory or --experimental-wasm-anyref");
return;
}
flag = 0;
}
// The mask for the bit in the flag which indicates if the segment is
// active or not.
constexpr uint8_t kIsPassiveMask = 0x01;
// The mask for the bit in the flag which indicates if the segment has an
// explicit table index field.
constexpr uint8_t kHasTableIndexMask = 0x02;
// The mask for the bit in the flag which indicates if the functions of this
// segment are defined as function indices (=0) or elements(=1).
constexpr uint8_t kFunctionsAsElementsMask = 0x04;
constexpr uint8_t kFullMask =
kIsPassiveMask | kHasTableIndexMask | kFunctionsAsElementsMask;
bool is_passive = flag & kIsPassiveMask;
*is_active = !is_passive;
*functions_as_elements = flag & kFunctionsAsElementsMask;
bool has_table_index = flag & kHasTableIndexMask;
if (is_passive && !enabled_features_.bulk_memory) {
error("Passive element segments require --experimental-wasm-bulk-memory");
return;
}
if (*functions_as_elements && !enabled_features_.bulk_memory) {
error(
"Illegal segment flag. Did you forget "
"--experimental-wasm-bulk-memory?");
return;
}
if (flag != 0 && !enabled_features_.bulk_memory &&
!enabled_features_.anyref) {
error(
"Invalid segment flag. Did you forget "
"--experimental-wasm-bulk-memory or --experimental-wasm-anyref?");
return;
}
if ((flag & kFullMask) != flag || (!(*is_active) && has_table_index)) {
errorf(pos, "illegal flag value %u. Must be 0, 1, 2, 4, 5 or 6", flag);
}
if (has_table_index) {
*table_index = consume_u32v("table index");
} else {
*table_index = 0;
}
if (*is_active) {
*offset = consume_init_expr(module_.get(), kWasmI32);
}
if (*is_active && !has_table_index) {
// Active segments without table indices are a special case for backwards
// compatibility. These cases have an implicit element kind or element
// type, so we are done already with the segment header.
return;
}
if (*functions_as_elements) {
// We have to check that there is an element type of type FuncRef. All
// other element types are not valid yet.
ValueType type = consume_reference_type();
if (!ValueTypes::IsSubType(kWasmFuncRef, type)) {
error(pc_ - 1, "invalid element segment type");
return;
}
} else {
// We have to check that there is an element kind of type Function. All
// other element kinds are not valid yet.
uint8_t val = consume_u8("element kind");
ImportExportKindCode kind = static_cast<ImportExportKindCode>(val);
if (kind != kExternalFunction) {
errorf(pos, "illegal element kind %x. Must be 0x00", val);
return;
}
}
}
void consume_data_segment_header(bool* is_active, uint32_t* index,
WasmInitExpr* offset) {
const byte* pos = pc();
uint32_t flag = consume_u32v("flag");
@ -1718,7 +1808,7 @@ class ModuleDecoderImpl : public Decoder {
}
if (flag == SegmentFlags::kActiveWithIndex) {
*is_active = true;
*index = consume_u32v(name);
*index = consume_u32v("memory index");
*offset = consume_init_expr(module_.get(), kWasmI32);
}
}
@ -1734,7 +1824,7 @@ class ModuleDecoderImpl : public Decoder {
return index;
}
uint32_t consume_passive_element() {
uint32_t consume_element_expr() {
uint32_t index = WasmElemSegment::kNullIndex;
uint8_t opcode = consume_u8("element opcode");
if (failed()) return index;

View File

@ -27,12 +27,14 @@
#define ACTIVE_NO_INDEX 0
#define PASSIVE 1
#define ACTIVE_WITH_INDEX 2
#define PASSIVE_WITH_ELEMENTS 5
#define ACTIVE_WITH_ELEMENTS 6
// The table index field in an element segment was repurposed as a flags field.
// To specify a table index, we have to set the flag value to 2, followed by
// the table index.
#define TABLE_INDEX0 U32V_1(ACTIVE_NO_INDEX)
#define TABLE_INDEX(v) U32V_1(ACTIVE_WITH_INDEX), U32V_1(v)
#define TABLE_INDEX0 static_cast<byte>(ACTIVE_NO_INDEX)
#define TABLE_INDEX(v) static_cast<byte>(ACTIVE_WITH_INDEX), U32V_1(v)
#define ZERO_ALIGNMENT 0
#define ZERO_OFFSET 0

View File

@ -84,6 +84,7 @@ let kSharedHasMaximumFlag = 3;
let kActiveNoIndex = 0;
let kPassive = 1;
let kActiveWithIndex = 2;
let kPassiveWithElements = 5;
// Function declaration flags
let kDeclFunctionName = 0x01;
@ -1164,13 +1165,16 @@ class WasmModuleBuilder {
}
section.emit_u32v(init.base);
section.emit_u8(kExprEnd);
if (init.table != 0) {
section.emit_u8(kExternalFunction);
}
section.emit_u32v(init.array.length);
for (let index of init.array) {
section.emit_u32v(index);
}
} else {
// Passive segment.
section.emit_u8(kPassive); // flags
section.emit_u8(kPassiveWithElements); // flags
section.emit_u8(kWasmAnyFunc);
section.emit_u32v(init.array.length);
for (let index of init.array) {

View File

@ -1086,6 +1086,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionMultipleTables) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 1
WASM_INIT_EXPR_I32V_1(7), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0)), // entry 1
@ -1132,15 +1133,18 @@ TEST_F(WasmModuleVerifyTest, ElementSectionMixedTables) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 1
WASM_INIT_EXPR_I32V_1(7), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0), // entry 1
TABLE_INDEX(2), // element for table 2
WASM_INIT_EXPR_I32V_1(12), // index
kExternalFunction, // type
1, // elements count
FUNC_INDEX(0), // function
TABLE_INDEX(3), // element for table 1
WASM_INIT_EXPR_I32V_1(17), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0)), // entry 1
@ -1173,6 +1177,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionMultipleTablesArbitraryOrder) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 0
WASM_INIT_EXPR_I32V_1(7), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0), // entry 1
@ -1219,10 +1224,12 @@ TEST_F(WasmModuleVerifyTest, ElementSectionMixedTablesArbitraryOrder) {
4, // entry count
TABLE_INDEX(2), // element for table 0
WASM_INIT_EXPR_I32V_1(10), // index
kExternalFunction, // type
1, // elements count
FUNC_INDEX(0), // function
TABLE_INDEX(3), // element for table 1
WASM_INIT_EXPR_I32V_1(17), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0), // entry 1
@ -1232,6 +1239,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionMixedTablesArbitraryOrder) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 1
WASM_INIT_EXPR_I32V_1(7), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0)), // entry 1
@ -1262,6 +1270,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionInitAnyRefTableWithFuncRef) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 1
WASM_INIT_EXPR_I32V_1(7), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0)), // entry 1
@ -1309,6 +1318,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionDontInitAnyRefImportedTable) {
FUNC_INDEX(0), // function
TABLE_INDEX(1), // element for table 1
WASM_INIT_EXPR_I32V_1(17), // index
kExternalFunction, // type
2, // elements count
FUNC_INDEX(0), // entry 0
FUNC_INDEX(0)), // entry 1
@ -2359,6 +2369,26 @@ TEST_F(WasmModuleVerifyTest, PassiveDataSegment) {
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
}
TEST_F(WasmModuleVerifyTest, ActiveElementSegmentWithElements) {
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1),
// element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), ACTIVE_WITH_ELEMENTS, TABLE_INDEX0,
WASM_INIT_EXPR_I32V_1(0), kLocalFuncRef, U32V_1(3),
REF_FUNC_ELEMENT(0), REF_FUNC_ELEMENT(0), REF_NULL_ELEMENT),
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(data);
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
}
TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
@ -2368,8 +2398,9 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
// table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1),
// element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kLocalFuncRef, U32V_1(3),
REF_FUNC_ELEMENT(0), REF_FUNC_ELEMENT(0), REF_NULL_ELEMENT),
SECTION(Element, ENTRY_COUNT(1), PASSIVE_WITH_ELEMENTS, kLocalFuncRef,
U32V_1(3), REF_FUNC_ELEMENT(0), REF_FUNC_ELEMENT(0),
REF_NULL_ELEMENT),
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE(data);
@ -2387,13 +2418,33 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegmentAnyRef) {
// table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1),
// element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kLocalAnyRef, U32V_1(0)),
SECTION(Element, ENTRY_COUNT(1), PASSIVE_WITH_ELEMENTS, kLocalAnyRef,
U32V_1(0)),
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, PassiveElementSegmentWithIndices) {
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1),
// element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kExternalFunction,
ENTRY_COUNT(3), U32V_1(0), U32V_1(0), U32V_1(0)),
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(data);
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
}
TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) {
static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0)),

View File

@ -1 +1 @@
1e0e2435efac858f3269de548d7c795fb6173ca8
2ecf4038f24fc08bd9da504f15942d3abb5ec685

View File

@ -1 +1 @@
088e8722b3fe99ea3a67a9852e787f461f348d0b
f9bd936c708402051f87f4ac8940d1916112a15a

View File

@ -10,15 +10,9 @@
# the bulk-memory proposal. Since we've enabled bulk-memory by default, we
# need to update to use its testsuite.
'linking': [FAIL],
'binary-leb128': [FAIL],
'elem': [FAIL],
'data': [FAIL],
# TODO(v8:9658): The encoding of element segments changed in the bulk memory
# proposal
'proposals/bulk-memory-operations/bulk': [FAIL],
'proposals/bulk-memory-operations/table_init': [FAIL],
'proposals/bulk-memory-operations/table_copy': [FAIL],
'proposals/bulk-memory-operations/elem': [FAIL],
'proposals/bulk-memory-operations/binary': [FAIL],
# TODO(mstarzinger): Roll newest tests into "js-types" repository.
'proposals/js-types/exports': [FAIL],
'proposals/js-types/globals': [FAIL],