[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: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#60020}
This commit is contained in:
parent
fee068bf2f
commit
a3ac513b5e
@ -794,6 +794,12 @@ class ModuleDecoderImpl : public Decoder {
|
||||
table_index);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ValueType type = consume_reference_type();
|
||||
if (type != kWasmAnyFunc) {
|
||||
error(pc_ - 1, "invalid element segment type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t num_elem =
|
||||
@ -806,12 +812,9 @@ class ModuleDecoderImpl : public Decoder {
|
||||
|
||||
WasmElemSegment* init = &module_->elem_segments.back();
|
||||
for (uint32_t j = 0; j < num_elem; j++) {
|
||||
WasmFunction* func = nullptr;
|
||||
uint32_t index =
|
||||
consume_func_index(module_.get(), &func, "element function index");
|
||||
DCHECK_IMPLIES(ok(), func != nullptr);
|
||||
if (!ok()) break;
|
||||
DCHECK_EQ(index, func->func_index);
|
||||
uint32_t index = is_active ? consume_element_func_index()
|
||||
: consume_passive_element();
|
||||
if (failed()) break;
|
||||
init->entries.push_back(index);
|
||||
}
|
||||
}
|
||||
@ -1607,6 +1610,37 @@ class ModuleDecoderImpl : public Decoder {
|
||||
*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,
|
||||
|
@ -1478,15 +1478,28 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
JSToWasmWrapperCache* js_to_wasm_cache,
|
||||
const WasmElemSegment& elem_segment, uint32_t dst,
|
||||
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(src, count, elem_segment.entries.size())) return false;
|
||||
|
||||
const WasmModule* module = instance->module();
|
||||
for (uint32_t i = 0; i < count; ++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);
|
||||
|
||||
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.
|
||||
uint32_t sig_id = module->signature_ids[function->sig_index];
|
||||
IndirectFunctionTableEntry(instance, entry_index)
|
||||
|
@ -120,6 +120,10 @@ struct WasmElemSegment {
|
||||
// Construct a passive segment, which has no table index or offset.
|
||||
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;
|
||||
WasmInitExpr offset;
|
||||
std::vector<uint32_t> entries;
|
||||
|
@ -107,6 +107,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
||||
CASE_FLOAT_OP(CopySign, "copysign")
|
||||
CASE_REF_OP(Null, "null")
|
||||
CASE_REF_OP(IsNull, "is_null")
|
||||
CASE_REF_OP(Func, "func")
|
||||
CASE_I32_OP(ConvertI64, "wrap/i64")
|
||||
CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc")
|
||||
CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc")
|
||||
|
@ -56,7 +56,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
|
||||
V(I64Const, 0x42, _) \
|
||||
V(F32Const, 0x43, _) \
|
||||
V(F64Const, 0x44, _) \
|
||||
V(RefNull, 0xd0, _)
|
||||
V(RefNull, 0xd0, _) \
|
||||
V(RefFunc, 0xd2, _)
|
||||
|
||||
// Load memory expressions.
|
||||
#define FOREACH_LOAD_MEM_OPCODE(V) \
|
||||
|
@ -38,7 +38,7 @@ function assertTable(obj, ...elems) {
|
||||
{
|
||||
let o = addFunctions(builder, kTableSize, true);
|
||||
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)
|
||||
@ -71,6 +71,10 @@ function assertTable(obj, ...elems) {
|
||||
assertTable(x.table, x.f0, x.f1, x.f2, x.f2, x.f3);
|
||||
x.init0(3, 3, 2);
|
||||
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() {
|
||||
|
@ -215,11 +215,6 @@ let kExprGetGlobal = 0x23;
|
||||
let kExprSetGlobal = 0x24;
|
||||
let kExprGetTable = 0x25;
|
||||
let kExprSetTable = 0x26;
|
||||
let kExprI32Const = 0x41;
|
||||
let kExprI64Const = 0x42;
|
||||
let kExprF32Const = 0x43;
|
||||
let kExprF64Const = 0x44;
|
||||
let kExprRefNull = 0xd0;
|
||||
let kExprI32LoadMem = 0x28;
|
||||
let kExprI64LoadMem = 0x29;
|
||||
let kExprF32LoadMem = 0x2a;
|
||||
@ -245,6 +240,10 @@ let kExprI64StoreMem16 = 0x3d;
|
||||
let kExprI64StoreMem32 = 0x3e;
|
||||
let kExprMemorySize = 0x3f;
|
||||
let kExprMemoryGrow = 0x40;
|
||||
let kExprI32Const = 0x41;
|
||||
let kExprI64Const = 0x42;
|
||||
let kExprF32Const = 0x43;
|
||||
let kExprF64Const = 0x44;
|
||||
let kExprI32Eqz = 0x45;
|
||||
let kExprI32Eq = 0x46;
|
||||
let kExprI32Ne = 0x47;
|
||||
@ -279,7 +278,6 @@ let kExprF64Lt = 0x63;
|
||||
let kExprF64Gt = 0x64;
|
||||
let kExprF64Le = 0x65;
|
||||
let kExprF64Ge = 0x66;
|
||||
let kExprRefIsNull = 0xd1;
|
||||
let kExprI32Clz = 0x67;
|
||||
let kExprI32Ctz = 0x68;
|
||||
let kExprI32Popcnt = 0x69;
|
||||
@ -374,6 +372,9 @@ let kExprI32SExtendI16 = 0xc1;
|
||||
let kExprI64SExtendI8 = 0xc2;
|
||||
let kExprI64SExtendI16 = 0xc3;
|
||||
let kExprI64SExtendI32 = 0xc4;
|
||||
let kExprRefNull = 0xd0;
|
||||
let kExprRefIsNull = 0xd1;
|
||||
let kExprRefFunc = 0xd2;
|
||||
|
||||
// Prefix opcodes
|
||||
let kNumericPrefix = 0xfc;
|
||||
@ -1101,6 +1102,7 @@ class WasmModuleBuilder {
|
||||
|
||||
for (let init of inits) {
|
||||
if (init.is_active) {
|
||||
// Active segment.
|
||||
section.emit_u8(0); // table index / flags
|
||||
if (init.is_global) {
|
||||
section.emit_u8(kExprGetGlobal);
|
||||
@ -1109,12 +1111,25 @@ class WasmModuleBuilder {
|
||||
}
|
||||
section.emit_u32v(init.base);
|
||||
section.emit_u8(kExprEnd);
|
||||
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_u32v(init.array.length);
|
||||
for (let index of init.array) {
|
||||
section.emit_u32v(index);
|
||||
section.emit_u8(kWasmAnyFunc);
|
||||
section.emit_u32v(init.array.length);
|
||||
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_u8(kExprEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -32,6 +32,9 @@ namespace module_decoder_unittest {
|
||||
#define WASM_INIT_EXPR_ANYREF WASM_REF_NULL, 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 NOP_BODY 2, 0, kExprNop
|
||||
|
||||
@ -2258,8 +2261,8 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
|
||||
// table declaration -----------------------------------------------------
|
||||
SECTION(Table, ENTRY_COUNT(1), kLocalAnyFunc, 0, 1),
|
||||
// element segments -----------------------------------------------------
|
||||
SECTION(Element, ENTRY_COUNT(1), PASSIVE,
|
||||
ADD_COUNT(FUNC_INDEX(0), FUNC_INDEX(0))),
|
||||
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kLocalAnyFunc, U32V_1(3),
|
||||
REF_FUNC_ELEMENT(0), REF_FUNC_ELEMENT(0), REF_NULL_ELEMENT),
|
||||
// code ------------------------------------------------------------------
|
||||
ONE_EMPTY_BODY};
|
||||
EXPECT_FAILURE(data);
|
||||
@ -2268,6 +2271,22 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
|
||||
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) {
|
||||
static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)),
|
||||
SECTION(DataCount, ENTRY_COUNT(0)),
|
||||
@ -2375,6 +2394,8 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
|
||||
#undef WASM_INIT_EXPR_F64
|
||||
#undef WASM_INIT_EXPR_ANYREF
|
||||
#undef WASM_INIT_EXPR_GLOBAL
|
||||
#undef REF_NULL_ELEMENT
|
||||
#undef REF_FUNC_ELEMENT
|
||||
#undef EMPTY_BODY
|
||||
#undef NOP_BODY
|
||||
#undef SIG_ENTRY_i_i
|
||||
|
Loading…
Reference in New Issue
Block a user