Commit b3e6013e authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Support importing mutable anyref globals

With this CL we support importing mutable anyref globals. The CL
contains the following changes:

* We store a reference to the buffer of the imported global in
  `WasmInstance::imported_mutable_globals_buffer`. This FixedArray is
  already used to keep the ArrayBuffers of value-typed imported
  mutable globals alive but was not accessed otherwise.

* We store the offset in the buffer of the imported global in
  `WasmInstance::imported_mutable_globals`. This `Address`-array is
  used for value-typed imported mutalbe globals to store direct
  pointers into the backing store of the ArrayBuffer of the imported
  global.

* In wasm-compiler.cc we generate code to load these fields and then
  load or store globals.

* in module-compiler.cc I removed the counter variable
  `next_imported_mutable_global_index`. The variable was only used for
  a DCHECK. I replaced the DCHECK with a slightly weaker DCHECK now.

* Tests.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I11f0f4d8637c98eded5fb2eec44bc8ead8ed5c7b
Reviewed-on: https://chromium-review.googlesource.com/c/1409169
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58822}
parent d6c91518
......@@ -3023,24 +3023,28 @@ Node* WasmGraphBuilder::CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode,
return tnode;
}
Node* WasmGraphBuilder::GetImportedMutableGlobals() {
if (imported_mutable_globals_ == nullptr) {
// Load imported_mutable_globals_ from the instance object at runtime.
imported_mutable_globals_ = graph()->NewNode(
mcgraph()->machine()->Load(MachineType::UintPtr()),
instance_node_.get(),
mcgraph()->Int32Constant(
WASM_INSTANCE_OBJECT_OFFSET(ImportedMutableGlobals)),
graph()->start(), graph()->start());
}
return imported_mutable_globals_.get();
}
void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
const wasm::WasmGlobal& global,
Node** base_node,
Node** offset_node) {
DCHECK_NOT_NULL(instance_node_);
if (global.mutability && global.imported) {
if (imported_mutable_globals_ == nullptr) {
// Load imported_mutable_globals_ from the instance object at runtime.
imported_mutable_globals_ = graph()->NewNode(
mcgraph()->machine()->Load(MachineType::UintPtr()),
instance_node_.get(),
mcgraph()->Int32Constant(
WASM_INSTANCE_OBJECT_OFFSET(ImportedMutableGlobals)),
graph()->start(), graph()->start());
}
*base_node = SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(MachineType::UintPtr()),
imported_mutable_globals_.get(),
GetImportedMutableGlobals(),
mcgraph()->Int32Constant(global.index * sizeof(Address)), Effect(),
Control()));
*offset_node = mcgraph()->Int32Constant(0);
......@@ -3073,6 +3077,34 @@ void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
}
}
void WasmGraphBuilder::GetBaseAndOffsetForImportedMutableAnyRefGlobal(
const wasm::WasmGlobal& global, Node** base, Node** offset) {
// Load the base from the ImportedMutableGlobalsBuffer of the instance.
Node* buffers = LOAD_INSTANCE_FIELD(ImportedMutableGlobalsBuffers,
MachineType::TaggedPointer());
*base = LOAD_FIXED_ARRAY_SLOT_ANY(buffers, global.index);
// For the offset we need the index of the global in the buffer, and then
// calculate the actual offset from the index. Load the index from the
// ImportedMutableGlobals array of the instance.
Node* index = SetEffect(
graph()->NewNode(mcgraph()->machine()->Load(MachineType::UintPtr()),
GetImportedMutableGlobals(),
mcgraph()->Int32Constant(global.index * sizeof(Address)),
Effect(), Control()));
// From the index, calculate the actual offset in the FixeArray. This
// is kHeaderSize + (index * kTaggedSize). kHeaderSize can be acquired with
// wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0).
Node* index_times_tagged_size =
graph()->NewNode(mcgraph()->machine()->IntMul(), Uint32ToUintptr(index),
mcgraph()->Int32Constant(kTaggedSize));
*offset = graph()->NewNode(
mcgraph()->machine()->IntAdd(), index_times_tagged_size,
mcgraph()->IntPtrConstant(
wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0)));
}
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
DCHECK_NOT_NULL(instance_cache_);
Node* mem_start = instance_cache_->mem_start;
......@@ -3151,11 +3183,19 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f,
}
Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
if (env_->module->globals[index].type == wasm::ValueType::kWasmAnyRef) {
Node* globals =
const wasm::WasmGlobal& global = env_->module->globals[index];
if (global.type == wasm::ValueType::kWasmAnyRef) {
if (global.mutability && global.imported) {
Node* base = nullptr;
Node* offset = nullptr;
GetBaseAndOffsetForImportedMutableAnyRefGlobal(global, &base, &offset);
return SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(MachineType::TaggedPointer()), base,
offset, Effect(), Control()));
}
Node* globals_buffer =
LOAD_INSTANCE_FIELD(TaggedGlobalsBuffer, MachineType::TaggedPointer());
return LOAD_FIXED_ARRAY_SLOT_ANY(globals,
env_->module->globals[index].offset);
return LOAD_FIXED_ARRAY_SLOT_ANY(globals_buffer, global.offset);
}
MachineType mem_type =
......@@ -3174,10 +3214,21 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
}
Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
if (env_->module->globals[index].type == wasm::ValueType::kWasmAnyRef) {
Node* globals =
const wasm::WasmGlobal& global = env_->module->globals[index];
if (global.type == wasm::ValueType::kWasmAnyRef) {
if (global.mutability && global.imported) {
Node* base = nullptr;
Node* offset = nullptr;
GetBaseAndOffsetForImportedMutableAnyRefGlobal(global, &base, &offset);
return SetEffect(graph()->NewNode(
mcgraph()->machine()->Store(StoreRepresentation(
MachineRepresentation::kTagged, kFullWriteBarrier)),
base, offset, val, Effect(), Control()));
}
Node* globals_buffer =
LOAD_INSTANCE_FIELD(TaggedGlobalsBuffer, MachineType::TaggedPointer());
return STORE_FIXED_ARRAY_SLOT_ANY(globals,
return STORE_FIXED_ARRAY_SLOT_ANY(globals_buffer,
env_->module->globals[index].offset, val);
}
......
......@@ -312,9 +312,14 @@ class WasmGraphBuilder {
void set_effect_ptr(Node** effect) { this->effect_ = effect; }
Node* GetImportedMutableGlobals();
void GetGlobalBaseAndOffset(MachineType mem_type, const wasm::WasmGlobal&,
Node** base_node, Node** offset_node);
void GetBaseAndOffsetForImportedMutableAnyRefGlobal(
const wasm::WasmGlobal& global, Node** base, Node** offset);
// Utilities to manipulate sets of instance cache nodes.
void InitInstanceCache(WasmInstanceCacheNodes* instance_cache);
void PrepareInstanceCacheForLoop(WasmInstanceCacheNodes* instance_cache,
......
......@@ -150,14 +150,12 @@ class InstanceBuilder {
// Process a single imported global.
bool ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
int import_index, int global_index,
int* next_imported_mutable_global_index,
Handle<String> module_name,
Handle<String> import_name, Handle<Object> value);
// Process a single imported WasmGlobalObject.
bool ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,
int import_index,
int* next_imported_mutable_global_index,
Handle<String> module_name,
Handle<String> import_name,
const WasmGlobal& global,
......@@ -938,9 +936,8 @@ bool InstanceBuilder::ProcessImportedMemory(Handle<WasmInstanceObject> instance,
bool InstanceBuilder::ProcessImportedWasmGlobalObject(
Handle<WasmInstanceObject> instance, int import_index,
int* next_imported_mutable_global_index, Handle<String> module_name,
Handle<String> import_name, const WasmGlobal& global,
Handle<WasmGlobalObject> global_object) {
Handle<String> module_name, Handle<String> import_name,
const WasmGlobal& global, Handle<WasmGlobalObject> global_object) {
if (global_object->type() != global.type) {
ReportLinkError("imported global does not match the expected type",
import_index, module_name, import_name);
......@@ -952,14 +949,26 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject(
return false;
}
if (global.mutability) {
Handle<JSArrayBuffer> buffer(global_object->untagged_buffer(), isolate_);
int index = (*next_imported_mutable_global_index)++;
instance->imported_mutable_globals_buffers()->set(index, *buffer);
// It is safe in this case to store the raw pointer to the buffer
// since the backing store of the JSArrayBuffer will not be
// relocated.
instance->imported_mutable_globals()[index] = reinterpret_cast<Address>(
raw_buffer_ptr(buffer, global_object->offset()));
DCHECK_LT(global.index, module_->num_imported_mutable_globals);
Handle<Object> buffer;
Address address_or_offset;
if (global.type == kWasmAnyRef) {
static_assert(sizeof(global_object->offset()) <= sizeof(Address),
"The offset into the globals buffer does not fit into "
"the imported_mutable_globals array");
buffer = handle(global_object->tagged_buffer(), isolate_);
// For anyref globals we use a relative offset, not an absolute address.
address_or_offset = static_cast<Address>(global_object->offset());
} else {
buffer = handle(global_object->untagged_buffer(), isolate_);
// It is safe in this case to store the raw pointer to the buffer
// since the backing store of the JSArrayBuffer will not be
// relocated.
address_or_offset = reinterpret_cast<Address>(raw_buffer_ptr(
Handle<JSArrayBuffer>::cast(buffer), global_object->offset()));
}
instance->imported_mutable_globals_buffers()->set(global.index, *buffer);
instance->imported_mutable_globals()[global.index] = address_or_offset;
return true;
}
......@@ -967,10 +976,11 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject(
return true;
}
bool InstanceBuilder::ProcessImportedGlobal(
Handle<WasmInstanceObject> instance, int import_index, int global_index,
int* next_imported_mutable_global_index, Handle<String> module_name,
Handle<String> import_name, Handle<Object> value) {
bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
int import_index, int global_index,
Handle<String> module_name,
Handle<String> import_name,
Handle<Object> value) {
// Immutable global imports are converted to numbers and written into
// the {untagged_globals_} array buffer.
//
......@@ -1009,9 +1019,8 @@ bool InstanceBuilder::ProcessImportedGlobal(
if (value->IsWasmGlobalObject()) {
auto global_object = Handle<WasmGlobalObject>::cast(value);
return ProcessImportedWasmGlobalObject(
instance, import_index, next_imported_mutable_global_index, module_name,
import_name, global, global_object);
return ProcessImportedWasmGlobalObject(instance, import_index, module_name,
import_name, global, global_object);
}
if (global.mutability) {
......@@ -1052,7 +1061,6 @@ bool InstanceBuilder::ProcessImportedGlobal(
int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
int num_imported_functions = 0;
int num_imported_tables = 0;
int num_imported_mutable_globals = 0;
DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
int num_imports = static_cast<int>(module_->import_table.size());
......@@ -1092,8 +1100,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
break;
}
case kExternalGlobal: {
if (!ProcessImportedGlobal(instance, index, import.index,
&num_imported_mutable_globals, module_name,
if (!ProcessImportedGlobal(instance, index, import.index, module_name,
import_name, value)) {
return -1;
}
......@@ -1124,10 +1131,6 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
break;
}
}
DCHECK_EQ(module_->num_imported_mutable_globals,
num_imported_mutable_globals);
return num_imported_functions;
}
......
......@@ -171,7 +171,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
TestGlobal(print);
})();
(function TestExportImmutableAnyRefGlobal() {
(function TestExportMutableAnyRefGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g1 = builder.addGlobal(kWasmAnyRef, true).exportAs("global1");
......@@ -194,3 +194,96 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertSame(obj1, instance.exports.global1.value);
assertSame(obj2, instance.exports.global2.value);
})();
(function TestImportMutableAnyRefGlobal() {
print(arguments.callee.name);
function Test(obj) {
let builder = new WasmModuleBuilder();
const g = builder.addImportedGlobal('m', 'val', kWasmAnyRef, true);
builder.addFunction('main', kSig_r_v)
.addBody([kExprGetGlobal, g])
.exportAs('main');
const global = new WebAssembly.Global({value: 'anyref', mutable: 'true'}, obj);
const instance = builder.instantiate({m: {val: global}});
assertSame(obj, instance.exports.main());
}
Test(null);
Test(undefined);
Test(1653);
Test("mystring");
Test({q: 14});
Test(print);
})();
(function TestImportMutableAnyRefGlobalFromOtherInstance() {
print(arguments.callee.name);
// Create an instance which exports globals.
let builder1 = new WasmModuleBuilder();
const g3 = builder1.addGlobal(kWasmAnyRef, true).exportAs("e3");
builder1.addGlobal(kWasmI32, true).exportAs("e1"); // Dummy.
builder1.addGlobal(kWasmAnyRef, true).exportAs("e4"); // Dummy.
const g2 = builder1.addGlobal(kWasmAnyRef, true).exportAs("e2");
builder1.addFunction("set_globals", kSig_v_rr)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, g2.index,
kExprGetLocal, 1,
kExprSetGlobal, g3.index,
])
.exportAs("set_globals");
builder1.addFunction('get_global2', kSig_r_v)
.addBody([kExprGetGlobal, g2.index])
.exportAs('get_global2');
builder1.addFunction('get_global3', kSig_r_v)
.addBody([kExprGetGlobal, g3.index])
.exportAs('get_global3');
const instance1 = builder1.instantiate();
const obj2 = {x: 221};
const obj3 = print;
instance1.exports.set_globals(obj2, obj3);
// Create an instance which imports the globals of the other instance.
let builder2 = new WasmModuleBuilder();
const i1 = builder2.addImportedGlobal('exports', 'e1', kWasmI32, true);
const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmAnyRef, true);
const i3 = builder2.addImportedGlobal('exports', 'e3', kWasmAnyRef, true);
const i4 = builder2.addImportedGlobal('exports', 'e4', kWasmAnyRef, true);
builder2.addFunction("set_globals", kSig_v_rr)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, i2,
kExprGetLocal, 1,
kExprSetGlobal, i3,
])
.exportAs("set_globals");
builder2.addFunction('get_global2', kSig_r_v)
.addBody([kExprGetGlobal, i2])
.exportAs('get_global2');
builder2.addFunction('get_global3', kSig_r_v)
.addBody([kExprGetGlobal, i3])
.exportAs('get_global3');
const instance2 = builder2.instantiate(instance1);
// Check if the globals were imported correctly.
assertSame(obj2, instance2.exports.get_global2());
assertSame(obj3, instance2.exports.get_global3());
// Check if instance2 can make changes visible for instance1.
instance2.exports.set_globals(null, undefined);
assertEquals(null, instance1.exports.get_global2());
assertEquals(undefined, instance1.exports.get_global3());
// Check if instance1 can make changes visible for instance2.
instance1.exports.set_globals("foo", 66343);
assertEquals("foo", instance2.exports.get_global2());
assertEquals(66343, instance2.exports.get_global3());
})();
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