Commit 644556e6 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Implement table.[get|set] wasm instructions

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: Ica85ba80c29d6d5c5b9163df8cf743c0f1da7f4f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1520715
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60280}
parent 790acd7c
......@@ -1121,6 +1121,8 @@ namespace internal {
TFC(WasmI64AtomicWait, WasmI64AtomicWait) \
TFC(WasmCallJavaScript, CallTrampoline) \
TFC(WasmMemoryGrow, WasmMemoryGrow) \
TFC(WasmTableGet, WasmTableGet) \
TFC(WasmTableSet, WasmTableSet) \
TFC(WasmRecordWrite, RecordWrite) \
TFC(WasmStackGuard, NoContext) \
TFC(WasmStackOverflow, NoContext) \
......@@ -1428,6 +1430,8 @@ namespace internal {
V(WasmI64AtomicWait) \
V(WasmCallJavaScript) \
V(WasmMemoryGrow) \
V(WasmTableGet) \
V(WasmTableSet) \
V(WasmRecordWrite) \
V(WasmStackGuard) \
V(WasmStackOverflow) \
......
......@@ -226,6 +226,58 @@ TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) {
ReturnRaw(Int32Constant(-1));
}
TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) {
TNode<Int32T> entry_index =
UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex));
TNode<Object> instance = LoadInstanceFromFrame();
TNode<Code> centry = LoadCEntryFromInstance(instance);
TNode<Object> context = LoadContextFromInstance(instance);
Label entry_index_out_of_range(this, Label::kDeferred);
TNode<BoolT> entry_index_fits_in_smi =
IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index));
GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range);
TNode<Smi> entry_index_smi = SmiFromInt32(entry_index);
TNode<Smi> table_index_smi =
UncheckedCast<Smi>(Parameter(Descriptor::kTableIndex));
TailCallRuntimeWithCEntry(Runtime::kWasmFunctionTableGet, centry, context,
instance, table_index_smi, entry_index_smi);
BIND(&entry_index_out_of_range);
MessageTemplate message_id =
wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds);
TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context,
SmiConstant(static_cast<int>(message_id)));
}
TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) {
TNode<Int32T> entry_index =
UncheckedCast<Int32T>(Parameter(Descriptor::kEntryIndex));
TNode<Object> instance = LoadInstanceFromFrame();
TNode<Code> centry = LoadCEntryFromInstance(instance);
TNode<Object> context = LoadContextFromInstance(instance);
Label entry_index_out_of_range(this, Label::kDeferred);
TNode<BoolT> entry_index_fits_in_smi =
IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index));
GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range);
TNode<Smi> entry_index_smi = SmiFromInt32(entry_index);
TNode<Smi> table_index_smi =
UncheckedCast<Smi>(Parameter(Descriptor::kTableIndex));
TNode<Object> value = UncheckedCast<Object>(Parameter(Descriptor::kValue));
TailCallRuntimeWithCEntry(Runtime::kWasmFunctionTableSet, centry, context,
instance, table_index_smi, entry_index_smi, value);
BIND(&entry_index_out_of_range);
MessageTemplate message_id =
wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds);
TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, context,
SmiConstant(static_cast<int>(message_id)));
}
TF_BUILTIN(BigIntToWasmI64, WasmBuiltinsAssembler) {
if (!Is64()) {
Unreachable();
......
......@@ -3357,25 +3357,66 @@ void WasmGraphBuilder::GetTableBaseAndOffset(uint32_t table_index, Node* index,
Node* WasmGraphBuilder::GetTable(uint32_t table_index, Node* index,
wasm::WasmCodePosition position) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
return SetEffect(
graph()->NewNode(mcgraph()->machine()->Load(MachineType::AnyTagged()),
base, offset, Effect(), Control()));
if (env_->module->tables[table_index].type == wasm::kWasmAnyRef) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
return SetEffect(
graph()->NewNode(mcgraph()->machine()->Load(MachineType::AnyTagged()),
base, offset, Effect(), Control()));
}
// We access anyfunc tables through runtime calls.
WasmTableGetDescriptor interface_descriptor;
auto call_descriptor = Linkage::GetStubCallDescriptor(
mcgraph()->zone(), // zone
interface_descriptor, // descriptor
interface_descriptor.GetStackParameterCount(), // stack parameter count
CallDescriptor::kNoFlags, // flags
Operator::kNoProperties, // properties
StubCallMode::kCallWasmRuntimeStub); // stub call mode
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched at relocation.
Node* call_target = mcgraph()->RelocatableIntPtrConstant(
wasm::WasmCode::kWasmTableGet, RelocInfo::WASM_STUB_CALL);
return SetEffect(SetControl(graph()->NewNode(
mcgraph()->common()->Call(call_descriptor), call_target,
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)), index,
Effect(), Control())));
}
Node* WasmGraphBuilder::SetTable(uint32_t table_index, Node* index, Node* val,
wasm::WasmCodePosition position) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
if (env_->module->tables[table_index].type == wasm::kWasmAnyRef) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
const Operator* op = mcgraph()->machine()->Store(
StoreRepresentation(MachineRepresentation::kTagged, kFullWriteBarrier));
const Operator* op = mcgraph()->machine()->Store(
StoreRepresentation(MachineRepresentation::kTagged, kFullWriteBarrier));
Node* store = graph()->NewNode(op, base, offset, val, Effect(), Control());
return SetEffect(store);
Node* store = graph()->NewNode(op, base, offset, val, Effect(), Control());
return SetEffect(store);
} else {
// We access anyfunc tables through runtime calls.
WasmTableSetDescriptor interface_descriptor;
auto call_descriptor = Linkage::GetStubCallDescriptor(
mcgraph()->zone(), // zone
interface_descriptor, // descriptor
interface_descriptor.GetStackParameterCount(), // stack parameter count
CallDescriptor::kNoFlags, // flags
Operator::kNoProperties, // properties
StubCallMode::kCallWasmRuntimeStub); // stub call mode
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched at relocation.
Node* call_target = mcgraph()->RelocatableIntPtrConstant(
wasm::WasmCode::kWasmTableSet, RelocInfo::WASM_STUB_CALL);
return SetEffect(SetControl(graph()->NewNode(
mcgraph()->common()->Call(call_descriptor), call_target,
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)),
index, val, Effect(), Control())));
}
}
Node* WasmGraphBuilder::CheckBoundsAndAlignment(
......
......@@ -359,6 +359,16 @@ void WasmMemoryGrowDescriptor::InitializePlatformSpecific(
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void WasmTableGetDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void WasmTableSetDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void WasmThrowDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
......
......@@ -82,6 +82,8 @@ namespace internal {
V(WasmI32AtomicWait) \
V(WasmI64AtomicWait) \
V(WasmMemoryGrow) \
V(WasmTableGet) \
V(WasmTableSet) \
V(WasmThrow) \
BUILTIN_LIST_TFS(V)
......@@ -1155,6 +1157,24 @@ class WasmMemoryGrowDescriptor final : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(WasmMemoryGrowDescriptor, CallInterfaceDescriptor)
};
class WasmTableGetDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_NO_CONTEXT(kTableIndex, kEntryIndex)
DEFINE_RESULT_AND_PARAMETER_TYPES(MachineType::AnyTagged(), // result 1
MachineType::TaggedSigned(), // kTableIndex
MachineType::Int32()) // kEntryIndex
DECLARE_DESCRIPTOR(WasmTableGetDescriptor, CallInterfaceDescriptor)
};
class WasmTableSetDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_NO_CONTEXT(kTableIndex, kEntryIndex, kValue)
DEFINE_PARAMETER_TYPES(MachineType::TaggedSigned(), // kTableIndex
MachineType::Int32(), // kEntryIndex
MachineType::AnyTagged()) // kValue
DECLARE_DESCRIPTOR(WasmTableSetDescriptor, CallInterfaceDescriptor)
};
class WasmThrowDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_NO_CONTEXT(kException)
......
......@@ -54,6 +54,11 @@ class ClearThreadInWasmScope {
}
};
Object ThrowWasmError(Isolate* isolate, MessageTemplate message) {
HandleScope scope(isolate);
Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(message);
return isolate->Throw(*error_obj);
}
} // namespace
RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue) {
......@@ -89,14 +94,10 @@ RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
}
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
ClearThreadInWasmScope clear_wasm_flag;
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(message_id, 0);
ClearThreadInWasmScope clear_wasm_flag;
HandleScope scope(isolate);
Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
MessageTemplateFromInt(message_id));
return isolate->Throw(*error_obj);
return ThrowWasmError(isolate, MessageTemplateFromInt(message_id));
}
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
......@@ -329,6 +330,49 @@ Object ThrowTableOutOfBounds(Isolate* isolate,
}
} // namespace
RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
// This runtime function is always being called from wasm code.
ClearThreadInWasmScope flag_scope;
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
CONVERT_UINT32_ARG_CHECKED(table_index, 1);
CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
DCHECK_LT(table_index, instance->tables()->length());
auto table = handle(
WasmTableObject::cast(instance->tables()->get(table_index)), isolate);
if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
}
return *WasmTableObject::Get(isolate, table, entry_index);
}
RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
// This runtime function is always being called from wasm code.
ClearThreadInWasmScope flag_scope;
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
CONVERT_UINT32_ARG_CHECKED(table_index, 1);
CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
CONVERT_ARG_CHECKED(Object, element_raw, 3);
// TODO(mstarzinger): Manually box because parameters are not visited yet.
Handle<Object> element(element_raw, isolate);
DCHECK_LT(table_index, instance->tables()->length());
auto table = handle(
WasmTableObject::cast(instance->tables()->get(table_index)), isolate);
if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
}
WasmTableObject::Set(isolate, table, entry_index, element);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
......
......@@ -542,6 +542,8 @@ namespace internal {
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
F(WasmTableCopy, 5, 1) \
F(WasmIsValidAnyFuncValue, 1, 1) \
......
......@@ -1576,11 +1576,14 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
int table_count = static_cast<int>(module_->tables.size());
for (int index = 0; index < table_count; ++index) {
auto table_object =
handle(WasmTableObject::cast(instance->tables()->get(index)), isolate_);
if (module_->tables[index].type == kWasmAnyFunc) {
auto table_object = handle(
WasmTableObject::cast(instance->tables()->get(index)), isolate_);
// Add the new dispatch table at the end to avoid redundant lookups.
WasmTableObject::AddDispatchTable(isolate_, table_object, instance, index);
// Add the new dispatch table at the end to avoid redundant lookups.
WasmTableObject::AddDispatchTable(isolate_, table_object, instance,
index);
}
}
}
......
......@@ -885,15 +885,15 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
Handle<FixedArray> elements(table->elements(), isolate);
// The FixedArray is addressed with int's.
int table_index = static_cast<int>(index);
int entry_index = static_cast<int>(index);
if (table->type() == wasm::kWasmAnyRef) {
elements->set(table_index, *element);
elements->set(entry_index, *element);
return;
}
if (element->IsNull(isolate)) {
ClearDispatchTables(isolate, table, table_index); // Degenerate case.
elements->set(table_index, ReadOnlyRoots(isolate).null_value());
ClearDispatchTables(isolate, table, entry_index); // Degenerate case.
elements->set(entry_index, ReadOnlyRoots(isolate).null_value());
return;
}
......@@ -905,10 +905,10 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
auto* wasm_function = &target_instance->module()->functions[func_index];
DCHECK_NOT_NULL(wasm_function);
DCHECK_NOT_NULL(wasm_function->sig);
UpdateDispatchTables(isolate, table, table_index, wasm_function->sig,
UpdateDispatchTables(isolate, table, entry_index, wasm_function->sig,
handle(exported_function->instance(), isolate),
func_index);
elements->set(table_index, *element);
elements->set(entry_index, *element);
}
Handle<Object> WasmTableObject::Get(Isolate* isolate,
......@@ -973,7 +973,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
}
void WasmTableObject::UpdateDispatchTables(
Isolate* isolate, Handle<WasmTableObject> table, int table_index,
Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
wasm::FunctionSig* sig, Handle<WasmInstanceObject> target_instance,
int target_func_index) {
// We simply need to update the IFTs for each instance that imports
......@@ -983,6 +983,13 @@ void WasmTableObject::UpdateDispatchTables(
for (int i = 0; i < dispatch_tables->length();
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset))->value();
if (table_index > 0) {
// Only table 0 has a dispatch table in the instance at the moment.
// TODO(ahaas): Introduce dispatch tables for the other tables as well.
continue;
}
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
......@@ -990,7 +997,7 @@ void WasmTableObject::UpdateDispatchTables(
// Note that {SignatureMap::Find} may return {-1} if the signature is
// not found; it will simply never match any check.
auto sig_id = instance->module()->signature_map.Find(*sig);
IndirectFunctionTableEntry(instance, table_index)
IndirectFunctionTableEntry(instance, entry_index)
.Set(sig_id, target_instance, target_func_index);
}
}
......
......@@ -298,7 +298,7 @@ class WasmTableObject : public JSObject {
static void UpdateDispatchTables(Isolate* isolate,
Handle<WasmTableObject> table,
int table_index, wasm::FunctionSig* sig,
int entry_index, wasm::FunctionSig* sig,
Handle<WasmInstanceObject> target_instance,
int target_func_index);
......
......@@ -6,6 +6,7 @@
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestGetAndSet() {
function addTableWithAccessors(builder, type, size, name) {
const table = builder.addTable(type, size);
const set_sig = makeSig([kWasmI32, type], []);
......@@ -46,9 +47,13 @@ const dummy_func = exports.set_table_func1;
assertSame(null, exports.get_table_func2(11));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_func1(11));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_func2(21));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_func1(-1));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_func2(-2));
assertSame(null, exports.get_table_ref1(14));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_ref2(14));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_ref1(44));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_ref2(-1));
assertTraps(kTrapTableOutOfBounds, () => exports.get_table_ref1(-4));
})();
(function testTableSetOOB() {
......@@ -83,3 +88,42 @@ const dummy_func = exports.set_table_func1;
assertSame(dummy_ref, exports.get_table_ref2(7));
assertSame(null, exports.get_table_ref1(7));
})();
(function testSetFunctionTableInvalidType() {
print(arguments.callee.name);
assertThrows(() => exports.set_table_func2(7, dummy_ref), TypeError);
})();
})();
(function testGetFunctionFromInitializedTable() {
print(arguments.callee.name);
const value1 = 11;
const value2 = 22;
const value3 = 13;
const builder = new WasmModuleBuilder();
const t1 = builder.addTable(kWasmAnyFunc, 10).index;
const t2 = builder.addTable(kWasmAnyFunc, 12).index;
const f1 = builder.addFunction('f', kSig_i_v).addBody([kExprI32Const, value1]);
const f2 = builder.addFunction('f', kSig_i_v).addBody([kExprI32Const, value2]);
const f3 = builder.addFunction('f', kSig_i_v).addBody([kExprI32Const, value3]);
builder.addFunction('get_t1', kSig_a_i)
.addBody([kExprGetLocal, 0, kExprGetTable, t1])
.exportFunc();
builder.addFunction('get_t2', kSig_a_i)
.addBody([kExprGetLocal, 0, kExprGetTable, t2])
.exportFunc();
const offset1 = 3;
const offset2 = 9;
builder.addElementSegment(t1, offset1, false, [f1.index, f2.index], false);
builder.addElementSegment(t2, offset2, false, [f3.index, f1.index], false);
const instance = builder.instantiate();
assertEquals(value1, instance.exports.get_t1(offset1)());
assertEquals(value2, instance.exports.get_t1(offset1 + 1)());
assertEquals(value3, instance.exports.get_t2(offset2)());
assertEquals(value1, instance.exports.get_t2(offset2 + 1)());
})();
......@@ -156,6 +156,7 @@ let kSig_v_a = makeSig([kWasmAnyFunc], []);
let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []);
let kSig_r_v = makeSig([], [kWasmAnyRef]);
let kSig_a_v = makeSig([], [kWasmAnyFunc]);
let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
function makeSig(params, results) {
return {params: params, results: results};
......
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