Commit 32562e91 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Implement anyref globals

This CL implements the global.get and global.set instruction for anyref
globals. This includes:

* Properly decode anyref globals.
* Add a FixedArray to WasmInstanceObject to store anyref globals.
* Initialize the FixedArray.
* Generate code for global.get and global set.

This CL does not allow to import globals yet.

R=clemensh@chromium.org

Bug: v8:7581
Change-Id: I62617409271d9b6f2253a191681189865aa1f459
Reviewed-on: https://chromium-review.googlesource.com/c/1380112Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58318}
parent 54fb5e38
......@@ -3145,22 +3145,36 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f,
}
Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
if (env_->module->globals[index].type == wasm::ValueType::kWasmAnyRef) {
Node* globals =
LOAD_INSTANCE_FIELD(TaggedGlobalsBuffer, MachineType::TaggedPointer());
return LOAD_FIXED_ARRAY_SLOT_ANY(globals,
env_->module->globals[index].offset);
}
MachineType mem_type =
wasm::ValueTypes::MachineTypeFor(env_->module->globals[index].type);
Node* base = nullptr;
Node* offset = nullptr;
GetGlobalBaseAndOffset(mem_type, env_->module->globals[index], &base,
&offset);
Node* load = SetEffect(graph()->NewNode(mcgraph()->machine()->Load(mem_type),
base, offset, Effect(), Control()));
Node* result = SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(mem_type), base, offset, Effect(), Control()));
#if defined(V8_TARGET_BIG_ENDIAN)
load = BuildChangeEndiannessLoad(load, mem_type,
env_->module->globals[index].type);
result = BuildChangeEndiannessLoad(result, mem_type,
env_->module->globals[index].type);
#endif
return load;
return result;
}
Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
if (env_->module->globals[index].type == wasm::ValueType::kWasmAnyRef) {
Node* globals =
LOAD_INSTANCE_FIELD(TaggedGlobalsBuffer, MachineType::TaggedPointer());
return STORE_FIXED_ARRAY_SLOT_ANY(globals,
env_->module->globals[index].offset, val);
}
MachineType mem_type =
wasm::ValueTypes::MachineTypeFor(env_->module->globals[index].type);
Node* base = nullptr;
......
......@@ -1782,8 +1782,11 @@ void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) { // NOLINT
if (has_memory_object()) {
os << "\n - memory_object: " << Brief(memory_object());
}
if (has_globals_buffer()) {
os << "\n - globals_buffer: " << Brief(globals_buffer());
if (has_untagged_globals_buffer()) {
os << "\n - untagged_globals_buffer: " << Brief(untagged_globals_buffer());
}
if (has_tagged_globals_buffer()) {
os << "\n - tagged_globals_buffer: " << Brief(tagged_globals_buffer());
}
if (has_imported_mutable_globals_buffers()) {
os << "\n - imported_mutable_globals_buffers: "
......
......@@ -354,7 +354,8 @@ class InstanceBuilder {
Handle<WasmModuleObject> module_object_;
MaybeHandle<JSReceiver> ffi_;
MaybeHandle<JSArrayBuffer> memory_;
Handle<JSArrayBuffer> globals_;
Handle<JSArrayBuffer> untagged_globals_;
Handle<FixedArray> tagged_globals_;
std::vector<TableInstance> table_instances_;
std::vector<Handle<JSFunction>> js_wrappers_;
std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
......@@ -1142,28 +1143,35 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Set up the globals for the new instance.
//--------------------------------------------------------------------------
uint32_t globals_buffer_size = module_->globals_buffer_size;
if (globals_buffer_size > 0) {
void* backing_store =
isolate_->array_buffer_allocator()->Allocate(globals_buffer_size);
uint32_t untagged_globals_buffer_size = module_->untagged_globals_buffer_size;
if (untagged_globals_buffer_size > 0) {
void* backing_store = isolate_->array_buffer_allocator()->Allocate(
untagged_globals_buffer_size);
if (backing_store == nullptr) {
thrower_->RangeError("Out of memory: wasm globals");
return {};
}
globals_ =
untagged_globals_ =
isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
constexpr bool is_external = false;
constexpr bool is_wasm_memory = false;
JSArrayBuffer::Setup(globals_, isolate_, is_external, backing_store,
globals_buffer_size, SharedFlag::kNotShared,
is_wasm_memory);
if (globals_.is_null()) {
JSArrayBuffer::Setup(untagged_globals_, isolate_, is_external,
backing_store, untagged_globals_buffer_size,
SharedFlag::kNotShared, is_wasm_memory);
if (untagged_globals_.is_null()) {
thrower_->RangeError("Out of memory: wasm globals");
return {};
}
instance->set_globals_start(
reinterpret_cast<byte*>(globals_->backing_store()));
instance->set_globals_buffer(*globals_);
reinterpret_cast<byte*>(untagged_globals_->backing_store()));
instance->set_untagged_globals_buffer(*untagged_globals_);
}
uint32_t tagged_globals_buffer_size = module_->tagged_globals_buffer_size;
if (tagged_globals_buffer_size > 0) {
tagged_globals_ = isolate_->factory()->NewFixedArray(
static_cast<int>(tagged_globals_buffer_size));
instance->set_tagged_globals_buffer(*tagged_globals_);
}
//--------------------------------------------------------------------------
......@@ -1425,7 +1433,7 @@ uint32_t InstanceBuilder::EvalUint32InitExpr(const WasmInitExpr& expr) {
case WasmInitExpr::kGlobalIndex: {
uint32_t offset = module_->globals[expr.val.global_index].offset;
return ReadLittleEndianValue<uint32_t>(
reinterpret_cast<Address>(raw_buffer_ptr(globals_, offset)));
reinterpret_cast<Address>(raw_buffer_ptr(untagged_globals_, offset)));
}
default:
UNREACHABLE();
......@@ -1452,8 +1460,8 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset,
num, ValueTypes::TypeName(global.type));
reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
global.offset, num, ValueTypes::TypeName(global.type));
switch (global.type) {
case kWasmI32:
WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
......@@ -1479,7 +1487,8 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
Handle<WasmGlobalObject> value) {
TRACE("init [globals_start=%p + %u] = ",
reinterpret_cast<void*>(raw_buffer_ptr(globals_, 0)), global.offset);
reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
global.offset);
switch (global.type) {
case kWasmI32: {
int32_t num = value->GetI32();
......@@ -1767,7 +1776,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
case kExternalGlobal: {
// Immutable global imports are converted to numbers and written into
// the {globals_} array buffer.
// the {untagged_globals_} array buffer.
//
// Mutable global imports instead have their backing array buffers
// referenced by this instance, and store the address of the imported
......@@ -1909,7 +1918,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
template <typename T>
T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset));
return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
}
// Process initialization of globals.
......@@ -1936,7 +1945,24 @@ void InstanceBuilder::InitGlobals() {
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
global.init.val.f64_const);
break;
case WasmInitExpr::kAnyRefConst:
DCHECK(enabled_.anyref);
if (global.imported) break; // We already initialized imported globals.
tagged_globals_->set(global.offset,
ReadOnlyRoots(isolate_).null_value(),
SKIP_WRITE_BARRIER);
break;
case WasmInitExpr::kGlobalIndex: {
if (global.type == ValueType::kWasmAnyRef) {
DCHECK(enabled_.anyref);
int other_offset =
module_->globals[global.init.val.global_index].offset;
tagged_globals_->set(global.offset,
tagged_globals_->get(other_offset),
SKIP_WRITE_BARRIER);
}
// Initialize with another global.
uint32_t new_offset = global.offset;
uint32_t old_offset =
......@@ -1945,8 +1971,8 @@ void InstanceBuilder::InitGlobals() {
size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
? sizeof(double)
: sizeof(int32_t);
memcpy(raw_buffer_ptr(globals_, new_offset),
raw_buffer_ptr(globals_, old_offset), size);
memcpy(raw_buffer_ptr(untagged_globals_, new_offset),
raw_buffer_ptr(untagged_globals_, old_offset), size);
break;
}
case WasmInitExpr::kNone:
......@@ -2126,7 +2152,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
global_addr < backing_store + buffer_size);
offset = static_cast<uint32_t>(global_addr - backing_store);
} else {
buffer = handle(instance->globals_buffer(), isolate_);
buffer = handle(instance->untagged_globals_buffer(), isolate_);
offset = global.offset;
}
......
......@@ -1187,24 +1187,27 @@ class ModuleDecoderImpl : public Decoder {
// Calculate individual global offsets and total size of globals table.
void CalculateGlobalOffsets(WasmModule* module) {
uint32_t offset = 0;
uint32_t untagged_offset = 0;
uint32_t tagged_offset = 0;
uint32_t num_imported_mutable_globals = 0;
if (module->globals.size() == 0) {
module->globals_buffer_size = 0;
return;
}
for (WasmGlobal& global : module->globals) {
byte size = ValueTypes::MemSize(ValueTypes::MachineTypeFor(global.type));
if (global.mutability && global.imported) {
DCHECK(enabled_features_.mut_global);
global.index = num_imported_mutable_globals++;
} else if (global.type == ValueType::kWasmAnyRef) {
global.offset = tagged_offset;
// All entries in the tagged_globals_buffer have size 1.
tagged_offset++;
} else {
offset = (offset + size - 1) & ~(size - 1); // align
global.offset = offset;
offset += size;
byte size =
ValueTypes::MemSize(ValueTypes::MachineTypeFor(global.type));
untagged_offset = (untagged_offset + size - 1) & ~(size - 1); // align
global.offset = untagged_offset;
untagged_offset += size;
}
}
module->globals_buffer_size = offset;
module->untagged_globals_buffer_size = untagged_offset;
module->tagged_globals_buffer_size = tagged_offset;
}
// Verifies the body (code) of a given function.
......
......@@ -165,7 +165,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
std::vector<WasmGlobal> globals;
// Size of the buffer required for all globals that are not imported and
// mutable.
uint32_t globals_buffer_size = 0;
uint32_t untagged_globals_buffer_size = 0;
uint32_t tagged_globals_buffer_size = 0;
uint32_t num_imported_mutable_globals = 0;
uint32_t num_imported_functions = 0;
uint32_t num_declared_functions = 0; // excluding imported
......
......@@ -204,8 +204,10 @@ ACCESSORS2(WasmInstanceObject, exports_object, JSObject, kExportsObjectOffset)
ACCESSORS2(WasmInstanceObject, native_context, Context, kNativeContextOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, memory_object, WasmMemoryObject,
kMemoryObjectOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, globals_buffer, JSArrayBuffer,
kGlobalsBufferOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, untagged_globals_buffer, JSArrayBuffer,
kUntaggedGlobalsBufferOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, tagged_globals_buffer, FixedArray,
kTaggedGlobalsBufferOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, imported_mutable_globals_buffers,
FixedArray, kImportedMutableGlobalsBuffersOffset)
OPTIONAL_ACCESSORS2(WasmInstanceObject, debug_info, WasmDebugInfo,
......
......@@ -394,7 +394,8 @@ class WasmInstanceObject : public JSObject {
DECL_ACCESSORS2(exports_object, JSObject)
DECL_ACCESSORS2(native_context, Context)
DECL_OPTIONAL_ACCESSORS2(memory_object, WasmMemoryObject)
DECL_OPTIONAL_ACCESSORS2(globals_buffer, JSArrayBuffer)
DECL_OPTIONAL_ACCESSORS2(untagged_globals_buffer, JSArrayBuffer)
DECL_OPTIONAL_ACCESSORS2(tagged_globals_buffer, FixedArray)
DECL_OPTIONAL_ACCESSORS2(imported_mutable_globals_buffers, FixedArray)
DECL_OPTIONAL_ACCESSORS2(debug_info, WasmDebugInfo)
DECL_OPTIONAL_ACCESSORS2(table_object, WasmTableObject)
......@@ -432,7 +433,8 @@ class WasmInstanceObject : public JSObject {
V(kExportsObjectOffset, kPointerSize) \
V(kNativeContextOffset, kPointerSize) \
V(kMemoryObjectOffset, kPointerSize) \
V(kGlobalsBufferOffset, kPointerSize) \
V(kUntaggedGlobalsBufferOffset, kPointerSize) \
V(kTaggedGlobalsBufferOffset, kPointerSize) \
V(kImportedMutableGlobalsBuffersOffset, kPointerSize) \
V(kDebugInfoOffset, kPointerSize) \
V(kTableObjectOffset, kPointerSize) \
......
......@@ -26,7 +26,7 @@ TestingModuleBuilder::TestingModuleBuilder(
runtime_exception_support_(exception_support),
lower_simd_(lower_simd) {
WasmJs::Install(isolate_, true);
test_module_->globals_buffer_size = kMaxGlobalsSize;
test_module_->untagged_globals_buffer_size = kMaxGlobalsSize;
memset(globals_data_, 0, sizeof(globals_data_));
uint32_t maybe_import_index = 0;
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-anyref --expose-gc
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDefaultValue() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_nullref = builder.addGlobal(kWasmAnyRef, true);
builder.addFunction("main", kSig_r_v)
.addBody([kExprGetGlobal, g_nullref.index])
.exportAs("main");
const instance = builder.instantiate();
assertNull(instance.exports.main());
})();
(function TestDefaultValueSecondGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_setref = builder.addGlobal(kWasmAnyRef, true);
const g_nullref = builder.addGlobal(kWasmAnyRef, true);
builder.addFunction("main", kSig_r_r)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, g_setref.index,
kExprGetGlobal, g_nullref.index
])
.exportAs("main");
const instance = builder.instantiate();
assertNull(instance.exports.main({}));
})();
(function TestGlobalChangeValue() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
// Dummy global for offset.
builder.addGlobal(kWasmAnyRef, true);
const g = builder.addGlobal(kWasmAnyRef, true);
builder.addFunction("main", kSig_r_r)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, g.index,
kExprGetGlobal, g.index
])
.exportAs("main");
const instance = builder.instantiate();
const test_value = {hello: 'world'};
assertSame(test_value, instance.exports.main(test_value));
})();
(function TestGlobalChangeValueWithGC() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const gc_index = builder.addImport("q", "gc", kSig_v_v);
// Dummy global for offset.
builder.addGlobal(kWasmAnyRef, true);
const g = builder.addGlobal(kWasmAnyRef, true);
builder.addFunction("main", kSig_r_r)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, g.index,
kExprCallFunction, gc_index, // call gc
kExprGetGlobal, g.index
])
.exportAs("main");
const instance = builder.instantiate({q: {gc: gc}});
const test_value = {hello: 'world'};
assertSame(test_value, instance.exports.main(test_value));
assertSame(5, instance.exports.main(5));
assertSame("Hello", instance.exports.main("Hello"));
})();
(function TestGlobalAsRoot() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g = builder.addGlobal(kWasmAnyRef, true);
builder.addFunction("get_global", kSig_r_v)
.addBody([
kExprGetGlobal, g.index
])
.exportAs("get_global");
builder.addFunction("set_global", kSig_v_r)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, g.index
])
.exportAs("set_global");
const instance = builder.instantiate();
let test_value = {hello: 'world'};
instance.exports.set_global(test_value);
test_value = null;
gc();
const result = instance.exports.get_global();
assertEquals('world', result.hello);
})();
......@@ -500,6 +500,9 @@ class WasmModuleBuilder {
section.emit_u8(byte_view[6]);
section.emit_u8(byte_view[7]);
break;
case kWasmAnyRef:
section.emit_u8(kExprRefNull);
break;
}
} else {
// Emit a global-index initializer.
......
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