Commit 4f8058e3 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Support table.init for multiple tables

Even though this is not spec'ed yet, it's good to have an implementation
so that we can use clusterfuzz on it.

R=binji@chromium.org

Bug: v8:7581
Change-Id: I323625322e5240dc6ac224dce8a1f1f7f6070758
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1695478Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62656}
parent f43944a2
......@@ -1626,6 +1626,7 @@ void InstanceBuilder::InitializeIndirectFunctionTables(
bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
Handle<WasmTableObject> table_object,
uint32_t table_index,
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
......@@ -1643,9 +1644,7 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
if (func_index == WasmElemSegment::kNullIndex) {
if (table_object->type() == kWasmFuncRef) {
IndirectFunctionTableEntry(instance, elem_segment.table_index,
entry_index)
.clear();
IndirectFunctionTableEntry(instance, table_index, entry_index).clear();
}
WasmTableObject::Set(isolate, table_object, entry_index,
isolate->factory()->null_value());
......@@ -1657,8 +1656,7 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
// Update the local dispatch table first if necessary.
if (table_object->type() == kWasmFuncRef) {
uint32_t sig_id = module->signature_ids[function->sig_index];
IndirectFunctionTableEntry(instance, elem_segment.table_index,
entry_index)
IndirectFunctionTableEntry(instance, table_index, entry_index)
.Set(sig_id, instance, func_index);
}
......@@ -1699,6 +1697,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
// Passive segments are not copied during instantiation.
if (!elem_segment.active) continue;
uint32_t table_index = elem_segment.table_index;
uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
uint32_t src = 0;
size_t count = elem_segment.entries.size();
......@@ -1708,7 +1707,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
handle(WasmTableObject::cast(
instance->tables().get(elem_segment.table_index)),
isolate_),
elem_segment, dst, src, count);
table_index, elem_segment, dst, src, count);
if (enabled_.bulk_memory) {
if (!success) {
thrower_->LinkError("table initializer is out of bounds");
......@@ -1749,19 +1748,12 @@ void InstanceBuilder::InitializeExceptions(
bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t table_index, uint32_t segment_index, uint32_t dst,
uint32_t src, uint32_t count) {
// This code path is only used for passive element segments with the
// table.init instruction. This instruction was introduced in the
// bulk-memory-operations proposal. At the moment, table.init can only operate
// on table-0. If table.init should work for tables with higher indices, then
// we have to adjust the code in {LoadElemSegmentImpl}. The code there uses
// {IndirectFunctionTableEntry} at the moment, which only works for table-0.
CHECK_EQ(table_index, 0);
auto& elem_segment = instance->module()->elem_segments[segment_index];
return LoadElemSegmentImpl(
isolate, instance,
handle(WasmTableObject::cast(instance->tables().get(table_index)),
isolate),
elem_segment, dst, src, count);
table_index, elem_segment, dst, src, count);
}
} // namespace wasm
......
......@@ -742,7 +742,8 @@ class WasmIndirectFunctionTable : public Struct {
DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign)
DECL_ACCESSORS(refs, FixedArray)
static Handle<WasmIndirectFunctionTable> New(Isolate* isolate, uint32_t size);
V8_EXPORT_PRIVATE static Handle<WasmIndirectFunctionTable> New(
Isolate* isolate, uint32_t size);
static void Resize(Isolate* isolate, Handle<WasmIndirectFunctionTable> table,
uint32_t new_size);
......
......@@ -396,7 +396,7 @@ void CheckTableCall(Isolate* isolate, Handle<WasmTableObject> table,
}
} // namespace
WASM_EXEC_TEST(TableInitElems) {
void TestTableInitElems(ExecutionTier execution_tier, int table_index) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
......@@ -416,21 +416,26 @@ WASM_EXEC_TEST(TableInitElems) {
// Passive element segment has [f0, f1, f2, f3, f4, null].
function_indexes.push_back(WasmElemSegment::kNullIndex);
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
// Add 10 function tables, even though we only test one table.
for (int i = 0; i < 10; ++i) {
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
}
r.builder().AddPassiveElementSegment(function_indexes);
WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
BUILD(call,
WASM_CALL_INDIRECT_TABLE0(table_index, sig_index, WASM_GET_LOCAL(0)));
const uint32_t call_index = call.function_index();
BUILD(r,
WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_TABLE_INIT(table_index, 0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_GET_LOCAL(2)),
kExprI32Const, 0);
auto table = handle(
WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
isolate);
auto table =
handle(WasmTableObject::cast(
r.builder().instance_object()->tables().get(table_index)),
isolate);
const double null = 0xDEADBEEF;
CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
......@@ -457,7 +462,17 @@ WASM_EXEC_TEST(TableInitElems) {
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 3, 4);
}
WASM_EXEC_TEST(TableInitOob) {
WASM_EXEC_TEST(TableInitElems0) { TestTableInitElems(execution_tier, 0); }
WASM_EXEC_TEST(TableInitElems7) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
TestTableInitElems(execution_tier, 7);
}
WASM_EXEC_TEST(TableInitElems9) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
TestTableInitElems(execution_tier, 9);
}
void TestTableInitOob(ExecutionTier execution_tier, int table_index) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
......@@ -474,21 +489,25 @@ WASM_EXEC_TEST(TableInitOob) {
function_indexes.push_back(fn.function_index());
}
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
for (int i = 0; i < 10; ++i) {
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
}
r.builder().AddPassiveElementSegment(function_indexes);
WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
BUILD(call,
WASM_CALL_INDIRECT_TABLE0(table_index, sig_index, WASM_GET_LOCAL(0)));
const uint32_t call_index = call.function_index();
BUILD(r,
WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_TABLE_INIT(table_index, 0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_GET_LOCAL(2)),
kExprI32Const, 0);
auto table = handle(
WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
isolate);
auto table =
handle(WasmTableObject::cast(
r.builder().instance_object()->tables().get(table_index)),
isolate);
const double null = 0xDEADBEEF;
CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
......@@ -523,6 +542,16 @@ WASM_EXEC_TEST(TableInitOob) {
r.CheckCallViaJS(0xDEADBEEF, 0, 10, 1);
}
WASM_EXEC_TEST(TableInitOob0) { TestTableInitOob(execution_tier, 0); }
WASM_EXEC_TEST(TableInitOob7) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
TestTableInitOob(execution_tier, 7);
}
WASM_EXEC_TEST(TableInitOob9) {
EXPERIMENTAL_FLAG_SCOPE(anyref);
TestTableInitOob(execution_tier, 9);
}
WASM_EXEC_TEST(TableCopyElems) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
......@@ -719,7 +748,7 @@ WASM_EXEC_TEST(ElemDropThenTableInit) {
r.builder().AddIndirectFunctionTable(nullptr, 1);
r.builder().AddPassiveElementSegment({});
BUILD(r, WASM_ELEM_DROP(0),
WASM_TABLE_INIT(0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_I32V_1(0)),
WASM_TABLE_INIT(0, 0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_I32V_1(0)),
kExprI32Const, 0);
r.CheckCallViaJS(0xDEADBEEF);
......
......@@ -173,8 +173,23 @@ void TestingModuleBuilder::AddIndirectFunctionTable(
table.maximum_size = table_size;
table.has_maximum_size = true;
table.type = kWasmFuncRef;
{
// Allocate the indirect function table.
Handle<FixedArray> old_tables =
table_index == 0
? isolate_->factory()->empty_fixed_array()
: handle(instance_object_->indirect_function_tables(), isolate_);
Handle<FixedArray> new_tables =
isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1);
Handle<WasmIndirectFunctionTable> table_obj =
WasmIndirectFunctionTable::New(isolate_, table.initial_size);
new_tables->set(table_index, *table_obj);
instance_object_->set_indirect_function_tables(*new_tables);
}
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance_object(), 0, table_size);
instance_object(), table_index, table_size);
Handle<WasmTableObject> table_obj =
WasmTableObject::New(isolate_, table.type, table.initial_size,
table.has_maximum_size, table.maximum_size, nullptr);
......@@ -194,8 +209,8 @@ void TestingModuleBuilder::AddIndirectFunctionTable(
}
Handle<FixedArray> old_tables(instance_object_->tables(), isolate_);
Handle<FixedArray> new_tables = isolate_->factory()->CopyFixedArrayAndGrow(
old_tables, old_tables->length() + 1);
Handle<FixedArray> new_tables =
isolate_->factory()->CopyFixedArrayAndGrow(old_tables, 1);
new_tables->set(old_tables->length(), *table_obj);
instance_object_->set_tables(*new_tables);
}
......
......@@ -423,6 +423,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
a, b, c, d, e, func, kExprCallIndirect, static_cast<byte>(index), TABLE_ZERO
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index), TABLE_ZERO
#define WASM_CALL_INDIRECT_TABLE0(table, index, func) \
func, kExprCallIndirect, static_cast<byte>(index), static_cast<byte>(table)
#define WASM_RETURN_CALL_INDIRECT0(index, func) \
func, kExprReturnCallIndirect, static_cast<byte>(index), TABLE_ZERO
......@@ -626,8 +628,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
dst, src, size, WASM_NUMERIC_OP(kExprMemoryCopy), MEMORY_ZERO, MEMORY_ZERO
#define WASM_MEMORY_FILL(dst, val, size) \
dst, val, size, WASM_NUMERIC_OP(kExprMemoryFill), MEMORY_ZERO
#define WASM_TABLE_INIT(seg, dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprTableInit), U32V_1(seg), TABLE_ZERO
#define WASM_TABLE_INIT(table, seg, dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprTableInit), U32V_1(seg), \
static_cast<byte>(table)
#define WASM_ELEM_DROP(seg) WASM_NUMERIC_OP(kExprElemDrop), U32V_1(seg)
#define WASM_TABLE_COPY(dst, src, size) \
dst, src, size, WASM_NUMERIC_OP(kExprTableCopy), TABLE_ZERO, TABLE_ZERO
......
......@@ -274,6 +274,11 @@ class TestModuleBuilder {
byte AddPassiveElementSegment() {
mod.elem_segments.emplace_back();
auto& init = mod.elem_segments.back();
// Add 5 empty elements.
for (uint32_t j = 0; j < 5; j++) {
init.entries.push_back(WasmElemSegment::kNullIndex);
}
return static_cast<byte>(mod.elem_segments.size() - 1);
}
......@@ -3104,7 +3109,7 @@ TEST_F(FunctionBodyDecoderTest, MemoryInit) {
ExpectValidates(sigs.v_v(),
{WASM_MEMORY_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
ExpectFailure(sigs.v_v(),
{WASM_TABLE_INIT(1, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
{WASM_TABLE_INIT(0, 1, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, MemoryInitInvalid) {
......@@ -3174,12 +3179,12 @@ TEST_F(FunctionBodyDecoderTest, TableInit) {
module = builder.module();
ExpectFailure(sigs.v_v(),
{WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
{WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
WASM_FEATURE_SCOPE(bulk_memory);
ExpectValidates(sigs.v_v(),
{WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
{WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
ExpectFailure(sigs.v_v(),
{WASM_TABLE_INIT(1, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
{WASM_TABLE_INIT(0, 1, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, TableInitInvalid) {
......@@ -3189,7 +3194,8 @@ TEST_F(FunctionBodyDecoderTest, TableInitInvalid) {
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
byte code[] = {WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO), WASM_END};
byte code[] = {WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO),
WASM_END};
for (size_t i = 0; i <= arraysize(code); ++i) {
Validate(i == arraysize(code), sigs.v_v(), VectorOf(code, i), kOmitEnd);
}
......@@ -3282,7 +3288,7 @@ TEST_F(FunctionBodyDecoderTest, TableFill) {
TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
module = builder.module();
{
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.i_v(), {WASM_TABLE_GROW(0, WASM_REF_NULL, WASM_ONE)});
......@@ -3294,13 +3300,46 @@ TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
WASM_FEATURE_SCOPE(bulk_memory);
builder.AddPassiveElementSegment();
ExpectFailure(sigs.v_v(),
{WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(0)});
{WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
ExpectFailure(sigs.v_v(),
{WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
}
TEST_F(FunctionBodyDecoderTest, TableInitMultiTable) {
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
{
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
module = builder.module();
// We added one table, therefore table.init on table 0 should work.
int table_index = 0;
ExpectValidates(sigs.v_v(), {WASM_TABLE_INIT(table_index, 0, WASM_ZERO,
WASM_ZERO, WASM_ZERO)});
// There is only one table, so table.init on table 1 should fail.
table_index = 1;
ExpectFailure(sigs.v_v(), {WASM_TABLE_INIT(table_index, 0, WASM_ZERO,
WASM_ZERO, WASM_ZERO)});
}
{
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
module = builder.module();
// We added two tables, therefore table.init on table 0 should work.
int table_index = 0;
ExpectValidates(sigs.v_v(), {WASM_TABLE_INIT(table_index, 0, WASM_ZERO,
WASM_ZERO, WASM_ZERO)});
// Also table.init on table 1 should work now.
table_index = 1;
ExpectValidates(sigs.v_v(), {WASM_TABLE_INIT(table_index, 0, WASM_ZERO,
WASM_ZERO, WASM_ZERO)});
}
}
class BranchTableIteratorTest : public TestWithZone {
public:
BranchTableIteratorTest() : TestWithZone() {}
......
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