Commit 9166affb authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Implement importing mutable globals

The WasmInstanceObject stores two new arrays:

- imported_mutable_globals_buffers_: a FixedArray of all the imported
  globals' array buffers.
- imported_mutable_globals: a calloc'd array of Addresses pointing to
  the mutable global in its array buffer.

When accessing the global, the generated code looks up the address in
imported_mutable_globals to find where to load/store.

Bug: v8:7625
Change-Id: I60844c21a788fce28f346455f10f2283d1c152e9
Reviewed-on: https://chromium-review.googlesource.com/1020602
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52794}
parent e81b0db7
......@@ -3515,35 +3515,56 @@ Node* WasmGraphBuilder::CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode,
}
void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
uint32_t offset, Node** base_node,
const wasm::WasmGlobal& global,
Node** base_node,
Node** offset_node) {
DCHECK_NOT_NULL(instance_node_);
if (globals_start_ == nullptr) {
// Load globals_start from the instance object at runtime.
// TODO(wasm): we currently generate only one load of the {globals_start}
// start per graph, which means it can be placed anywhere by the scheduler.
// This is legal because the globals_start should never change.
// However, in some cases (e.g. if the instance object is already in a
// register), it is slightly more efficient to reload this value from the
// instance object. Since this depends on register allocation, it is not
// possible to express in the graph, and would essentially constitute a
// "mem2reg" optimization in TurboFan.
globals_start_ = graph()->NewNode(
if (global.mutability && global.imported) {
DCHECK(FLAG_experimental_wasm_mut_global);
if (imported_mutable_globals_ == nullptr) {
// Load imported_mutable_globals_ from the instance object at runtime.
imported_mutable_globals_ = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::UintPtr()),
instance_node_.get(),
jsgraph()->Int32Constant(
WASM_INSTANCE_OBJECT_OFFSET(ImportedMutableGlobals)),
graph()->start(), graph()->start());
}
*base_node = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::UintPtr()),
instance_node_.get(),
jsgraph()->Int32Constant(WASM_INSTANCE_OBJECT_OFFSET(GlobalsStart)),
graph()->start(), graph()->start());
}
*base_node = globals_start_.get();
*offset_node = jsgraph()->Int32Constant(offset);
if (mem_type == MachineType::Simd128() && offset != 0) {
// TODO(titzer,bbudge): code generation for SIMD memory offsets is broken.
*base_node =
graph()->NewNode(kPointerSize == 4 ? jsgraph()->machine()->Int32Add()
: jsgraph()->machine()->Int64Add(),
*base_node, *offset_node);
imported_mutable_globals_.get(),
jsgraph()->Int32Constant(global.index * sizeof(Address)), *effect_,
*control_);
*offset_node = jsgraph()->Int32Constant(0);
*effect_ = *base_node;
} else {
if (globals_start_ == nullptr) {
// Load globals_start from the instance object at runtime.
// TODO(wasm): we currently generate only one load of the {globals_start}
// start per graph, which means it can be placed anywhere by the
// scheduler. This is legal because the globals_start should never change.
// However, in some cases (e.g. if the instance object is already in a
// register), it is slightly more efficient to reload this value from the
// instance object. Since this depends on register allocation, it is not
// possible to express in the graph, and would essentially constitute a
// "mem2reg" optimization in TurboFan.
globals_start_ = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::UintPtr()),
instance_node_.get(),
jsgraph()->Int32Constant(WASM_INSTANCE_OBJECT_OFFSET(GlobalsStart)),
graph()->start(), graph()->start());
}
*base_node = globals_start_.get();
*offset_node = jsgraph()->Int32Constant(global.offset);
if (mem_type == MachineType::Simd128() && global.offset != 0) {
// TODO(titzer,bbudge): code generation for SIMD memory offsets is broken.
*base_node =
graph()->NewNode(kPointerSize == 4 ? jsgraph()->machine()->Int32Add()
: jsgraph()->machine()->Int64Add(),
*base_node, *offset_node);
*offset_node = jsgraph()->Int32Constant(0);
}
}
}
......@@ -3637,7 +3658,7 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
wasm::ValueTypes::MachineTypeFor(env_->module->globals[index].type);
Node* base = nullptr;
Node* offset = nullptr;
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index].offset, &base,
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index], &base,
&offset);
Node* node = graph()->NewNode(jsgraph()->machine()->Load(mem_type), base,
offset, *effect_, *control_);
......@@ -3650,7 +3671,7 @@ Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
wasm::ValueTypes::MachineTypeFor(env_->module->globals[index].type);
Node* base = nullptr;
Node* offset = nullptr;
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index].offset, &base,
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index], &base,
&offset);
const Operator* op = jsgraph()->machine()->Store(
StoreRepresentation(mem_type.representation(), kNoWriteBarrier));
......
......@@ -284,7 +284,7 @@ class WasmGraphBuilder {
void set_effect_ptr(Node** effect) { this->effect_ = effect; }
void GetGlobalBaseAndOffset(MachineType mem_type, uint32_t offset,
void GetGlobalBaseAndOffset(MachineType mem_type, const wasm::WasmGlobal&,
Node** base_node, Node** offset_node);
// Utilities to manipulate sets of instance cache nodes.
......@@ -354,6 +354,7 @@ class WasmGraphBuilder {
Node** effect_ = nullptr;
WasmInstanceCacheNodes* instance_cache_ = nullptr;
SetOncePointer<Node> globals_start_;
SetOncePointer<Node> imported_mutable_globals_;
Node** cur_buffer_;
size_t cur_bufsize_;
Node* def_buffer_[kDefaultBufferSize];
......
......@@ -1149,18 +1149,35 @@ class LiftoffCompiler {
SetLocal(operand.index, true);
}
LiftoffRegister GetGlobalBaseAndOffset(const WasmGlobal* global,
LiftoffRegList& pinned,
uint32_t* offset) {
LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg));
if (global->mutability && global->imported) {
DCHECK(FLAG_experimental_wasm_mut_global);
LOAD_INSTANCE_FIELD(addr, ImportedMutableGlobals, kPointerLoadType);
__ Load(addr, addr.gp(), no_reg, global->index * sizeof(Address),
kPointerLoadType, pinned);
*offset = 0;
} else {
LOAD_INSTANCE_FIELD(addr, GlobalsStart, kPointerLoadType);
*offset = global->offset;
}
return addr;
}
void GetGlobal(Decoder* decoder, Value* result,
const GlobalIndexOperand<validate>& operand) {
const auto* global = &env_->module->globals[operand.index];
if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global"))
return;
LiftoffRegList pinned;
LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg));
LOAD_INSTANCE_FIELD(addr, GlobalsStart, kPointerLoadType);
uint32_t offset = 0;
LiftoffRegister addr = GetGlobalBaseAndOffset(global, pinned, &offset);
LiftoffRegister value =
pinned.set(__ GetUnusedRegister(reg_class_for(global->type), pinned));
LoadType type = LoadType::ForValueType(global->type);
__ Load(value, addr.gp(), no_reg, global->offset, type, pinned);
__ Load(value, addr.gp(), no_reg, offset, type, pinned);
__ PushRegister(global->type, value);
}
......@@ -1170,11 +1187,11 @@ class LiftoffCompiler {
if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global"))
return;
LiftoffRegList pinned;
LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg));
LOAD_INSTANCE_FIELD(addr, GlobalsStart, kPointerLoadType);
uint32_t offset = 0;
LiftoffRegister addr = GetGlobalBaseAndOffset(global, pinned, &offset);
LiftoffRegister reg = pinned.set(__ PopToRegister(pinned));
StoreType type = StoreType::ForValueType(global->type);
__ Store(addr.gp(), no_reg, global->offset, reg, type, pinned);
__ Store(addr.gp(), no_reg, offset, reg, type, pinned);
}
void Unreachable(Decoder* decoder) { unsupported(decoder, "unreachable"); }
......
......@@ -1700,6 +1700,19 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
instance->set_globals_buffer(*globals_);
}
//--------------------------------------------------------------------------
// Set up the array of references to imported globals' array buffers.
//--------------------------------------------------------------------------
if (module_->num_imported_mutable_globals > 0) {
DCHECK(FLAG_experimental_wasm_mut_global);
// TODO(binji): This allocates one slot for each mutable global, which is
// more than required if multiple globals are imported from the same
// module.
Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
module_->num_imported_mutable_globals, TENURED);
instance->set_imported_mutable_globals_buffers(*buffers_array);
}
//--------------------------------------------------------------------------
// Reserve the metadata for indirect function tables.
//--------------------------------------------------------------------------
......@@ -2116,6 +2129,7 @@ void InstanceBuilder::SanitizeImports() {
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());
for (int index = 0; index < static_cast<int>(module_->import_table.size());
......@@ -2301,8 +2315,12 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
break;
}
case kExternalGlobal: {
// Global imports are converted to numbers and written into the
// {globals_} array buffer.
// Immutable global imports are converted to numbers and written into
// the {globals_} array buffer.
//
// Mutable global imports instead have their backing array buffers
// referenced by this instance, and store the address of the imported
// global in the {imported_mutable_globals_} array.
WasmGlobal& global = module_->globals[import.index];
// The mutable-global proposal allows importing i64 values, but only if
......@@ -2329,20 +2347,50 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
}
}
if (FLAG_experimental_wasm_mut_global && value->IsWasmGlobalObject()) {
auto global_object = Handle<WasmGlobalObject>::cast(value);
if (global_object->type() != global.type) {
ReportLinkError("imported global does not match the expected type",
index, module_name, import_name);
if (FLAG_experimental_wasm_mut_global) {
if (value->IsWasmGlobalObject()) {
auto global_object = Handle<WasmGlobalObject>::cast(value);
if (global_object->type() != global.type) {
ReportLinkError(
"imported global does not match the expected type", index,
module_name, import_name);
return -1;
}
if (global.mutability) {
Handle<JSArrayBuffer> buffer(global_object->array_buffer(),
isolate_);
int index = num_imported_mutable_globals++;
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()));
} else {
WriteGlobalValue(global, global_object);
}
} else if (value->IsNumber()) {
if (global.mutability) {
ReportLinkError(
"imported mutable global must be a WebAssembly.Global object",
index, module_name, import_name);
}
WriteGlobalValue(global, value->Number());
} else {
ReportLinkError(
"global import must be a number or WebAssembly.Global object",
index, module_name, import_name);
return -1;
}
WriteGlobalValue(global, global_object);
} else if (value->IsNumber()) {
WriteGlobalValue(global, value->Number());
} else {
ReportLinkError("global import must be a number", index, module_name,
import_name);
return -1;
if (value->IsNumber()) {
WriteGlobalValue(global, value->Number());
} else {
ReportLinkError("global import must be a number", index,
module_name, import_name);
return -1;
}
}
break;
}
......@@ -2352,6 +2400,9 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
}
DCHECK_EQ(module_->num_imported_mutable_globals,
num_imported_mutable_globals);
return num_imported_functions;
}
......@@ -2363,6 +2414,11 @@ T* InstanceBuilder::GetRawGlobalPtr(WasmGlobal& global) {
// Process initialization of globals.
void InstanceBuilder::InitGlobals() {
for (auto global : module_->globals) {
if (global.mutability && global.imported) {
DCHECK(FLAG_experimental_wasm_mut_global);
continue;
}
switch (global.init.kind) {
case WasmInitExpr::kI32Const:
*GetRawGlobalPtr<int32_t>(global) = global.init.val.i32_const;
......
......@@ -499,11 +499,11 @@ class ModuleDecoderImpl : public Decoder {
// ===== Imported global =========================================
import->index = static_cast<uint32_t>(module_->globals.size());
module_->globals.push_back(
{kWasmStmt, false, WasmInitExpr(), 0, true, false});
{kWasmStmt, false, WasmInitExpr(), {0}, true, false});
WasmGlobal* global = &module_->globals.back();
global->type = consume_value_type();
global->mutability = consume_mutability();
if (global->mutability) {
if (!FLAG_experimental_wasm_mut_global && global->mutability) {
error("mutable globals cannot be imported");
}
break;
......@@ -571,7 +571,7 @@ class ModuleDecoderImpl : public Decoder {
TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
// Add an uninitialized global and pass a pointer to it.
module_->globals.push_back(
{kWasmStmt, false, WasmInitExpr(), 0, false, false});
{kWasmStmt, false, WasmInitExpr(), {0}, false, false});
WasmGlobal* global = &module_->globals.back();
DecodeGlobalInModule(module_.get(), i + imported_globals, global);
}
......@@ -988,13 +988,19 @@ class ModuleDecoderImpl : public Decoder {
uint32_t offset = 0;
if (module->globals.size() == 0) {
module->globals_size = 0;
module->num_imported_mutable_globals = 0;
return;
}
for (WasmGlobal& global : module->globals) {
byte size = ValueTypes::MemSize(ValueTypes::MachineTypeFor(global.type));
offset = (offset + size - 1) & ~(size - 1); // align
global.offset = offset;
offset += size;
if (global.mutability && global.imported) {
DCHECK(FLAG_experimental_wasm_mut_global);
global.index = module->num_imported_mutable_globals++;
} else {
offset = (offset + size - 1) & ~(size - 1); // align
global.offset = offset;
offset += size;
}
}
module->globals_size = offset;
}
......
......@@ -1595,6 +1595,16 @@ class ThreadImpl {
return true;
}
byte* GetGlobalPtr(const WasmGlobal* global) {
if (global->mutability && global->imported) {
DCHECK(FLAG_experimental_wasm_mut_global);
return reinterpret_cast<byte*>(
instance_object_->imported_mutable_globals()[global->index]);
} else {
return instance_object_->globals_start() + global->offset;
}
}
// Check if our control stack (frames_) exceeds the limit. Trigger stack
// overflow if it does, and unwinding the current frame.
// Returns true if execution can continue, false if the current activation was
......@@ -1898,7 +1908,7 @@ class ThreadImpl {
GlobalIndexOperand<Decoder::kNoValidate> operand(&decoder,
code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index];
byte* ptr = instance_object_->globals_start() + global->offset;
byte* ptr = GetGlobalPtr(global);
WasmValue val;
switch (global->type) {
#define CASE_TYPE(wasm, ctype) \
......@@ -1918,7 +1928,7 @@ class ThreadImpl {
GlobalIndexOperand<Decoder::kNoValidate> operand(&decoder,
code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index];
byte* ptr = instance_object_->globals_start() + global->offset;
byte* ptr = GetGlobalPtr(global);
WasmValue val = Pop();
switch (global->type) {
#define CASE_TYPE(wasm, ctype) \
......
......@@ -50,12 +50,15 @@ struct WasmFunction {
// Static representation of a wasm global variable.
struct WasmGlobal {
ValueType type; // type of the global.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
uint32_t offset; // offset into global memory.
bool imported; // true if imported.
bool exported; // true if exported.
ValueType type; // type of the global.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
union {
uint32_t index; // index of imported mutable global.
uint32_t offset; // offset into global memory (if not imported & mutable).
};
bool imported; // true if imported.
bool exported; // true if exported.
};
// Note: An exception signature only uses the params portion of a
......@@ -141,7 +144,11 @@ struct V8_EXPORT_PRIVATE WasmModule {
int start_function_index = -1; // start function, >= 0 if any
std::vector<WasmGlobal> globals;
// Size of the buffer required for all globals that are not imported and
// mutable.
// TODO(wasm): Rename for clarity?
uint32_t globals_size = 0;
uint32_t num_imported_mutable_globals = 0;
uint32_t num_imported_functions = 0;
uint32_t num_declared_functions = 0;
uint32_t num_exported_functions = 0;
......
......@@ -116,6 +116,8 @@ PRIMITIVE_ACCESSORS(WasmInstanceObject, imported_function_targets, Address*,
kImportedFunctionTargetsOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, globals_start, byte*,
kGlobalsStartOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, imported_mutable_globals, Address*,
kImportedMutableGlobalsOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, indirect_function_table_size, uint32_t,
kIndirectFunctionTableSizeOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, indirect_function_table_sig_ids,
......@@ -130,6 +132,8 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, memory_object, WasmMemoryObject,
kMemoryObjectOffset)
ACCESSORS(WasmInstanceObject, globals_buffer, JSArrayBuffer,
kGlobalsBufferOffset)
ACCESSORS(WasmInstanceObject, imported_mutable_globals_buffers, FixedArray,
kImportedMutableGlobalsBuffersOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo,
kDebugInfoOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, table_object, WasmTableObject,
......
......@@ -61,10 +61,14 @@ class WasmInstanceNativeAllocations {
// Allocates initial native storage for a given instance.
WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,
size_t num_imported_functions) {
size_t num_imported_functions,
size_t num_imported_mutable_globals) {
SET(instance, imported_function_targets,
reinterpret_cast<Address*>(
calloc(num_imported_functions, sizeof(Address))));
SET(instance, imported_mutable_globals,
reinterpret_cast<Address*>(
calloc(num_imported_mutable_globals, sizeof(Address))));
}
~WasmInstanceNativeAllocations() { free(); }
// Frees natively-allocated storage.
......@@ -72,9 +76,11 @@ class WasmInstanceNativeAllocations {
::free(indirect_function_table_sig_ids_);
::free(indirect_function_table_targets_);
::free(imported_function_targets_);
::free(imported_mutable_globals_);
indirect_function_table_sig_ids_ = nullptr;
indirect_function_table_targets_ = nullptr;
imported_function_targets_ = nullptr;
imported_mutable_globals_ = nullptr;
}
// Resizes the indirect function table.
void resize_indirect_function_table(Isolate* isolate,
......@@ -117,6 +123,7 @@ class WasmInstanceNativeAllocations {
uint32_t* indirect_function_table_sig_ids_ = nullptr;
Address* indirect_function_table_targets_ = nullptr;
Address* imported_function_targets_ = nullptr;
Address* imported_mutable_globals_ = nullptr;
#undef SET
};
......@@ -750,8 +757,10 @@ Handle<WasmInstanceObject> WasmInstanceObject::New(
// Initialize the imported function arrays.
auto num_imported_functions =
compiled_module->shared()->module()->num_imported_functions;
auto num_imported_mutable_globals =
compiled_module->shared()->module()->num_imported_mutable_globals;
auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate(
isolate, instance, num_imported_functions);
isolate, instance, num_imported_functions, num_imported_mutable_globals);
instance->set_managed_native_allocations(*native_allocations);
Handle<FixedArray> imported_function_instances =
......
......@@ -265,6 +265,7 @@ class WasmInstanceObject : public JSObject {
DECL_ACCESSORS(exports_object, JSObject)
DECL_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject)
DECL_OPTIONAL_ACCESSORS(globals_buffer, JSArrayBuffer)
DECL_OPTIONAL_ACCESSORS(imported_mutable_globals_buffers, FixedArray)
DECL_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo)
DECL_OPTIONAL_ACCESSORS(table_object, WasmTableObject)
DECL_ACCESSORS(imported_function_instances, FixedArray)
......@@ -277,6 +278,7 @@ class WasmInstanceObject : public JSObject {
DECL_PRIMITIVE_ACCESSORS(memory_mask, uint32_t)
DECL_PRIMITIVE_ACCESSORS(imported_function_targets, Address*)
DECL_PRIMITIVE_ACCESSORS(globals_start, byte*)
DECL_PRIMITIVE_ACCESSORS(imported_mutable_globals, Address*)
DECL_PRIMITIVE_ACCESSORS(indirect_function_table_size, uint32_t)
DECL_PRIMITIVE_ACCESSORS(indirect_function_table_sig_ids, uint32_t*)
DECL_PRIMITIVE_ACCESSORS(indirect_function_table_targets, Address*)
......@@ -287,6 +289,7 @@ class WasmInstanceObject : public JSObject {
V(kExportsObjectOffset, kPointerSize) \
V(kMemoryObjectOffset, kPointerSize) \
V(kGlobalsBufferOffset, kPointerSize) \
V(kImportedMutableGlobalsBuffersOffset, kPointerSize) \
V(kDebugInfoOffset, kPointerSize) \
V(kTableObjectOffset, kPointerSize) \
V(kFunctionTablesOffset, kPointerSize) \
......@@ -301,6 +304,7 @@ class WasmInstanceObject : public JSObject {
V(kMemoryMaskOffset, kUInt32Size) /* untagged */ \
V(kImportedFunctionTargetsOffset, kPointerSize) /* untagged */ \
V(kGlobalsStartOffset, kPointerSize) /* untagged */ \
V(kImportedMutableGlobalsOffset, kPointerSize) /* untagged */ \
V(kIndirectFunctionTableSigIdsOffset, kPointerSize) /* untagged */ \
V(kIndirectFunctionTableTargetsOffset, kPointerSize) /* untagged */ \
V(kIndirectFunctionTableSizeOffset, kUInt32Size) /* untagged */ \
......
......@@ -204,7 +204,7 @@ const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) {
byte size = ValueTypes::MemSize(ValueTypes::MachineTypeFor(type));
global_offset = (global_offset + size - 1) & ~(size - 1); // align
test_module_.globals.push_back(
{type, true, WasmInitExpr(), global_offset, false, false});
{type, true, WasmInitExpr(), {global_offset}, false, false});
global_offset += size;
// limit number of globals.
CHECK_LT(global_offset, kMaxGlobalsSize);
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-mut-global
// Flags: --experimental-wasm-mut-global --expose-gc
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
......@@ -35,3 +35,142 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
builder.addImportedGlobal("mod", "g", kWasmI64);
assertThrows(() => builder.instantiate({mod: {g: 1234}}));
})();
function addGlobalGetterAndSetter(builder, index, name, type) {
builder.addFunction('get' + name, makeSig([], [type]))
.addBody([kExprGetGlobal, index])
.exportFunc();
builder.addFunction('set' + name, makeSig([type], []))
.addBody([kExprGetLocal, 0, kExprSetGlobal, index])
.exportFunc();
}
(function TestImportMutableGlobal() {
const globalDesc = [
{name: 'i32', type: kWasmI32},
{name: 'f32', type: kWasmF32},
{name: 'f64', type: kWasmF64},
];
let globals = {};
let builder = new WasmModuleBuilder();
for (let [index, {name, type}] of globalDesc.entries()) {
globals[name] = new WebAssembly.Global({type: name, mutable: true});
builder.addImportedGlobal("mod", name, type, true);
addGlobalGetterAndSetter(builder, index, name, type);
}
let inst = builder.instantiate(
{mod: {i32: globals.i32, f32: globals.f32, f64: globals.f64}});
for (let type of ['i32', 'f32', 'f64']) {
let global = globals[type];
let get = inst.exports['get' + type];
let set = inst.exports['set' + type];
assertEquals(get(), global.value, type);
set(1234567);
assertEquals(1234567, global.value, type);
global.value = 7654321;
assertEquals(7654321, get(), type);
}
})();
(function TestImportExportedMutableGlobal() {
const globalDesc = [
{name: 'i32', type: kWasmI32},
{name: 'f32', type: kWasmF32},
{name: 'f64', type: kWasmF64},
];
let builder1 = new WasmModuleBuilder();
let builder2 = new WasmModuleBuilder();
for (let [index, {name, type}] of globalDesc.entries()) {
builder1.addGlobal(type, true).exportAs(name);
addGlobalGetterAndSetter(builder1, index, name, type);
builder2.addImportedGlobal("mod", name, type, true);
addGlobalGetterAndSetter(builder2, index, name, type);
}
let inst1 = builder1.instantiate();
let inst2 = builder2.instantiate({
mod: {
i32: inst1.exports.i32,
f32: inst1.exports.f32,
f64: inst1.exports.f64
}
});
for (let type of ['i32', 'f32', 'f64']) {
let get1 = inst1.exports['get' + type];
let get2 = inst2.exports['get' + type];
let set1 = inst1.exports['set' + type];
let set2 = inst2.exports['set' + type];
assertEquals(get1(), get2(), type);
set1(1234567);
assertEquals(1234567, get2(), type);
set2(7654321);
assertEquals(7654321, get1(), type);
}
})();
(function TestImportExportedMutableGlobalI64() {
function addGettersAndSetters(builder) {
const index = 0;
builder.addFunction('geti64_hi', makeSig([], [kWasmI32]))
.addBody([
kExprGetGlobal, index,
kExprI64Const, 32, kExprI64ShrU,
kExprI32ConvertI64])
.exportFunc();
builder.addFunction('geti64_lo', makeSig([], [kWasmI32]))
.addBody([kExprGetGlobal, index, kExprI32ConvertI64])
.exportFunc();
builder.addFunction("seti64", makeSig([kWasmI32, kWasmI32], []))
.addBody([
kExprGetLocal, 1, kExprI64UConvertI32,
kExprGetLocal, 0, kExprI64UConvertI32,
kExprI64Const, 32, kExprI64Shl,
kExprI64Ior,
kExprSetGlobal, index])
.exportFunc();
};
let builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI64, true).exportAs('i64');
addGettersAndSetters(builder);
let inst1 = builder.instantiate();
builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", 'i64', kWasmI64, true);
addGettersAndSetters(builder);
let inst2 = builder.instantiate({ mod: { i64: inst1.exports.i64 } });
assertEquals(inst1.exports.geti64_lo(), inst2.exports.geti64_lo());
assertEquals(inst1.exports.geti64_hi(), inst2.exports.geti64_hi());
inst1.exports.seti64(13579, 24680);
assertEquals(13579, inst2.exports.geti64_hi());
assertEquals(24680, inst2.exports.geti64_lo());
inst2.exports.seti64(97531, 86420);
assertEquals(97531, inst1.exports.geti64_hi());
assertEquals(86420, inst1.exports.geti64_lo());
})();
(function TestImportMutableAcrossGc() {
let builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true).exportAs('i32');
let inst1 = builder.instantiate();
builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", 'i32', kWasmI32, true);
addGlobalGetterAndSetter(builder, 0, 'i32', kWasmI32);
let inst2 = builder.instantiate({ mod: { i32: inst1.exports.i32 } });
delete inst1;
gc();
inst2.exports.seti32(0x789abcde);
assertEquals(0x789abcde, inst2.exports.geti32());
})();
......@@ -249,9 +249,9 @@ class WasmModuleBuilder {
return this.num_imported_funcs++;
}
addImportedGlobal(module = "", name, type) {
addImportedGlobal(module = "", name, type, mutable = false) {
let o = {module: module, name: name, kind: kExternalGlobal, type: type,
mutable: false}
mutable: mutable};
this.imports.push(o);
return this.num_imported_globals++;
}
......
......@@ -208,7 +208,8 @@ class TestModuleBuilder {
mod.set_origin(origin);
}
byte AddGlobal(ValueType type, bool mutability = true) {
mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false});
mod.globals.push_back(
{type, mutability, WasmInitExpr(), {0}, false, false});
CHECK_LE(mod.globals.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.globals.size() - 1);
}
......
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