Commit a3ac513b authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Implement passive element binary format

Passive elements have a different binary format, where the contents are
instructions instead of function indexes:

    0xd0 0x0b       -> (ref.null)
    0xd2 var:x 0x0b -> (ref.func x)

Bug: v8:8891
Change-Id: Ie7e8efe7b5acdf99622880dd97d28d3c13744dff
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1497516
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60020}
parent fee068bf
...@@ -794,6 +794,12 @@ class ModuleDecoderImpl : public Decoder { ...@@ -794,6 +794,12 @@ class ModuleDecoderImpl : public Decoder {
table_index); table_index);
break; break;
} }
} else {
ValueType type = consume_reference_type();
if (type != kWasmAnyFunc) {
error(pc_ - 1, "invalid element segment type");
break;
}
} }
uint32_t num_elem = uint32_t num_elem =
...@@ -806,12 +812,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -806,12 +812,9 @@ class ModuleDecoderImpl : public Decoder {
WasmElemSegment* init = &module_->elem_segments.back(); WasmElemSegment* init = &module_->elem_segments.back();
for (uint32_t j = 0; j < num_elem; j++) { for (uint32_t j = 0; j < num_elem; j++) {
WasmFunction* func = nullptr; uint32_t index = is_active ? consume_element_func_index()
uint32_t index = : consume_passive_element();
consume_func_index(module_.get(), &func, "element function index"); if (failed()) break;
DCHECK_IMPLIES(ok(), func != nullptr);
if (!ok()) break;
DCHECK_EQ(index, func->func_index);
init->entries.push_back(index); init->entries.push_back(index);
} }
} }
...@@ -1607,6 +1610,37 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1607,6 +1610,37 @@ class ModuleDecoderImpl : public Decoder {
*offset = consume_init_expr(module_.get(), kWasmI32); *offset = consume_init_expr(module_.get(), kWasmI32);
} }
} }
uint32_t consume_element_func_index() {
WasmFunction* func = nullptr;
uint32_t index =
consume_func_index(module_.get(), &func, "element function index");
if (failed()) return index;
DCHECK_NE(func, nullptr);
DCHECK_EQ(index, func->func_index);
DCHECK_NE(index, WasmElemSegment::kNullIndex);
return index;
}
uint32_t consume_passive_element() {
uint32_t index = WasmElemSegment::kNullIndex;
uint8_t opcode = consume_u8("element opcode");
if (failed()) return index;
switch (opcode) {
case kExprRefNull:
index = WasmElemSegment::kNullIndex;
break;
case kExprRefFunc:
index = consume_element_func_index();
if (failed()) return index;
break;
default:
error("invalid opcode in element");
break;
}
expect_u8("end opcode", kExprEnd);
return index;
}
}; };
ModuleResult DecodeWasmModule(const WasmFeatures& enabled, ModuleResult DecodeWasmModule(const WasmFeatures& enabled,
......
...@@ -1478,15 +1478,28 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance, ...@@ -1478,15 +1478,28 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
JSToWasmWrapperCache* js_to_wasm_cache, JSToWasmWrapperCache* js_to_wasm_cache,
const WasmElemSegment& elem_segment, uint32_t dst, const WasmElemSegment& elem_segment, uint32_t dst,
uint32_t src, size_t count) { uint32_t src, size_t count) {
// TODO(wasm): Move this functionality into wasm-objects, since it is used
// for both instantiation and in the implementation of the table.init
// instruction.
if (!IsInBounds(dst, count, table_instance.table_size)) return false; if (!IsInBounds(dst, count, table_instance.table_size)) return false;
if (!IsInBounds(src, count, elem_segment.entries.size())) return false; if (!IsInBounds(src, count, elem_segment.entries.size())) return false;
const WasmModule* module = instance->module(); const WasmModule* module = instance->module();
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
uint32_t func_index = elem_segment.entries[src + i]; uint32_t func_index = elem_segment.entries[src + i];
const WasmFunction* function = &module->functions[func_index];
int entry_index = static_cast<int>(dst + i); int entry_index = static_cast<int>(dst + i);
if (func_index == WasmElemSegment::kNullIndex) {
IndirectFunctionTableEntry(instance, entry_index).clear();
if (!table_instance.table_object.is_null()) {
WasmTableObject::Set(isolate, table_instance.table_object, entry_index,
Handle<JSFunction>::null());
}
continue;
}
const WasmFunction* function = &module->functions[func_index];
// Update the local dispatch table first. // Update the local dispatch table first.
uint32_t sig_id = module->signature_ids[function->sig_index]; uint32_t sig_id = module->signature_ids[function->sig_index];
IndirectFunctionTableEntry(instance, entry_index) IndirectFunctionTableEntry(instance, entry_index)
......
...@@ -120,6 +120,10 @@ struct WasmElemSegment { ...@@ -120,6 +120,10 @@ struct WasmElemSegment {
// Construct a passive segment, which has no table index or offset. // Construct a passive segment, which has no table index or offset.
WasmElemSegment() : table_index(0), active(false) {} WasmElemSegment() : table_index(0), active(false) {}
// Used in the {entries} vector to represent a `ref.null` entry in a passive
// segment.
static const uint32_t kNullIndex = ~0u;
uint32_t table_index; uint32_t table_index;
WasmInitExpr offset; WasmInitExpr offset;
std::vector<uint32_t> entries; std::vector<uint32_t> entries;
......
...@@ -107,6 +107,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -107,6 +107,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_FLOAT_OP(CopySign, "copysign") CASE_FLOAT_OP(CopySign, "copysign")
CASE_REF_OP(Null, "null") CASE_REF_OP(Null, "null")
CASE_REF_OP(IsNull, "is_null") CASE_REF_OP(IsNull, "is_null")
CASE_REF_OP(Func, "func")
CASE_I32_OP(ConvertI64, "wrap/i64") CASE_I32_OP(ConvertI64, "wrap/i64")
CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc") CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc") CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
......
...@@ -56,7 +56,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); ...@@ -56,7 +56,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(I64Const, 0x42, _) \ V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \ V(F32Const, 0x43, _) \
V(F64Const, 0x44, _) \ V(F64Const, 0x44, _) \
V(RefNull, 0xd0, _) V(RefNull, 0xd0, _) \
V(RefFunc, 0xd2, _)
// Load memory expressions. // Load memory expressions.
#define FOREACH_LOAD_MEM_OPCODE(V) \ #define FOREACH_LOAD_MEM_OPCODE(V) \
......
...@@ -38,7 +38,7 @@ function assertTable(obj, ...elems) { ...@@ -38,7 +38,7 @@ function assertTable(obj, ...elems) {
{ {
let o = addFunctions(builder, kTableSize, true); let o = addFunctions(builder, kTableSize, true);
builder.addPassiveElementSegment( builder.addPassiveElementSegment(
[o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]); [o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index, null]);
} }
builder.addFunction("init0", sig_v_iii) builder.addFunction("init0", sig_v_iii)
...@@ -71,6 +71,10 @@ function assertTable(obj, ...elems) { ...@@ -71,6 +71,10 @@ function assertTable(obj, ...elems) {
assertTable(x.table, x.f0, x.f1, x.f2, x.f2, x.f3); assertTable(x.table, x.f0, x.f1, x.f2, x.f2, x.f3);
x.init0(3, 3, 2); x.init0(3, 3, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f3, x.f4); assertTable(x.table, x.f0, x.f1, x.f2, x.f3, x.f4);
// test writing null
x.init0(0, 5, 1);
assertTable(x.table, null, x.f1, x.f2, x.f3, x.f4);
})(); })();
(function TestTableInitOob() { (function TestTableInitOob() {
......
...@@ -215,11 +215,6 @@ let kExprGetGlobal = 0x23; ...@@ -215,11 +215,6 @@ let kExprGetGlobal = 0x23;
let kExprSetGlobal = 0x24; let kExprSetGlobal = 0x24;
let kExprGetTable = 0x25; let kExprGetTable = 0x25;
let kExprSetTable = 0x26; let kExprSetTable = 0x26;
let kExprI32Const = 0x41;
let kExprI64Const = 0x42;
let kExprF32Const = 0x43;
let kExprF64Const = 0x44;
let kExprRefNull = 0xd0;
let kExprI32LoadMem = 0x28; let kExprI32LoadMem = 0x28;
let kExprI64LoadMem = 0x29; let kExprI64LoadMem = 0x29;
let kExprF32LoadMem = 0x2a; let kExprF32LoadMem = 0x2a;
...@@ -245,6 +240,10 @@ let kExprI64StoreMem16 = 0x3d; ...@@ -245,6 +240,10 @@ let kExprI64StoreMem16 = 0x3d;
let kExprI64StoreMem32 = 0x3e; let kExprI64StoreMem32 = 0x3e;
let kExprMemorySize = 0x3f; let kExprMemorySize = 0x3f;
let kExprMemoryGrow = 0x40; let kExprMemoryGrow = 0x40;
let kExprI32Const = 0x41;
let kExprI64Const = 0x42;
let kExprF32Const = 0x43;
let kExprF64Const = 0x44;
let kExprI32Eqz = 0x45; let kExprI32Eqz = 0x45;
let kExprI32Eq = 0x46; let kExprI32Eq = 0x46;
let kExprI32Ne = 0x47; let kExprI32Ne = 0x47;
...@@ -279,7 +278,6 @@ let kExprF64Lt = 0x63; ...@@ -279,7 +278,6 @@ let kExprF64Lt = 0x63;
let kExprF64Gt = 0x64; let kExprF64Gt = 0x64;
let kExprF64Le = 0x65; let kExprF64Le = 0x65;
let kExprF64Ge = 0x66; let kExprF64Ge = 0x66;
let kExprRefIsNull = 0xd1;
let kExprI32Clz = 0x67; let kExprI32Clz = 0x67;
let kExprI32Ctz = 0x68; let kExprI32Ctz = 0x68;
let kExprI32Popcnt = 0x69; let kExprI32Popcnt = 0x69;
...@@ -374,6 +372,9 @@ let kExprI32SExtendI16 = 0xc1; ...@@ -374,6 +372,9 @@ let kExprI32SExtendI16 = 0xc1;
let kExprI64SExtendI8 = 0xc2; let kExprI64SExtendI8 = 0xc2;
let kExprI64SExtendI16 = 0xc3; let kExprI64SExtendI16 = 0xc3;
let kExprI64SExtendI32 = 0xc4; let kExprI64SExtendI32 = 0xc4;
let kExprRefNull = 0xd0;
let kExprRefIsNull = 0xd1;
let kExprRefFunc = 0xd2;
// Prefix opcodes // Prefix opcodes
let kNumericPrefix = 0xfc; let kNumericPrefix = 0xfc;
...@@ -1101,6 +1102,7 @@ class WasmModuleBuilder { ...@@ -1101,6 +1102,7 @@ class WasmModuleBuilder {
for (let init of inits) { for (let init of inits) {
if (init.is_active) { if (init.is_active) {
// Active segment.
section.emit_u8(0); // table index / flags section.emit_u8(0); // table index / flags
if (init.is_global) { if (init.is_global) {
section.emit_u8(kExprGetGlobal); section.emit_u8(kExprGetGlobal);
...@@ -1109,12 +1111,25 @@ class WasmModuleBuilder { ...@@ -1109,12 +1111,25 @@ class WasmModuleBuilder {
} }
section.emit_u32v(init.base); section.emit_u32v(init.base);
section.emit_u8(kExprEnd); section.emit_u8(kExprEnd);
section.emit_u32v(init.array.length);
for (let index of init.array) {
section.emit_u32v(index);
}
} else { } else {
// Passive segment.
section.emit_u8(kPassive); // flags section.emit_u8(kPassive); // flags
} section.emit_u8(kWasmAnyFunc);
section.emit_u32v(init.array.length); section.emit_u32v(init.array.length);
for (let index of init.array) { for (let index of init.array) {
if (index === null) {
section.emit_u8(kExprRefNull);
section.emit_u8(kExprEnd);
} else {
section.emit_u8(kExprRefFunc);
section.emit_u32v(index); section.emit_u32v(index);
section.emit_u8(kExprEnd);
}
}
} }
} }
}); });
......
...@@ -32,6 +32,9 @@ namespace module_decoder_unittest { ...@@ -32,6 +32,9 @@ namespace module_decoder_unittest {
#define WASM_INIT_EXPR_ANYREF WASM_REF_NULL, kExprEnd #define WASM_INIT_EXPR_ANYREF WASM_REF_NULL, kExprEnd
#define WASM_INIT_EXPR_GLOBAL(index) WASM_GET_GLOBAL(index), kExprEnd #define WASM_INIT_EXPR_GLOBAL(index) WASM_GET_GLOBAL(index), kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kExprEnd
#define REF_FUNC_ELEMENT(v) kExprRefFunc, U32V_1(v), kExprEnd
#define EMPTY_BODY 0 #define EMPTY_BODY 0
#define NOP_BODY 2, 0, kExprNop #define NOP_BODY 2, 0, kExprNop
...@@ -2258,8 +2261,8 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) { ...@@ -2258,8 +2261,8 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
// table declaration ----------------------------------------------------- // table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalAnyFunc, 0, 1), SECTION(Table, ENTRY_COUNT(1), kLocalAnyFunc, 0, 1),
// element segments ----------------------------------------------------- // element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, SECTION(Element, ENTRY_COUNT(1), PASSIVE, kLocalAnyFunc, U32V_1(3),
ADD_COUNT(FUNC_INDEX(0), FUNC_INDEX(0))), REF_FUNC_ELEMENT(0), REF_FUNC_ELEMENT(0), REF_NULL_ELEMENT),
// code ------------------------------------------------------------------ // code ------------------------------------------------------------------
ONE_EMPTY_BODY}; ONE_EMPTY_BODY};
EXPECT_FAILURE(data); EXPECT_FAILURE(data);
...@@ -2268,6 +2271,22 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) { ...@@ -2268,6 +2271,22 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5); EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
} }
TEST_F(WasmModuleVerifyTest, PassiveElementSegmentAnyRef) {
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalAnyFunc, 0, 1),
// element segments -----------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kLocalAnyRef, U32V_1(0)),
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) { TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) {
static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)), static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0)), SECTION(DataCount, ENTRY_COUNT(0)),
...@@ -2375,6 +2394,8 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) { ...@@ -2375,6 +2394,8 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
#undef WASM_INIT_EXPR_F64 #undef WASM_INIT_EXPR_F64
#undef WASM_INIT_EXPR_ANYREF #undef WASM_INIT_EXPR_ANYREF
#undef WASM_INIT_EXPR_GLOBAL #undef WASM_INIT_EXPR_GLOBAL
#undef REF_NULL_ELEMENT
#undef REF_FUNC_ELEMENT
#undef EMPTY_BODY #undef EMPTY_BODY
#undef NOP_BODY #undef NOP_BODY
#undef SIG_ENTRY_i_i #undef SIG_ENTRY_i_i
......
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