Commit 741f2312 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Allow anyref values in WebAssembly.Global objects

This CL adds support for anyref in WebAssembly.Global objects. Note
that the specification is not complete yet in this area.

I did the following changes:
- I renamed the `array_buffer` field of WasmGlobalObject to
  `untagged_buffer`
- I added an additional field of type FixedArray, `tagged_buffer`.
  - In the constructor of WasmGlobalObject I allocate either the former
    or the latter, but not both.
- In the WebAssembly.Global constructor I added special handling for
  the case where no initial value is provided. In that case I set the
  inital value to `null` and not to `undefined`.

R=titzer@chromium.org

Bug: v8:7581
Change-Id: I7e4855d7e6c04a9bcdc7ebd450caca5819d060e2
Reviewed-on: https://chromium-review.googlesource.com/c/1398226
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58625}
parent 195686d3
...@@ -1792,7 +1792,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject( ...@@ -1792,7 +1792,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject(
return false; return false;
} }
if (global.mutability) { if (global.mutability) {
Handle<JSArrayBuffer> buffer(global_object->array_buffer(), isolate_); Handle<JSArrayBuffer> buffer(global_object->untagged_buffer(), isolate_);
int index = (*next_imported_mutable_global_index)++; int index = (*next_imported_mutable_global_index)++;
instance->imported_mutable_globals_buffers()->set(index, *buffer); instance->imported_mutable_globals_buffers()->set(index, *buffer);
// It is safe in this case to store the raw pointer to the buffer // It is safe in this case to store the raw pointer to the buffer
...@@ -2193,33 +2193,35 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { ...@@ -2193,33 +2193,35 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
case kExternalGlobal: { case kExternalGlobal: {
const WasmGlobal& global = module_->globals[exp.index]; const WasmGlobal& global = module_->globals[exp.index];
if (enabled_.mut_global) { if (enabled_.mut_global) {
Handle<JSArrayBuffer> buffer; Handle<JSArrayBuffer> untagged_buffer;
uint32_t offset; uint32_t offset;
if (global.mutability && global.imported) { if (global.mutability && global.imported) {
Handle<FixedArray> buffers_array( Handle<FixedArray> buffers_array(
instance->imported_mutable_globals_buffers(), isolate_); instance->imported_mutable_globals_buffers(), isolate_);
buffer = buffers_array->GetValueChecked<JSArrayBuffer>( untagged_buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
isolate_, global.index); isolate_, global.index);
Address global_addr = Address global_addr =
instance->imported_mutable_globals()[global.index]; instance->imported_mutable_globals()[global.index];
size_t buffer_size = buffer->byte_length(); size_t buffer_size = untagged_buffer->byte_length();
Address backing_store = Address backing_store =
reinterpret_cast<Address>(buffer->backing_store()); reinterpret_cast<Address>(untagged_buffer->backing_store());
CHECK(global_addr >= backing_store && CHECK(global_addr >= backing_store &&
global_addr < backing_store + buffer_size); global_addr < backing_store + buffer_size);
offset = static_cast<uint32_t>(global_addr - backing_store); offset = static_cast<uint32_t>(global_addr - backing_store);
} else { } else {
buffer = handle(instance->untagged_globals_buffer(), isolate_); untagged_buffer =
handle(instance->untagged_globals_buffer(), isolate_);
offset = global.offset; offset = global.offset;
} }
// Since the global's array buffer is always provided, allocation // Since the global's array untagged_buffer is always provided,
// should never fail. // allocation should never fail.
Handle<WasmGlobalObject> global_obj = Handle<WasmGlobalObject> global_obj =
WasmGlobalObject::New(isolate_, buffer, global.type, offset, WasmGlobalObject::New(isolate_, untagged_buffer,
global.mutability) MaybeHandle<FixedArray>(), global.type,
offset, global.mutability)
.ToHandleChecked(); .ToHandleChecked();
desc.set_value(global_obj); desc.set_value(global_obj);
} else { } else {
......
...@@ -1152,6 +1152,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1152,6 +1152,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
type = i::wasm::kWasmI64; type = i::wasm::kWasmI64;
} else if (string->StringEquals(v8_str(isolate, "f64"))) { } else if (string->StringEquals(v8_str(isolate, "f64"))) {
type = i::wasm::kWasmF64; type = i::wasm::kWasmF64;
} else if (string->StringEquals(v8_str(isolate, "anyref"))) {
type = i::wasm::kWasmAnyRef;
} else { } else {
thrower.TypeError( thrower.TypeError(
"Descriptor property 'value' must be 'i32', 'i64', 'f32', or " "Descriptor property 'value' must be 'i32', 'i64', 'f32', or "
...@@ -1163,7 +1165,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1163,7 +1165,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
const uint32_t offset = 0; const uint32_t offset = 0;
i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj = i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj =
i::WasmGlobalObject::New(i_isolate, i::MaybeHandle<i::JSArrayBuffer>(), i::WasmGlobalObject::New(i_isolate, i::MaybeHandle<i::JSArrayBuffer>(),
type, offset, is_mutable); i::MaybeHandle<i::FixedArray>(), type, offset,
is_mutable);
i::Handle<i::WasmGlobalObject> global_obj; i::Handle<i::WasmGlobalObject> global_obj;
if (!maybe_global_obj.ToHandle(&global_obj)) { if (!maybe_global_obj.ToHandle(&global_obj)) {
...@@ -1222,6 +1225,17 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1222,6 +1225,17 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
global_obj->SetF64(f64_value); global_obj->SetF64(f64_value);
break; break;
} }
case i::wasm::kWasmAnyRef: {
if (args.Length() < 2) {
// When no inital value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
global_obj->SetAnyRef(
handle(i::ReadOnlyRoots(i_isolate).null_value(), i_isolate));
break;
}
global_obj->SetAnyRef(Utils::OpenHandle(*value));
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -1469,6 +1483,9 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -1469,6 +1483,9 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::kWasmF64: case i::wasm::kWasmF64:
return_value.Set(receiver->GetF64()); return_value.Set(receiver->GetF64());
break; break;
case i::wasm::kWasmAnyRef:
return_value.Set(Utils::ToLocal(receiver->GetAnyRef()));
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -1530,6 +1547,10 @@ void WebAssemblyGlobalSetValue( ...@@ -1530,6 +1547,10 @@ void WebAssemblyGlobalSetValue(
receiver->SetF64(f64_value); receiver->SetF64(f64_value);
break; break;
} }
case i::wasm::kWasmAnyRef: {
receiver->SetAnyRef(Utils::OpenHandle(*args[0]));
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -120,7 +120,9 @@ OPTIONAL_ACCESSORS2(WasmMemoryObject, instances, WeakArrayList, ...@@ -120,7 +120,9 @@ OPTIONAL_ACCESSORS2(WasmMemoryObject, instances, WeakArrayList,
kInstancesOffset) kInstancesOffset)
// WasmGlobalObject // WasmGlobalObject
ACCESSORS2(WasmGlobalObject, array_buffer, JSArrayBuffer, kArrayBufferOffset) ACCESSORS2(WasmGlobalObject, untagged_buffer, JSArrayBuffer,
kUntaggedBufferOffset)
ACCESSORS2(WasmGlobalObject, tagged_buffer, FixedArray, kTaggedBufferOffset)
SMI_ACCESSORS(WasmGlobalObject, offset, kOffsetOffset) SMI_ACCESSORS(WasmGlobalObject, offset, kOffsetOffset)
SMI_ACCESSORS(WasmGlobalObject, flags, kFlagsOffset) SMI_ACCESSORS(WasmGlobalObject, flags, kFlagsOffset)
BIT_FIELD_ACCESSORS(WasmGlobalObject, flags, type, WasmGlobalObject::TypeBits) BIT_FIELD_ACCESSORS(WasmGlobalObject, flags, type, WasmGlobalObject::TypeBits)
...@@ -132,8 +134,9 @@ int WasmGlobalObject::type_size() const { ...@@ -132,8 +134,9 @@ int WasmGlobalObject::type_size() const {
} }
Address WasmGlobalObject::address() const { Address WasmGlobalObject::address() const {
DCHECK_LE(offset() + type_size(), array_buffer()->byte_length()); DCHECK_NE(type(), wasm::kWasmAnyRef);
return Address(array_buffer()->backing_store()) + offset(); DCHECK_LE(offset() + type_size(), untagged_buffer()->byte_length());
return Address(untagged_buffer()->backing_store()) + offset();
} }
int32_t WasmGlobalObject::GetI32() { int32_t WasmGlobalObject::GetI32() {
...@@ -152,6 +155,11 @@ double WasmGlobalObject::GetF64() { ...@@ -152,6 +155,11 @@ double WasmGlobalObject::GetF64() {
return ReadLittleEndianValue<double>(address()); return ReadLittleEndianValue<double>(address());
} }
Handle<Object> WasmGlobalObject::GetAnyRef() {
DCHECK_EQ(type(), wasm::kWasmAnyRef);
return handle(tagged_buffer()->get(offset()), GetIsolate());
}
void WasmGlobalObject::SetI32(int32_t value) { void WasmGlobalObject::SetI32(int32_t value) {
WriteLittleEndianValue<int32_t>(address(), value); WriteLittleEndianValue<int32_t>(address(), value);
} }
...@@ -168,6 +176,11 @@ void WasmGlobalObject::SetF64(double value) { ...@@ -168,6 +176,11 @@ void WasmGlobalObject::SetF64(double value) {
WriteLittleEndianValue<double>(address(), value); WriteLittleEndianValue<double>(address(), value);
} }
void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmAnyRef);
tagged_buffer()->set(offset(), *value);
}
// WasmInstanceObject // WasmInstanceObject
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset) PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset) PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset)
......
...@@ -1097,32 +1097,42 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate, ...@@ -1097,32 +1097,42 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate,
// static // static
MaybeHandle<WasmGlobalObject> WasmGlobalObject::New( MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer, Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,
wasm::ValueType type, int32_t offset, bool is_mutable) { MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type,
int32_t offset, bool is_mutable) {
Handle<JSFunction> global_ctor( Handle<JSFunction> global_ctor(
isolate->native_context()->wasm_global_constructor(), isolate); isolate->native_context()->wasm_global_constructor(), isolate);
auto global_obj = Handle<WasmGlobalObject>::cast( auto global_obj = Handle<WasmGlobalObject>::cast(
isolate->factory()->NewJSObject(global_ctor)); isolate->factory()->NewJSObject(global_ctor));
if (type == wasm::kWasmAnyRef) {
Handle<FixedArray> tagged_buffer;
if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) {
// If no buffer was provided, create one.
tagged_buffer = isolate->factory()->NewFixedArray(1, TENURED);
CHECK_EQ(offset, 0);
}
global_obj->set_tagged_buffer(*tagged_buffer);
} else {
Handle<JSArrayBuffer> untagged_buffer;
uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type); uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type);
if (!maybe_untagged_buffer.ToHandle(&untagged_buffer)) {
Handle<JSArrayBuffer> buffer;
if (!maybe_buffer.ToHandle(&buffer)) {
// If no buffer was provided, create one long enough for the given type. // If no buffer was provided, create one long enough for the given type.
buffer = untagged_buffer =
isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED); isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
const bool initialize = true; const bool initialize = true;
if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, type_size, if (!JSArrayBuffer::SetupAllocatingData(untagged_buffer, isolate,
initialize)) { type_size, initialize)) {
return {}; return {};
} }
} }
// Check that the offset is in bounds. // Check that the offset is in bounds.
CHECK_LE(offset + type_size, buffer->byte_length()); CHECK_LE(offset + type_size, untagged_buffer->byte_length());
global_obj->set_array_buffer(*buffer); global_obj->set_untagged_buffer(*untagged_buffer);
}
global_obj->set_flags(0); global_obj->set_flags(0);
global_obj->set_type(type); global_obj->set_type(type);
global_obj->set_offset(offset); global_obj->set_offset(offset);
......
...@@ -335,7 +335,8 @@ class WasmGlobalObject : public JSObject { ...@@ -335,7 +335,8 @@ class WasmGlobalObject : public JSObject {
public: public:
DECL_CAST2(WasmGlobalObject) DECL_CAST2(WasmGlobalObject)
DECL_ACCESSORS2(array_buffer, JSArrayBuffer) DECL_ACCESSORS2(untagged_buffer, JSArrayBuffer)
DECL_ACCESSORS2(tagged_buffer, FixedArray)
DECL_INT32_ACCESSORS(offset) DECL_INT32_ACCESSORS(offset)
DECL_INT_ACCESSORS(flags) DECL_INT_ACCESSORS(flags)
DECL_PRIMITIVE_ACCESSORS(type, wasm::ValueType) DECL_PRIMITIVE_ACCESSORS(type, wasm::ValueType)
...@@ -351,7 +352,8 @@ class WasmGlobalObject : public JSObject { ...@@ -351,7 +352,8 @@ class WasmGlobalObject : public JSObject {
// Layout description. // Layout description.
#define WASM_GLOBAL_OBJECT_FIELDS(V) \ #define WASM_GLOBAL_OBJECT_FIELDS(V) \
V(kArrayBufferOffset, kTaggedSize) \ V(kUntaggedBufferOffset, kTaggedSize) \
V(kTaggedBufferOffset, kTaggedSize) \
V(kOffsetOffset, kTaggedSize) \ V(kOffsetOffset, kTaggedSize) \
V(kFlagsOffset, kTaggedSize) \ V(kFlagsOffset, kTaggedSize) \
V(kSize, 0) V(kSize, 0)
...@@ -361,7 +363,8 @@ class WasmGlobalObject : public JSObject { ...@@ -361,7 +363,8 @@ class WasmGlobalObject : public JSObject {
#undef WASM_GLOBAL_OBJECT_FIELDS #undef WASM_GLOBAL_OBJECT_FIELDS
V8_EXPORT_PRIVATE static MaybeHandle<WasmGlobalObject> New( V8_EXPORT_PRIVATE static MaybeHandle<WasmGlobalObject> New(
Isolate* isolate, MaybeHandle<JSArrayBuffer> buffer, wasm::ValueType type, Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,
MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type,
int32_t offset, bool is_mutable); int32_t offset, bool is_mutable);
inline int type_size() const; inline int type_size() const;
...@@ -370,11 +373,13 @@ class WasmGlobalObject : public JSObject { ...@@ -370,11 +373,13 @@ class WasmGlobalObject : public JSObject {
inline int64_t GetI64(); inline int64_t GetI64();
inline float GetF32(); inline float GetF32();
inline double GetF64(); inline double GetF64();
inline Handle<Object> GetAnyRef();
inline void SetI32(int32_t value); inline void SetI32(int32_t value);
inline void SetI64(int64_t value); inline void SetI64(int64_t value);
inline void SetF32(float value); inline void SetF32(float value);
inline void SetF64(double value); inline void SetF64(double value);
inline void SetAnyRef(Handle<Object> value);
private: private:
// This function returns the address of the global's data in the // This function returns the address of the global's data in the
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --experimental-wasm-anyref --expose-gc // Flags: --experimental-wasm-anyref --expose-gc --experimental-wasm-mut-global
load("test/mjsunit/wasm/wasm-constants.js"); load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
...@@ -128,3 +128,45 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -128,3 +128,45 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
Test({q: 14}); Test({q: 14});
Test(print); Test(print);
})(); })();
(function TestAnyRefGlobalObjectDefaultValue() {
print(arguments.callee.name);
let default_init = new WebAssembly.Global({value: 'anyref', mutable: true});
assertSame(null, default_init.value);
assertSame(null, default_init.valueOf());
})();
(function TestAnyRefGlobalObject() {
print(arguments.callee.name);
function TestGlobal(obj) {
const global = new WebAssembly.Global({value: 'anyref'}, obj);
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
TestGlobal(undefined);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({a: 11});
TestGlobal(print);
})();
(function TestAnyRefGlobalObjectSetValue() {
print(arguments.callee.name);
let global = new WebAssembly.Global({value: 'anyref', mutable: true});
function TestGlobal(obj) {
global.value = obj;
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
TestGlobal(undefined);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({a: 11});
TestGlobal(print);
})();
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