[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:
Ben Smith 2019-03-04 10:52:03 -08:00 committed by Commit Bot
parent fee068bf2f
commit a3ac513b5e
8 changed files with 114 additions and 21 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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;

View File

@ -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")

View File

@ -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) \

View File

@ -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() {

View File

@ -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);
}
}
}
}
});

View File

@ -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