diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-20 13:40:20 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-22 12:41:23 +0000 |
commit | 7961cea6d1041e3e454dae6a1da660b453efd238 (patch) | |
tree | c0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/v8/src/wasm | |
parent | b7034d0803538058e5c9d904ef03cf5eab34f6ef (diff) | |
download | qtwebengine-chromium-7961cea6d1041e3e454dae6a1da660b453efd238.tar.gz |
BASELINE: Update Chromium to 78.0.3904.130
Change-Id: If185e0c0061b3437531c97c9c8c78f239352a68b
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/wasm')
35 files changed, 2374 insertions, 1360 deletions
diff --git a/chromium/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h b/chromium/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h index 57a157d3a7f..dc68267825c 100644 --- a/chromium/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h +++ b/chromium/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h @@ -210,7 +210,9 @@ void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset, void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst, uint32_t offset) { - LoadFromInstance(dst, offset, kTaggedSize); + DCHECK_LE(offset, kMaxInt); + Ldr(dst, liftoff::GetInstanceOperand()); + LoadTaggedPointerField(dst, MemOperand(dst, offset)); } void LiftoffAssembler::SpillInstance(Register instance) { diff --git a/chromium/v8/src/wasm/baseline/liftoff-compiler.cc b/chromium/v8/src/wasm/baseline/liftoff-compiler.cc index 7a87ae1a956..02de06763c1 100644 --- a/chromium/v8/src/wasm/baseline/liftoff-compiler.cc +++ b/chromium/v8/src/wasm/baseline/liftoff-compiler.cc @@ -1606,32 +1606,35 @@ class LiftoffCompiler { void GenerateRuntimeCall(Runtime::FunctionId runtime_function, int num_args, Register* args) { - auto call_descriptor = compiler::Linkage::GetRuntimeCallDescriptor( - compilation_zone_, runtime_function, num_args, - compiler::Operator::kNoProperties, compiler::CallDescriptor::kNoFlags); // Currently, only one argument is supported. More arguments require some // caution for the parallel register moves (reuse StackTransferRecipe). DCHECK_EQ(1, num_args); +#ifdef DEBUG + auto call_descriptor = compiler::Linkage::GetRuntimeCallDescriptor( + compilation_zone_, runtime_function, num_args, + compiler::Operator::kNoProperties, compiler::CallDescriptor::kNoFlags); constexpr size_t kInputShift = 1; // Input 0 is the call target. compiler::LinkageLocation param_loc = call_descriptor->GetInputLocation(kInputShift); - if (param_loc.IsRegister()) { - Register reg = Register::from_code(param_loc.AsRegister()); - __ Move(LiftoffRegister(reg), LiftoffRegister(args[0]), - LiftoffAssembler::kWasmIntPtr); - } else { - DCHECK(param_loc.IsCallerFrameSlot()); - LiftoffStackSlots stack_slots(&asm_); - stack_slots.Add(LiftoffAssembler::VarState(LiftoffAssembler::kWasmIntPtr, - LiftoffRegister(args[0]))); - stack_slots.Construct(); - } + // Runtime calls take their arguments on the stack. + DCHECK(param_loc.IsCallerFrameSlot()); +#endif + LiftoffStackSlots stack_slots(&asm_); + stack_slots.Add(LiftoffAssembler::VarState(LiftoffAssembler::kWasmIntPtr, + LiftoffRegister(args[0]))); + stack_slots.Construct(); // Set context to "no context" for the runtime call. __ TurboAssembler::Move(kContextRegister, Smi::FromInt(Context::kNoContext)); Register centry = kJavaScriptCallCodeStartRegister; - LOAD_TAGGED_PTR_INSTANCE_FIELD(centry, CEntryStub); + LOAD_INSTANCE_FIELD(centry, IsolateRoot, kSystemPointerSize); + // All cache registers are spilled and there are no register arguments. + LiftoffRegList pinned; + auto centry_id = + Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit; + __ LoadTaggedPointer(centry, centry, no_reg, + IsolateData::builtin_slot_offset(centry_id), pinned); __ CallRuntimeWithCEntry(runtime_function, centry); safepoint_table_builder_.DefineSafepoint(&asm_, Safepoint::kNoLazyDeopt); } diff --git a/chromium/v8/src/wasm/c-api.cc b/chromium/v8/src/wasm/c-api.cc index 86bba189b83..e812dd7994f 100644 --- a/chromium/v8/src/wasm/c-api.cc +++ b/chromium/v8/src/wasm/c-api.cc @@ -29,6 +29,9 @@ #include "include/libplatform/libplatform.h" #include "src/api/api-inl.h" #include "src/compiler/wasm-compiler.h" +#include "src/objects/js-collection-inl.h" +#include "src/objects/managed.h" +#include "src/objects/stack-frame-info-inl.h" #include "src/wasm/leb-helper.h" #include "src/wasm/module-instantiate.h" #include "src/wasm/wasm-arguments.h" @@ -37,6 +40,10 @@ #include "src/wasm/wasm-result.h" #include "src/wasm/wasm-serialization.h" +#ifdef WASM_API_DEBUG +#error "WASM_API_DEBUG is unsupported" +#endif + namespace wasm { namespace { @@ -98,35 +105,36 @@ Name GetNameFromWireBytes(const i::wasm::WireBytesRef& ref, const i::Vector<const uint8_t>& wire_bytes) { DCHECK_LE(ref.offset(), wire_bytes.length()); DCHECK_LE(ref.end_offset(), wire_bytes.length()); + if (ref.length() == 0) return Name::make(); Name name = Name::make_uninitialized(ref.length()); std::memcpy(name.get(), wire_bytes.begin() + ref.offset(), ref.length()); return name; } -own<FuncType*> FunctionSigToFuncType(const i::wasm::FunctionSig* sig) { +own<FuncType> FunctionSigToFuncType(const i::wasm::FunctionSig* sig) { size_t param_count = sig->parameter_count(); - vec<ValType*> params = vec<ValType*>::make_uninitialized(param_count); + ownvec<ValType> params = ownvec<ValType>::make_uninitialized(param_count); for (size_t i = 0; i < param_count; i++) { params[i] = ValType::make(V8ValueTypeToWasm(sig->GetParam(i))); } size_t return_count = sig->return_count(); - vec<ValType*> results = vec<ValType*>::make_uninitialized(return_count); + ownvec<ValType> results = ownvec<ValType>::make_uninitialized(return_count); for (size_t i = 0; i < return_count; i++) { results[i] = ValType::make(V8ValueTypeToWasm(sig->GetReturn(i))); } return FuncType::make(std::move(params), std::move(results)); } -own<ExternType*> GetImportExportType(const i::wasm::WasmModule* module, - const i::wasm::ImportExportKindCode kind, - const uint32_t index) { +own<ExternType> GetImportExportType(const i::wasm::WasmModule* module, + const i::wasm::ImportExportKindCode kind, + const uint32_t index) { switch (kind) { case i::wasm::kExternalFunction: { return FunctionSigToFuncType(module->functions[index].sig); } case i::wasm::kExternalTable: { const i::wasm::WasmTable& table = module->tables[index]; - own<ValType*> elem = ValType::make(V8ValueTypeToWasm(table.type)); + own<ValType> elem = ValType::make(V8ValueTypeToWasm(table.type)); Limits limits(table.initial_size, table.has_maximum_size ? table.maximum_size : -1); return TableType::make(std::move(elem), limits); @@ -139,7 +147,7 @@ own<ExternType*> GetImportExportType(const i::wasm::WasmModule* module, } case i::wasm::kExternalGlobal: { const i::wasm::WasmGlobal& global = module->globals[index]; - own<ValType*> content = ValType::make(V8ValueTypeToWasm(global.type)); + own<ValType> content = ValType::make(V8ValueTypeToWasm(global.type)); Mutability mutability = global.mutability ? VAR : CONST; return GlobalType::make(std::move(content), mutability); } @@ -187,14 +195,6 @@ auto seal(const typename implement<C>::type* x) -> const C* { return reinterpret_cast<const C*>(x); } -#ifdef DEBUG -template <class T> -void vec<T>::make_data() {} - -template <class T> -void vec<T>::free_data() {} -#endif - /////////////////////////////////////////////////////////////////////////////// // Runtime Environment @@ -214,8 +214,8 @@ Config::~Config() { impl(this)->~ConfigImpl(); } void Config::operator delete(void* p) { ::operator delete(p); } -auto Config::make() -> own<Config*> { - return own<Config*>(seal<Config>(new (std::nothrow) ConfigImpl())); +auto Config::make() -> own<Config> { + return own<Config>(seal<Config>(new (std::nothrow) ConfigImpl())); } // Engine @@ -247,13 +247,13 @@ Engine::~Engine() { impl(this)->~EngineImpl(); } void Engine::operator delete(void* p) { ::operator delete(p); } -auto Engine::make(own<Config*>&& config) -> own<Engine*> { +auto Engine::make(own<Config>&& config) -> own<Engine> { i::FLAG_expose_gc = true; i::FLAG_experimental_wasm_anyref = true; i::FLAG_experimental_wasm_bigint = true; i::FLAG_experimental_wasm_mv = true; auto engine = new (std::nothrow) EngineImpl; - if (!engine) return own<Engine*>(); + if (!engine) return own<Engine>(); engine->platform = v8::platform::NewDefaultPlatform(); v8::V8::InitializePlatform(engine->platform.get()); v8::V8::Initialize(); @@ -273,6 +273,38 @@ StoreImpl::~StoreImpl() { delete create_params_.array_buffer_allocator; } +struct ManagedData { + ManagedData(void* info, void (*finalizer)(void*)) + : info(info), finalizer(finalizer) {} + + ~ManagedData() { + if (finalizer) (*finalizer)(info); + } + + void* info; + void (*finalizer)(void*); +}; + +void StoreImpl::SetHostInfo(i::Handle<i::Object> object, void* info, + void (*finalizer)(void*)) { + i::HandleScope scope(i_isolate()); + // Ideally we would specify the total size kept alive by {info} here, + // but all we get from the embedder is a {void*}, so our best estimate + // is the size of the metadata. + size_t estimated_size = sizeof(ManagedData); + i::Handle<i::Object> wrapper = i::Managed<ManagedData>::FromRawPtr( + i_isolate(), estimated_size, new ManagedData(info, finalizer)); + int32_t hash = object->GetOrCreateHash(i_isolate()).value(); + i::JSWeakCollection::Set(host_info_map_, object, wrapper, hash); +} + +void* StoreImpl::GetHostInfo(i::Handle<i::Object> key) { + i::Object raw = + i::EphemeronHashTable::cast(host_info_map_->table()).Lookup(key); + if (raw.IsTheHole(i_isolate())) return nullptr; + return i::Managed<ManagedData>::cast(raw).raw()->info; +} + template <> struct implement<Store> { using type = StoreImpl; @@ -282,33 +314,40 @@ Store::~Store() { impl(this)->~StoreImpl(); } void Store::operator delete(void* p) { ::operator delete(p); } -auto Store::make(Engine*) -> own<Store*> { +auto Store::make(Engine*) -> own<Store> { auto store = make_own(new (std::nothrow) StoreImpl()); - if (!store) return own<Store*>(); + if (!store) return own<Store>(); // Create isolate. store->create_params_.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); - auto isolate = v8::Isolate::New(store->create_params_); - if (!isolate) return own<Store*>(); + v8::Isolate* isolate = v8::Isolate::New(store->create_params_); + if (!isolate) return own<Store>(); + store->isolate_ = isolate; + isolate->SetData(0, store.get()); + // We intentionally do not call isolate->Enter() here, because that would + // prevent embedders from using stores with overlapping but non-nested + // lifetimes. The consequence is that Isolate::Current() is dysfunctional + // and hence must not be called by anything reachable via this file. { v8::HandleScope handle_scope(isolate); // Create context. - auto context = v8::Context::New(isolate); - if (context.IsEmpty()) return own<Store*>(); - v8::Context::Scope context_scope(context); - - store->isolate_ = isolate; + v8::Local<v8::Context> context = v8::Context::New(isolate); + if (context.IsEmpty()) return own<Store>(); + context->Enter(); // The Exit() call is in ~StoreImpl. store->context_ = v8::Eternal<v8::Context>(isolate, context); + + // Create weak map for Refs with host info. + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + store->host_info_map_ = i_isolate->global_handles()->Create( + *i_isolate->factory()->NewJSWeakMap()); } - // We intentionally do not call isolate->Enter() here, because that would - // prevent embedders from using stores with overlapping but non-nested - // lifetimes. The consequence is that Isolate::Current() is dysfunctional - // and hence must not be called by anything reachable via this file. - store->context()->Enter(); - isolate->SetData(0, store.get()); + // We want stack traces for traps. + constexpr int kStackLimit = 10; + isolate->SetCaptureStackTraceForUncaughtExceptions(true, kStackLimit, + v8::StackTrace::kOverview); return make_own(seal<Store>(store.release())); } @@ -329,21 +368,46 @@ struct implement<ValType> { using type = ValTypeImpl; }; -ValTypeImpl* valtypes[] = { - new ValTypeImpl(I32), new ValTypeImpl(I64), new ValTypeImpl(F32), - new ValTypeImpl(F64), new ValTypeImpl(ANYREF), new ValTypeImpl(FUNCREF), -}; +ValTypeImpl* valtype_i32 = new ValTypeImpl(I32); +ValTypeImpl* valtype_i64 = new ValTypeImpl(I64); +ValTypeImpl* valtype_f32 = new ValTypeImpl(F32); +ValTypeImpl* valtype_f64 = new ValTypeImpl(F64); +ValTypeImpl* valtype_anyref = new ValTypeImpl(ANYREF); +ValTypeImpl* valtype_funcref = new ValTypeImpl(FUNCREF); ValType::~ValType() {} void ValType::operator delete(void*) {} -auto ValType::make(ValKind k) -> own<ValType*> { - auto result = seal<ValType>(valtypes[k]); - return own<ValType*>(result); +own<ValType> ValType::make(ValKind k) { + ValTypeImpl* valtype; + switch (k) { + case I32: + valtype = valtype_i32; + break; + case I64: + valtype = valtype_i64; + break; + case F32: + valtype = valtype_f32; + break; + case F64: + valtype = valtype_f64; + break; + case ANYREF: + valtype = valtype_anyref; + break; + case FUNCREF: + valtype = valtype_funcref; + break; + default: + // TODO(wasm+): support new value types + UNREACHABLE(); + } + return own<ValType>(seal<ValType>(valtype)); } -auto ValType::copy() const -> own<ValType*> { return make(kind()); } +auto ValType::copy() const -> own<ValType> { return make(kind()); } auto ValType::kind() const -> ValKind { return impl(this)->kind; } @@ -365,7 +429,7 @@ ExternType::~ExternType() { impl(this)->~ExternTypeImpl(); } void ExternType::operator delete(void* p) { ::operator delete(p); } -auto ExternType::copy() const -> own<ExternType*> { +auto ExternType::copy() const -> own<ExternType> { switch (kind()) { case EXTERN_FUNC: return func()->copy(); @@ -383,11 +447,11 @@ auto ExternType::kind() const -> ExternKind { return impl(this)->kind; } // Function Types struct FuncTypeImpl : ExternTypeImpl { - vec<ValType*> params; - vec<ValType*> results; + ownvec<ValType> params; + ownvec<ValType> results; - FuncTypeImpl(vec<ValType*>& params, // NOLINT(runtime/references) - vec<ValType*>& results) // NOLINT(runtime/references) + FuncTypeImpl(ownvec<ValType>& params, // NOLINT(runtime/references) + ownvec<ValType>& results) // NOLINT(runtime/references) : ExternTypeImpl(EXTERN_FUNC), params(std::move(params)), results(std::move(results)) {} @@ -402,23 +466,23 @@ struct implement<FuncType> { FuncType::~FuncType() {} -auto FuncType::make(vec<ValType*>&& params, vec<ValType*>&& results) - -> own<FuncType*> { +auto FuncType::make(ownvec<ValType>&& params, ownvec<ValType>&& results) + -> own<FuncType> { return params && results - ? own<FuncType*>(seal<FuncType>(new (std::nothrow) - FuncTypeImpl(params, results))) - : own<FuncType*>(); + ? own<FuncType>(seal<FuncType>(new (std::nothrow) + FuncTypeImpl(params, results))) + : own<FuncType>(); } -auto FuncType::copy() const -> own<FuncType*> { - return make(params().copy(), results().copy()); +auto FuncType::copy() const -> own<FuncType> { + return make(params().deep_copy(), results().deep_copy()); } -auto FuncType::params() const -> const vec<ValType*>& { +auto FuncType::params() const -> const ownvec<ValType>& { return impl(this)->params; } -auto FuncType::results() const -> const vec<ValType*>& { +auto FuncType::results() const -> const ownvec<ValType>& { return impl(this)->results; } @@ -437,10 +501,10 @@ auto ExternType::func() const -> const FuncType* { // Global Types struct GlobalTypeImpl : ExternTypeImpl { - own<ValType*> content; + own<ValType> content; Mutability mutability; - GlobalTypeImpl(own<ValType*>& content, // NOLINT(runtime/references) + GlobalTypeImpl(own<ValType>& content, // NOLINT(runtime/references) Mutability mutability) : ExternTypeImpl(EXTERN_GLOBAL), content(std::move(content)), @@ -456,14 +520,14 @@ struct implement<GlobalType> { GlobalType::~GlobalType() {} -auto GlobalType::make(own<ValType*>&& content, Mutability mutability) - -> own<GlobalType*> { - return content ? own<GlobalType*>(seal<GlobalType>( +auto GlobalType::make(own<ValType>&& content, Mutability mutability) + -> own<GlobalType> { + return content ? own<GlobalType>(seal<GlobalType>( new (std::nothrow) GlobalTypeImpl(content, mutability))) - : own<GlobalType*>(); + : own<GlobalType>(); } -auto GlobalType::copy() const -> own<GlobalType*> { +auto GlobalType::copy() const -> own<GlobalType> { return make(content()->copy(), mutability()); } @@ -490,10 +554,10 @@ auto ExternType::global() const -> const GlobalType* { // Table Types struct TableTypeImpl : ExternTypeImpl { - own<ValType*> element; + own<ValType> element; Limits limits; - TableTypeImpl(own<ValType*>& element, // NOLINT(runtime/references) + TableTypeImpl(own<ValType>& element, // NOLINT(runtime/references) Limits limits) : ExternTypeImpl(EXTERN_TABLE), element(std::move(element)), @@ -509,14 +573,13 @@ struct implement<TableType> { TableType::~TableType() {} -auto TableType::make(own<ValType*>&& element, Limits limits) - -> own<TableType*> { - return element ? own<TableType*>(seal<TableType>( +auto TableType::make(own<ValType>&& element, Limits limits) -> own<TableType> { + return element ? own<TableType>(seal<TableType>( new (std::nothrow) TableTypeImpl(element, limits))) - : own<TableType*>(); + : own<TableType>(); } -auto TableType::copy() const -> own<TableType*> { +auto TableType::copy() const -> own<TableType> { return make(element()->copy(), limits()); } @@ -556,12 +619,12 @@ struct implement<MemoryType> { MemoryType::~MemoryType() {} -auto MemoryType::make(Limits limits) -> own<MemoryType*> { - return own<MemoryType*>( +auto MemoryType::make(Limits limits) -> own<MemoryType> { + return own<MemoryType>( seal<MemoryType>(new (std::nothrow) MemoryTypeImpl(limits))); } -auto MemoryType::copy() const -> own<MemoryType*> { +auto MemoryType::copy() const -> own<MemoryType> { return MemoryType::make(limits()); } @@ -584,11 +647,11 @@ auto ExternType::memory() const -> const MemoryType* { struct ImportTypeImpl { Name module; Name name; - own<ExternType*> type; + own<ExternType> type; - ImportTypeImpl(Name& module, // NOLINT(runtime/references) - Name& name, // NOLINT(runtime/references) - own<ExternType*>& type) // NOLINT(runtime/references) + ImportTypeImpl(Name& module, // NOLINT(runtime/references) + Name& name, // NOLINT(runtime/references) + own<ExternType>& type) // NOLINT(runtime/references) : module(std::move(module)), name(std::move(name)), type(std::move(type)) {} @@ -605,15 +668,15 @@ ImportType::~ImportType() { impl(this)->~ImportTypeImpl(); } void ImportType::operator delete(void* p) { ::operator delete(p); } -auto ImportType::make(Name&& module, Name&& name, own<ExternType*>&& type) - -> own<ImportType*> { +auto ImportType::make(Name&& module, Name&& name, own<ExternType>&& type) + -> own<ImportType> { return module && name && type - ? own<ImportType*>(seal<ImportType>( + ? own<ImportType>(seal<ImportType>( new (std::nothrow) ImportTypeImpl(module, name, type))) - : own<ImportType*>(); + : own<ImportType>(); } -auto ImportType::copy() const -> own<ImportType*> { +auto ImportType::copy() const -> own<ImportType> { return make(module().copy(), name().copy(), type()->copy()); } @@ -629,10 +692,10 @@ auto ImportType::type() const -> const ExternType* { struct ExportTypeImpl { Name name; - own<ExternType*> type; + own<ExternType> type; - ExportTypeImpl(Name& name, // NOLINT(runtime/references) - own<ExternType*>& type) // NOLINT(runtime/references) + ExportTypeImpl(Name& name, // NOLINT(runtime/references) + own<ExternType>& type) // NOLINT(runtime/references) : name(std::move(name)), type(std::move(type)) {} ~ExportTypeImpl() {} @@ -647,14 +710,13 @@ ExportType::~ExportType() { impl(this)->~ExportTypeImpl(); } void ExportType::operator delete(void* p) { ::operator delete(p); } -auto ExportType::make(Name&& name, own<ExternType*>&& type) - -> own<ExportType*> { - return name && type ? own<ExportType*>(seal<ExportType>( +auto ExportType::make(Name&& name, own<ExternType>&& type) -> own<ExportType> { + return name && type ? own<ExportType>(seal<ExportType>( new (std::nothrow) ExportTypeImpl(name, type))) - : own<ExportType*>(); + : own<ExportType>(); } -auto ExportType::copy() const -> own<ExportType*> { +auto ExportType::copy() const -> own<ExportType> { return make(name().copy(), type()->copy()); } @@ -680,7 +742,7 @@ i::Handle<i::String> VecToString(i::Isolate* isolate, template <class Ref, class JSType> class RefImpl { public: - static own<Ref*> make(StoreImpl* store, i::Handle<JSType> obj) { + static own<Ref> make(StoreImpl* store, i::Handle<JSType> obj) { RefImpl* self = new (std::nothrow) RefImpl(); if (!self) return nullptr; i::Isolate* isolate = store->i_isolate(); @@ -688,17 +750,9 @@ class RefImpl { return make_own(seal<Ref>(self)); } - void Reset() { - i::GlobalHandles::Destroy(location()); - if (host_data_) { - if (host_data_->finalizer) { - host_data_->finalizer(host_data_->info); - } - delete host_data_; - } - } + ~RefImpl() { i::GlobalHandles::Destroy(location()); } - own<Ref*> copy() const { return make(store(), v8_object()); } + own<Ref> copy() const { return make(store(), v8_object()); } StoreImpl* store() const { return StoreImpl::get(isolate()); } @@ -706,41 +760,20 @@ class RefImpl { i::Handle<JSType> v8_object() const { return i::Handle<JSType>::cast(val_); } - void* get_host_info() const { - if (host_data_ == nullptr) return nullptr; - return host_data_->info; - } + void* get_host_info() const { return store()->GetHostInfo(v8_object()); } void set_host_info(void* info, void (*finalizer)(void*)) { - host_data_ = new HostData(location(), info, finalizer); - i::GlobalHandles::MakeWeak(host_data_->location, host_data_, &v8_finalizer, - v8::WeakCallbackType::kParameter); + store()->SetHostInfo(v8_object(), info, finalizer); } private: - struct HostData { - HostData(i::Address* location, void* info, void (*finalizer)(void*)) - : location(location), info(info), finalizer(finalizer) {} - i::Address* location; - void* info; - void (*finalizer)(void*); - }; - RefImpl() {} - static void v8_finalizer(const v8::WeakCallbackInfo<void>& info) { - HostData* data = reinterpret_cast<HostData*>(info.GetParameter()); - i::GlobalHandles::Destroy(data->location); - if (data->finalizer) (*data->finalizer)(data->info); - delete data; - } - i::Address* location() const { return reinterpret_cast<i::Address*>(val_.address()); } i::Handle<i::JSReceiver> val_; - HostData* host_data_ = nullptr; }; template <> @@ -749,13 +782,17 @@ struct implement<Ref> { }; Ref::~Ref() { - impl(this)->Reset(); delete impl(this); } void Ref::operator delete(void* p) {} -auto Ref::copy() const -> own<Ref*> { return impl(this)->copy(); } +auto Ref::copy() const -> own<Ref> { return impl(this)->copy(); } + +auto Ref::same(const Ref* that) const -> bool { + i::HandleScope handle_scope(impl(this)->isolate()); + return impl(this)->v8_object()->SameValue(*impl(that)->v8_object()); +} auto Ref::get_host_info() const -> void* { return impl(this)->get_host_info(); } @@ -766,6 +803,52 @@ void Ref::set_host_info(void* info, void (*finalizer)(void*)) { /////////////////////////////////////////////////////////////////////////////// // Runtime Objects +// Frames + +namespace { + +struct FrameImpl { + FrameImpl(own<Instance>&& instance, uint32_t func_index, size_t func_offset, + size_t module_offset) + : instance(std::move(instance)), + func_index(func_index), + func_offset(func_offset), + module_offset(module_offset) {} + + ~FrameImpl() {} + + own<Instance> instance; + uint32_t func_index; + size_t func_offset; + size_t module_offset; +}; + +} // namespace + +template <> +struct implement<Frame> { + using type = FrameImpl; +}; + +Frame::~Frame() { impl(this)->~FrameImpl(); } + +void Frame::operator delete(void* p) { ::operator delete(p); } + +own<Frame> Frame::copy() const { + auto self = impl(this); + return own<Frame>(seal<Frame>( + new (std::nothrow) FrameImpl(self->instance->copy(), self->func_index, + self->func_offset, self->module_offset))); +} + +Instance* Frame::instance() const { return impl(this)->instance.get(); } + +uint32_t Frame::func_index() const { return impl(this)->func_index; } + +size_t Frame::func_offset() const { return impl(this)->func_offset; } + +size_t Frame::module_offset() const { return impl(this)->module_offset; } + // Traps template <> @@ -775,9 +858,9 @@ struct implement<Trap> { Trap::~Trap() {} -auto Trap::copy() const -> own<Trap*> { return impl(this)->copy(); } +auto Trap::copy() const -> own<Trap> { return impl(this)->copy(); } -auto Trap::make(Store* store_abs, const Message& message) -> own<Trap*> { +auto Trap::make(Store* store_abs, const Message& message) -> own<Trap> { auto store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope handle_scope(isolate); @@ -801,6 +884,58 @@ auto Trap::message() const -> Message { return vec<byte_t>::adopt(length, utf8.release()); } +namespace { + +own<Instance> GetInstance(StoreImpl* store, + i::Handle<i::WasmInstanceObject> instance); + +own<Frame> CreateFrameFromInternal(i::Handle<i::FixedArray> frames, int index, + i::Isolate* isolate, StoreImpl* store) { + i::Handle<i::StackTraceFrame> frame(i::StackTraceFrame::cast(frames->get(0)), + isolate); + i::Handle<i::WasmInstanceObject> instance = + i::StackTraceFrame::GetWasmInstance(frame); + uint32_t func_index = i::StackTraceFrame::GetLineNumber(frame); + size_t func_offset = i::StackTraceFrame::GetFunctionOffset(frame); + size_t module_offset = i::StackTraceFrame::GetColumnNumber(frame); + return own<Frame>(seal<Frame>(new (std::nothrow) FrameImpl( + GetInstance(store, instance), func_index, func_offset, module_offset))); +} + +} // namespace + +own<Frame> Trap::origin() const { + i::Isolate* isolate = impl(this)->isolate(); + i::HandleScope handle_scope(isolate); + + i::Handle<i::JSMessageObject> message = + isolate->CreateMessage(impl(this)->v8_object(), nullptr); + i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()), + isolate); + if (frames->length() == 0) { + return own<Frame>(); + } + return CreateFrameFromInternal(frames, 0, isolate, impl(this)->store()); +} + +ownvec<Frame> Trap::trace() const { + i::Isolate* isolate = impl(this)->isolate(); + i::HandleScope handle_scope(isolate); + + i::Handle<i::JSMessageObject> message = + isolate->CreateMessage(impl(this)->v8_object(), nullptr); + i::Handle<i::FixedArray> frames(i::FixedArray::cast(message->stack_frames()), + isolate); + int num_frames = frames->length(); + // {num_frames} can be 0; the code below can handle that case. + ownvec<Frame> result = ownvec<Frame>::make_uninitialized(num_frames); + for (int i = 0; i < num_frames; i++) { + result[i] = + CreateFrameFromInternal(frames, i, isolate, impl(this)->store()); + } + return result; +} + // Foreign Objects template <> @@ -810,9 +945,9 @@ struct implement<Foreign> { Foreign::~Foreign() {} -auto Foreign::copy() const -> own<Foreign*> { return impl(this)->copy(); } +auto Foreign::copy() const -> own<Foreign> { return impl(this)->copy(); } -auto Foreign::make(Store* store_abs) -> own<Foreign*> { +auto Foreign::make(Store* store_abs) -> own<Foreign> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope handle_scope(isolate); @@ -831,7 +966,7 @@ struct implement<Module> { Module::~Module() {} -auto Module::copy() const -> own<Module*> { return impl(this)->copy(); } +auto Module::copy() const -> own<Module> { return impl(this)->copy(); } auto Module::validate(Store* store_abs, const vec<byte_t>& binary) -> bool { i::wasm::ModuleWireBytes bytes( @@ -841,66 +976,60 @@ auto Module::validate(Store* store_abs, const vec<byte_t>& binary) -> bool { return isolate->wasm_engine()->SyncValidate(isolate, features, bytes); } -class NopErrorThrower : public i::wasm::ErrorThrower { - public: - explicit NopErrorThrower(i::Isolate* isolate) - : i::wasm::ErrorThrower(isolate, "ignored") {} - ~NopErrorThrower() { Reset(); } -}; - -auto Module::make(Store* store_abs, const vec<byte_t>& binary) -> own<Module*> { +auto Module::make(Store* store_abs, const vec<byte_t>& binary) -> own<Module> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope scope(isolate); i::wasm::ModuleWireBytes bytes( {reinterpret_cast<const uint8_t*>(binary.get()), binary.size()}); i::wasm::WasmFeatures features = i::wasm::WasmFeaturesFromIsolate(isolate); - NopErrorThrower thrower(isolate); + i::wasm::ErrorThrower thrower(isolate, "ignored"); i::Handle<i::WasmModuleObject> module; if (!isolate->wasm_engine() ->SyncCompile(isolate, features, &thrower, bytes) .ToHandle(&module)) { + thrower.Reset(); // The API provides no way to expose the error. return nullptr; } return implement<Module>::type::make(store, module); } -auto Module::imports() const -> vec<ImportType*> { +auto Module::imports() const -> ownvec<ImportType> { const i::wasm::NativeModule* native_module = impl(this)->v8_object()->native_module(); const i::wasm::WasmModule* module = native_module->module(); const i::Vector<const uint8_t> wire_bytes = native_module->wire_bytes(); const std::vector<i::wasm::WasmImport>& import_table = module->import_table; size_t size = import_table.size(); - vec<ImportType*> imports = vec<ImportType*>::make_uninitialized(size); + ownvec<ImportType> imports = ownvec<ImportType>::make_uninitialized(size); for (uint32_t i = 0; i < size; i++) { const i::wasm::WasmImport& imp = import_table[i]; Name module_name = GetNameFromWireBytes(imp.module_name, wire_bytes); Name name = GetNameFromWireBytes(imp.field_name, wire_bytes); - own<ExternType*> type = GetImportExportType(module, imp.kind, imp.index); + own<ExternType> type = GetImportExportType(module, imp.kind, imp.index); imports[i] = ImportType::make(std::move(module_name), std::move(name), std::move(type)); } return imports; } -vec<ExportType*> ExportsImpl(i::Handle<i::WasmModuleObject> module_obj) { +ownvec<ExportType> ExportsImpl(i::Handle<i::WasmModuleObject> module_obj) { const i::wasm::NativeModule* native_module = module_obj->native_module(); const i::wasm::WasmModule* module = native_module->module(); const i::Vector<const uint8_t> wire_bytes = native_module->wire_bytes(); const std::vector<i::wasm::WasmExport>& export_table = module->export_table; size_t size = export_table.size(); - vec<ExportType*> exports = vec<ExportType*>::make_uninitialized(size); + ownvec<ExportType> exports = ownvec<ExportType>::make_uninitialized(size); for (uint32_t i = 0; i < size; i++) { const i::wasm::WasmExport& exp = export_table[i]; Name name = GetNameFromWireBytes(exp.name, wire_bytes); - own<ExternType*> type = GetImportExportType(module, exp.kind, exp.index); + own<ExternType> type = GetImportExportType(module, exp.kind, exp.index); exports[i] = ExportType::make(std::move(name), std::move(type)); } return exports; } -auto Module::exports() const -> vec<ExportType*> { +auto Module::exports() const -> ownvec<ExportType> { return ExportsImpl(impl(this)->v8_object()); } @@ -923,11 +1052,11 @@ auto Module::serialize() const -> vec<byte_t> { {reinterpret_cast<uint8_t*>(ptr), serial_size})) { buffer.reset(); } - return std::move(buffer); + return buffer; } auto Module::deserialize(Store* store_abs, const vec<byte_t>& serialized) - -> own<Module*> { + -> own<Module> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope handle_scope(isolate); @@ -963,13 +1092,12 @@ void Shared<Module>::operator delete(void* p) { ::operator delete(p); } -auto Module::share() const -> own<Shared<Module>*> { +auto Module::share() const -> own<Shared<Module>> { auto shared = seal<Shared<Module>>(new vec<byte_t>(serialize())); return make_own(shared); } -auto Module::obtain(Store* store, const Shared<Module>* shared) - -> own<Module*> { +auto Module::obtain(Store* store, const Shared<Module>* shared) -> own<Module> { return Module::deserialize(store, *impl(shared)); } @@ -982,7 +1110,7 @@ struct implement<Extern> { Extern::~Extern() {} -auto Extern::copy() const -> own<Extern*> { return impl(this)->copy(); } +auto Extern::copy() const -> own<Extern> { return impl(this)->copy(); } auto Extern::kind() const -> ExternKind { i::Handle<i::JSReceiver> obj = impl(this)->v8_object(); @@ -995,7 +1123,7 @@ auto Extern::kind() const -> ExternKind { UNREACHABLE(); } -auto Extern::type() const -> own<ExternType*> { +auto Extern::type() const -> own<ExternType> { switch (kind()) { case EXTERN_FUNC: return func()->type(); @@ -1053,11 +1181,11 @@ struct implement<Func> { Func::~Func() {} -auto Func::copy() const -> own<Func*> { return impl(this)->copy(); } +auto Func::copy() const -> own<Func> { return impl(this)->copy(); } struct FuncData { Store* store; - own<FuncType*> type; + own<FuncType> type; enum Kind { kCallback, kCallbackWithEnv } kind; union { Func::callback callback; @@ -1077,8 +1205,7 @@ struct FuncData { if (finalizer) (*finalizer)(env); } - static i::Address v8_callback(void* data, i::Address argv); - static void finalize_func_data(void* data); + static i::Address v8_callback(i::Address host_data_foreign, i::Address argv); }; namespace { @@ -1111,11 +1238,11 @@ class SignatureHelper : public i::AllStatic { return sig; } - static own<FuncType*> Deserialize(i::PodArray<i::wasm::ValueType> sig) { + static own<FuncType> Deserialize(i::PodArray<i::wasm::ValueType> sig) { int result_arity = ResultArity(sig); int param_arity = sig.length() - result_arity - 1; - vec<ValType*> results = vec<ValType*>::make_uninitialized(result_arity); - vec<ValType*> params = vec<ValType*>::make_uninitialized(param_arity); + ownvec<ValType> results = ownvec<ValType>::make_uninitialized(result_arity); + ownvec<ValType> params = ownvec<ValType>::make_uninitialized(param_arity); int i = 0; for (; i < result_arity; ++i) { @@ -1146,29 +1273,30 @@ class SignatureHelper : public i::AllStatic { } }; -auto make_func(Store* store_abs, FuncData* data) -> own<Func*> { +auto make_func(Store* store_abs, FuncData* data) -> own<Func> { auto store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope handle_scope(isolate); + i::Handle<i::Managed<FuncData>> embedder_data = + i::Managed<FuncData>::FromRawPtr(isolate, sizeof(FuncData), data); i::Handle<i::WasmCapiFunction> function = i::WasmCapiFunction::New( - isolate, reinterpret_cast<i::Address>(&FuncData::v8_callback), data, - SignatureHelper::Serialize(isolate, data->type.get())); + isolate, reinterpret_cast<i::Address>(&FuncData::v8_callback), + embedder_data, SignatureHelper::Serialize(isolate, data->type.get())); auto func = implement<Func>::type::make(store, function); - func->set_host_info(data, &FuncData::finalize_func_data); return func; } } // namespace auto Func::make(Store* store, const FuncType* type, Func::callback callback) - -> own<Func*> { + -> own<Func> { auto data = new FuncData(store, type, FuncData::kCallback); data->callback = callback; return make_func(store, data); } auto Func::make(Store* store, const FuncType* type, callback_with_env callback, - void* env, void (*finalizer)(void*)) -> own<Func*> { + void* env, void (*finalizer)(void*)) -> own<Func> { auto data = new FuncData(store, type, FuncData::kCallbackWithEnv); data->callback_with_env = callback; data->env = env; @@ -1176,7 +1304,7 @@ auto Func::make(Store* store, const FuncType* type, callback_with_env callback, return make_func(store, data); } -auto Func::type() const -> own<FuncType*> { +auto Func::type() const -> own<FuncType> { i::Handle<i::JSFunction> func = impl(this)->v8_object(); if (i::WasmCapiFunction::IsWasmCapiFunction(*func)) { return SignatureHelper::Deserialize(SignatureHelper::GetSig(func)); @@ -1216,6 +1344,37 @@ auto Func::result_arity() const -> size_t { namespace { +own<Ref> V8RefValueToWasm(StoreImpl* store, i::Handle<i::Object> value) { + if (value->IsNull(store->i_isolate())) return nullptr; + return implement<Ref>::type::make(store, + i::Handle<i::JSReceiver>::cast(value)); +} + +i::Handle<i::Object> WasmRefToV8(i::Isolate* isolate, const Ref* ref) { + if (ref == nullptr) return i::ReadOnlyRoots(isolate).null_value_handle(); + return impl(ref)->v8_object(); +} + +i::Handle<i::Object> CallTargetForCaching(i::Isolate* isolate, + i::Address real_call_target) { + if (i::kTaggedSize == i::kInt32Size) { + return isolate->factory()->NewForeign(real_call_target); + } else { + // 64-bit uncompressed platform. + return i::handle(i::Smi((real_call_target << i::kSmiTagSize) | i::kSmiTag), + isolate); + } +} + +i::Address CallTargetFromCache(i::Object cached_call_target) { + if (i::kTaggedSize == i::kInt32Size) { + return i::Foreign::cast(cached_call_target).foreign_address(); + } else { + // 64-bit uncompressed platform. + return cached_call_target.ptr() >> i::kSmiTagSize; + } +} + void PrepareFunctionData(i::Isolate* isolate, i::Handle<i::WasmExportedFunctionData> function_data, i::wasm::FunctionSig* sig) { @@ -1228,16 +1387,16 @@ void PrepareFunctionData(i::Isolate* isolate, // Compute packed args size. function_data->set_packed_args_size( i::wasm::CWasmArgumentsPacker::TotalSize(sig)); - // Get call target (function table offset). This is an Address, we store - // it as a pseudo-Smi by shifting it by one bit, so the GC leaves it alone. - i::Address call_target = - function_data->instance().GetCallTarget(function_data->function_index()); - i::Smi smi_target((call_target << i::kSmiTagSize) | i::kSmiTag); - function_data->set_wasm_call_target(smi_target); + // Get call target (function table offset), and wrap it as a cacheable object + // (pseudo-Smi or Foreign, depending on platform). + i::Handle<i::Object> call_target = CallTargetForCaching( + isolate, + function_data->instance().GetCallTarget(function_data->function_index())); + function_data->set_wasm_call_target(*call_target); } void PushArgs(i::wasm::FunctionSig* sig, const Val args[], - i::wasm::CWasmArgumentsPacker* packer) { + i::wasm::CWasmArgumentsPacker* packer, StoreImpl* store) { for (size_t i = 0; i < sig->parameter_count(); i++) { i::wasm::ValueType type = sig->GetParam(i); switch (type) { @@ -1255,7 +1414,7 @@ void PushArgs(i::wasm::FunctionSig* sig, const Val args[], break; case i::wasm::kWasmAnyRef: case i::wasm::kWasmFuncRef: - packer->Push(impl(args[i].ref())->v8_object()->ptr()); + packer->Push(WasmRefToV8(store->i_isolate(), args[i].ref())->ptr()); break; case i::wasm::kWasmExnRef: // TODO(jkummerow): Implement these. @@ -1288,13 +1447,8 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[], case i::wasm::kWasmAnyRef: case i::wasm::kWasmFuncRef: { i::Address raw = packer->Pop<i::Address>(); - if (raw == i::kNullAddress) { - results[i] = Val(nullptr); - } else { - i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw)); - i::Handle<i::JSReceiver> obj(raw_obj, store->i_isolate()); - results[i] = Val(implement<Ref>::type::make(store, obj)); - } + i::Handle<i::Object> obj(i::Object(raw), store->i_isolate()); + results[i] = Val(V8RefValueToWasm(store, obj)); break; } case i::wasm::kWasmExnRef: @@ -1307,9 +1461,9 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[], } } -own<Trap*> CallWasmCapiFunction(i::WasmCapiFunctionData data, const Val args[], - Val results[]) { - FuncData* func_data = reinterpret_cast<FuncData*>(data.embedder_data()); +own<Trap> CallWasmCapiFunction(i::WasmCapiFunctionData data, const Val args[], + Val results[]) { + FuncData* func_data = i::Managed<FuncData>::cast(data.embedder_data()).raw(); if (func_data->kind == FuncData::kCallback) { return (func_data->callback)(args, results); } @@ -1317,9 +1471,28 @@ own<Trap*> CallWasmCapiFunction(i::WasmCapiFunctionData data, const Val args[], return (func_data->callback_with_env)(func_data->env, args, results); } +i::Handle<i::JSReceiver> GetProperException( + i::Isolate* isolate, i::Handle<i::Object> maybe_exception) { + if (maybe_exception->IsJSReceiver()) { + return i::Handle<i::JSReceiver>::cast(maybe_exception); + } + i::MaybeHandle<i::String> maybe_string = + i::Object::ToString(isolate, maybe_exception); + i::Handle<i::String> string = isolate->factory()->empty_string(); + if (!maybe_string.ToHandle(&string)) { + // If converting the {maybe_exception} to string threw another exception, + // just give up and leave {string} as the empty string. + isolate->clear_pending_exception(); + } + // {NewError} cannot fail when its input is a plain String, so we always + // get an Error object here. + return i::Handle<i::JSReceiver>::cast( + isolate->factory()->NewError(isolate->error_function(), string)); +} + } // namespace -auto Func::call(const Val args[], Val results[]) const -> own<Trap*> { +auto Func::call(const Val args[], Val results[]) const -> own<Trap> { auto func = impl(this); auto store = func->store(); auto isolate = store->i_isolate(); @@ -1343,10 +1516,10 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap*> { i::Handle<i::Code> wrapper_code = i::Handle<i::Code>( i::Code::cast(function_data->c_wrapper_code()), isolate); i::Address call_target = - function_data->wasm_call_target().ptr() >> i::kSmiTagSize; + CallTargetFromCache(function_data->wasm_call_target()); i::wasm::CWasmArgumentsPacker packer(function_data->packed_args_size()); - PushArgs(sig, args, &packer); + PushArgs(sig, args, &packer, store); i::Handle<i::Object> object_ref = instance; if (function_index < @@ -1377,28 +1550,24 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap*> { if (isolate->has_pending_exception()) { i::Handle<i::Object> exception(isolate->pending_exception(), isolate); isolate->clear_pending_exception(); - if (!exception->IsJSReceiver()) { - i::MaybeHandle<i::String> maybe_string = - i::Object::ToString(isolate, exception); - i::Handle<i::String> string = maybe_string.is_null() - ? isolate->factory()->empty_string() - : maybe_string.ToHandleChecked(); - exception = - isolate->factory()->NewError(isolate->error_function(), string); - } - return implement<Trap>::type::make( - store, i::Handle<i::JSReceiver>::cast(exception)); + return implement<Trap>::type::make(store, + GetProperException(isolate, exception)); } PopArgs(sig, results, &packer, store); return nullptr; } -i::Address FuncData::v8_callback(void* data, i::Address argv) { - FuncData* self = reinterpret_cast<FuncData*>(data); +i::Address FuncData::v8_callback(i::Address host_data_foreign, + i::Address argv) { + FuncData* self = + i::Managed<FuncData>::cast(i::Object(host_data_foreign))->raw(); + StoreImpl* store = impl(self->store); + i::Isolate* isolate = store->i_isolate(); + i::HandleScope scope(isolate); - const vec<ValType*>& param_types = self->type->params(); - const vec<ValType*>& result_types = self->type->results(); + const ownvec<ValType>& param_types = self->type->params(); + const ownvec<ValType>& result_types = self->type->results(); int num_param_types = static_cast<int>(param_types.size()); int num_result_types = static_cast<int>(result_types.size()); @@ -1428,19 +1597,14 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { case FUNCREF: { i::Address raw = v8::base::ReadUnalignedValue<i::Address>(p); p += sizeof(raw); - if (raw == i::kNullAddress) { - params[i] = Val(nullptr); - } else { - i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw)); - i::Handle<i::JSReceiver> obj(raw_obj, raw_obj.GetIsolate()); - params[i] = Val(implement<Ref>::type::make(impl(self->store), obj)); - } + i::Handle<i::Object> obj(i::Object(raw), isolate); + params[i] = Val(V8RefValueToWasm(store, obj)); break; } } } - own<Trap*> trap; + own<Trap> trap; if (self->kind == kCallbackWithEnv) { trap = self->callback_with_env(self->env, params.get(), results.get()); } else { @@ -1448,7 +1612,6 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { } if (trap) { - i::Isolate* isolate = impl(self->store)->i_isolate(); isolate->Throw(*impl(trap.get())->v8_object()); i::Object ex = isolate->pending_exception(); isolate->clear_pending_exception(); @@ -1476,12 +1639,8 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { break; case ANYREF: case FUNCREF: { - if (results[i].ref() == nullptr) { - v8::base::WriteUnalignedValue(p, i::kNullAddress); - } else { - v8::base::WriteUnalignedValue( - p, impl(results[i].ref())->v8_object()->ptr()); - } + v8::base::WriteUnalignedValue( + p, WasmRefToV8(isolate, results[i].ref())->ptr()); p += sizeof(i::Address); break; } @@ -1490,10 +1649,6 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { return i::kNullAddress; } -void FuncData::finalize_func_data(void* data) { - delete reinterpret_cast<FuncData*>(data); -} - // Global Instances template <> @@ -1503,10 +1658,10 @@ struct implement<Global> { Global::~Global() {} -auto Global::copy() const -> own<Global*> { return impl(this)->copy(); } +auto Global::copy() const -> own<Global> { return impl(this)->copy(); } auto Global::make(Store* store_abs, const GlobalType* type, const Val& val) - -> own<Global*> { + -> own<Global> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope handle_scope(isolate); @@ -1528,7 +1683,7 @@ auto Global::make(Store* store_abs, const GlobalType* type, const Val& val) return global; } -auto Global::type() const -> own<GlobalType*> { +auto Global::type() const -> own<GlobalType> { i::Handle<i::WasmGlobalObject> v8_global = impl(this)->v8_object(); ValKind kind = V8ValueTypeToWasm(v8_global->type()); Mutability mutability = v8_global->is_mutable() ? VAR : CONST; @@ -1546,15 +1701,11 @@ auto Global::get() const -> Val { return Val(v8_global->GetF32()); case F64: return Val(v8_global->GetF64()); - case ANYREF: { - i::Handle<i::JSReceiver> obj = - i::Handle<i::JSReceiver>::cast(v8_global->GetRef()); - return Val(RefImpl<Ref, i::JSReceiver>::make(impl(this)->store(), obj)); - } + case ANYREF: case FUNCREF: { - i::Handle<i::JSFunction> obj = - i::Handle<i::JSFunction>::cast(v8_global->GetRef()); - return Val(implement<Func>::type::make(impl(this)->store(), obj)); + StoreImpl* store = impl(this)->store(); + i::HandleScope scope(store->i_isolate()); + return Val(V8RefValueToWasm(store, v8_global->GetRef())); } default: // TODO(wasm+): support new value types @@ -1574,10 +1725,12 @@ void Global::set(const Val& val) { case F64: return v8_global->SetF64(val.f64()); case ANYREF: - return v8_global->SetAnyRef(impl(val.ref())->v8_object()); + return v8_global->SetAnyRef( + WasmRefToV8(impl(this)->store()->i_isolate(), val.ref())); case FUNCREF: { - bool result = v8_global->SetFuncRef(impl(this)->store()->i_isolate(), - impl(val.ref())->v8_object()); + i::Isolate* isolate = impl(this)->store()->i_isolate(); + bool result = + v8_global->SetFuncRef(isolate, WasmRefToV8(isolate, val.ref())); DCHECK(result); USE(result); return; @@ -1597,14 +1750,13 @@ struct implement<Table> { Table::~Table() {} -auto Table::copy() const -> own<Table*> { return impl(this)->copy(); } +auto Table::copy() const -> own<Table> { return impl(this)->copy(); } auto Table::make(Store* store_abs, const TableType* type, const Ref* ref) - -> own<Table*> { + -> own<Table> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope scope(isolate); - auto enabled_features = i::wasm::WasmFeaturesFromFlags(); // Get "element". i::wasm::ValueType i_type; @@ -1613,13 +1765,11 @@ auto Table::make(Store* store_abs, const TableType* type, const Ref* ref) i_type = i::wasm::kWasmFuncRef; break; case ANYREF: - if (enabled_features.anyref) { - i_type = i::wasm::kWasmAnyRef; - break; - } // Else fall through. - V8_FALLTHROUGH; + DCHECK(i::wasm::WasmFeaturesFromFlags().anyref); // See Engine::make(). + i_type = i::wasm::kWasmAnyRef; + break; default: - UNREACHABLE(); // 'element' must be 'FUNCREF'. + UNREACHABLE(); return nullptr; } @@ -1652,42 +1802,44 @@ auto Table::make(Store* store_abs, const TableType* type, const Ref* ref) return implement<Table>::type::make(store, table_obj); } -auto Table::type() const -> own<TableType*> { +auto Table::type() const -> own<TableType> { i::Handle<i::WasmTableObject> table = impl(this)->v8_object(); uint32_t min = table->current_length(); uint32_t max; if (!table->maximum_length().ToUint32(&max)) max = 0xFFFFFFFFu; - // TODO(wasm+): support new element types. - return TableType::make(ValType::make(FUNCREF), Limits(min, max)); + ValKind kind; + switch (table->type()) { + case i::wasm::kWasmFuncRef: + kind = FUNCREF; + break; + case i::wasm::kWasmAnyRef: + kind = ANYREF; + break; + default: + UNREACHABLE(); + } + return TableType::make(ValType::make(kind), Limits(min, max)); } -auto Table::get(size_t index) const -> own<Ref*> { +auto Table::get(size_t index) const -> own<Ref> { i::Handle<i::WasmTableObject> table = impl(this)->v8_object(); - if (index >= table->current_length()) return own<Ref*>(); + if (index >= table->current_length()) return own<Ref>(); i::Isolate* isolate = table->GetIsolate(); i::HandleScope handle_scope(isolate); i::Handle<i::Object> result = i::WasmTableObject::Get(isolate, table, static_cast<uint32_t>(index)); - if (!result->IsJSFunction()) return own<Ref*>(); - DCHECK(i::WasmExportedFunction::IsWasmExportedFunction(*result) || - i::WasmCapiFunction::IsWasmCapiFunction(*result)); - // TODO(wasm+): other references - return implement<Func>::type::make(impl(this)->store(), - i::Handle<i::JSFunction>::cast(result)); + // TODO(jkummerow): If we support both JavaScript and the C-API at the same + // time, we need to handle Smis and other JS primitives here. + DCHECK(result->IsNull(isolate) || result->IsJSReceiver()); + return V8RefValueToWasm(impl(this)->store(), result); } auto Table::set(size_t index, const Ref* ref) -> bool { - if (ref && !impl(ref)->v8_object()->IsFunction()) { - WASM_UNIMPLEMENTED("non-function table elements"); - } i::Handle<i::WasmTableObject> table = impl(this)->v8_object(); if (index >= table->current_length()) return false; i::Isolate* isolate = table->GetIsolate(); i::HandleScope handle_scope(isolate); - i::Handle<i::Object> obj = - ref ? i::Handle<i::Object>::cast(impl(ref)->v8_object()) - : i::Handle<i::Object>::cast( - i::ReadOnlyRoots(isolate).null_value_handle()); + i::Handle<i::Object> obj = WasmRefToV8(isolate, ref); i::WasmTableObject::Set(isolate, table, static_cast<uint32_t>(index), obj); return true; } @@ -1701,10 +1853,7 @@ auto Table::grow(size_t delta, const Ref* ref) -> bool { i::Handle<i::WasmTableObject> table = impl(this)->v8_object(); i::Isolate* isolate = table->GetIsolate(); i::HandleScope scope(isolate); - i::Handle<i::Object> init_value = - ref == nullptr - ? i::Handle<i::Object>::cast(isolate->factory()->null_value()) - : i::Handle<i::Object>::cast(impl(ref)->v8_object()); + i::Handle<i::Object> init_value = WasmRefToV8(isolate, ref); int result = i::WasmTableObject::Grow( isolate, table, static_cast<uint32_t>(delta), init_value); return result >= 0; @@ -1719,9 +1868,9 @@ struct implement<Memory> { Memory::~Memory() {} -auto Memory::copy() const -> own<Memory*> { return impl(this)->copy(); } +auto Memory::copy() const -> own<Memory> { return impl(this)->copy(); } -auto Memory::make(Store* store_abs, const MemoryType* type) -> own<Memory*> { +auto Memory::make(Store* store_abs, const MemoryType* type) -> own<Memory> { StoreImpl* store = impl(store_abs); i::Isolate* isolate = store->i_isolate(); i::HandleScope scope(isolate); @@ -1738,12 +1887,12 @@ auto Memory::make(Store* store_abs, const MemoryType* type) -> own<Memory*> { i::Handle<i::WasmMemoryObject> memory_obj; if (!i::WasmMemoryObject::New(isolate, minimum, maximum, is_shared) .ToHandle(&memory_obj)) { - return own<Memory*>(); + return own<Memory>(); } return implement<Memory>::type::make(store, memory_obj); } -auto Memory::type() const -> own<MemoryType*> { +auto Memory::type() const -> own<MemoryType> { i::Handle<i::WasmMemoryObject> memory = impl(this)->v8_object(); uint32_t min = static_cast<uint32_t>(memory->array_buffer().byte_length() / i::wasm::kWasmPageSize); @@ -1784,10 +1933,10 @@ struct implement<Instance> { Instance::~Instance() {} -auto Instance::copy() const -> own<Instance*> { return impl(this)->copy(); } +auto Instance::copy() const -> own<Instance> { return impl(this)->copy(); } -auto Instance::make(Store* store_abs, const Module* module_abs, - const Extern* const imports[]) -> own<Instance*> { +own<Instance> Instance::make(Store* store_abs, const Module* module_abs, + const Extern* const imports[], own<Trap>* trap) { StoreImpl* store = impl(store_abs); const implement<Module>::type* module = impl(module_abs); i::Isolate* isolate = store->i_isolate(); @@ -1795,11 +1944,12 @@ auto Instance::make(Store* store_abs, const Module* module_abs, DCHECK_EQ(module->v8_object()->GetIsolate(), isolate); - vec<ImportType*> import_types = module_abs->imports(); + if (trap) *trap = nullptr; + ownvec<ImportType> import_types = module_abs->imports(); i::Handle<i::JSObject> imports_obj = isolate->factory()->NewJSObject(isolate->object_function()); for (size_t i = 0; i < import_types.size(); ++i) { - auto type = import_types[i]; + ImportType* type = import_types[i].get(); i::Handle<i::String> module_str = VecToString(isolate, type->module()); i::Handle<i::String> name_str = VecToString(isolate, type->name()); @@ -1817,17 +1967,45 @@ auto Instance::make(Store* store_abs, const Module* module_abs, ignore(i::Object::SetProperty(isolate, module_obj, name_str, impl(imports[i])->v8_object())); } + i::wasm::ErrorThrower thrower(isolate, "instantiation"); + i::MaybeHandle<i::WasmInstanceObject> instance_obj = + isolate->wasm_engine()->SyncInstantiate( + isolate, &thrower, module->v8_object(), imports_obj, + i::MaybeHandle<i::JSArrayBuffer>()); + if (trap) { + if (thrower.error()) { + *trap = implement<Trap>::type::make( + store, GetProperException(isolate, thrower.Reify())); + DCHECK(!thrower.error()); // Reify() called Reset(). + DCHECK(!isolate->has_pending_exception()); // Hasn't been thrown yet. + return own<Instance>(); + } else if (isolate->has_pending_exception()) { + i::Handle<i::Object> maybe_exception(isolate->pending_exception(), + isolate); + *trap = implement<Trap>::type::make( + store, GetProperException(isolate, maybe_exception)); + isolate->clear_pending_exception(); + return own<Instance>(); + } + } else if (instance_obj.is_null()) { + // If no {trap} output is specified, silently swallow all errors. + thrower.Reset(); + isolate->clear_pending_exception(); + return own<Instance>(); + } + return implement<Instance>::type::make(store, instance_obj.ToHandleChecked()); +} - NopErrorThrower thrower(isolate); - i::Handle<i::WasmInstanceObject> instance_obj = - isolate->wasm_engine() - ->SyncInstantiate(isolate, &thrower, module->v8_object(), imports_obj, - i::MaybeHandle<i::JSArrayBuffer>()) - .ToHandleChecked(); - return implement<Instance>::type::make(store, instance_obj); +namespace { + +own<Instance> GetInstance(StoreImpl* store, + i::Handle<i::WasmInstanceObject> instance) { + return implement<Instance>::type::make(store, instance); } -auto Instance::exports() const -> vec<Extern*> { +} // namespace + +auto Instance::exports() const -> ownvec<Extern> { const implement<Instance>::type* instance = impl(this); StoreImpl* store = instance->store(); i::Isolate* isolate = store->i_isolate(); @@ -1837,9 +2015,10 @@ auto Instance::exports() const -> vec<Extern*> { isolate); i::Handle<i::JSObject> exports_obj(instance_obj->exports_object(), isolate); - vec<ExportType*> export_types = ExportsImpl(module_obj); - vec<Extern*> exports = vec<Extern*>::make_uninitialized(export_types.size()); - if (!exports) return vec<Extern*>::invalid(); + ownvec<ExportType> export_types = ExportsImpl(module_obj); + ownvec<Extern> exports = + ownvec<Extern>::make_uninitialized(export_types.size()); + if (!exports) return ownvec<Extern>::invalid(); for (size_t i = 0; i < export_types.size(); ++i) { auto& name = export_types[i]->name(); @@ -1852,20 +2031,20 @@ auto Instance::exports() const -> vec<Extern*> { switch (type->kind()) { case EXTERN_FUNC: { DCHECK(i::WasmExportedFunction::IsWasmExportedFunction(*obj)); - exports[i].reset(implement<Func>::type::make( - store, i::Handle<i::WasmExportedFunction>::cast(obj))); + exports[i] = implement<Func>::type::make( + store, i::Handle<i::WasmExportedFunction>::cast(obj)); } break; case EXTERN_GLOBAL: { - exports[i].reset(implement<Global>::type::make( - store, i::Handle<i::WasmGlobalObject>::cast(obj))); + exports[i] = implement<Global>::type::make( + store, i::Handle<i::WasmGlobalObject>::cast(obj)); } break; case EXTERN_TABLE: { - exports[i].reset(implement<Table>::type::make( - store, i::Handle<i::WasmTableObject>::cast(obj))); + exports[i] = implement<Table>::type::make( + store, i::Handle<i::WasmTableObject>::cast(obj)); } break; case EXTERN_MEMORY: { - exports[i].reset(implement<Memory>::type::make( - store, i::Handle<i::WasmMemoryObject>::cast(obj))); + exports[i] = implement<Memory>::type::make( + store, i::Handle<i::WasmMemoryObject>::cast(obj)); } break; } } @@ -1898,152 +2077,151 @@ struct borrowed_vec { } // extern "C++" -#define WASM_DEFINE_OWN(name, Name) \ - struct wasm_##name##_t : Name {}; \ - \ - void wasm_##name##_delete(wasm_##name##_t* x) { delete x; } \ - \ - extern "C++" inline auto hide(Name* x)->wasm_##name##_t* { \ - return static_cast<wasm_##name##_t*>(x); \ - } \ - extern "C++" inline auto hide(const Name* x)->const wasm_##name##_t* { \ - return static_cast<const wasm_##name##_t*>(x); \ - } \ - extern "C++" inline auto reveal(wasm_##name##_t* x)->Name* { return x; } \ - extern "C++" inline auto reveal(const wasm_##name##_t* x)->const Name* { \ - return x; \ - } \ - extern "C++" inline auto get(wasm::own<Name*>& x)->wasm_##name##_t* { \ - return hide(x.get()); \ - } \ - extern "C++" inline auto get(const wasm::own<Name*>& x) \ - ->const wasm_##name##_t* { \ - return hide(x.get()); \ - } \ - extern "C++" inline auto release(wasm::own<Name*>&& x)->wasm_##name##_t* { \ - return hide(x.release()); \ - } \ - extern "C++" inline auto adopt(wasm_##name##_t* x)->wasm::own<Name*> { \ - return make_own(x); \ +#define WASM_DEFINE_OWN(name, Name) \ + struct wasm_##name##_t : Name {}; \ + \ + void wasm_##name##_delete(wasm_##name##_t* x) { delete x; } \ + \ + extern "C++" inline auto hide_##name(Name* x)->wasm_##name##_t* { \ + return static_cast<wasm_##name##_t*>(x); \ + } \ + extern "C++" inline auto hide_##name(const Name* x) \ + ->const wasm_##name##_t* { \ + return static_cast<const wasm_##name##_t*>(x); \ + } \ + extern "C++" inline auto reveal_##name(wasm_##name##_t* x)->Name* { \ + return x; \ + } \ + extern "C++" inline auto reveal_##name(const wasm_##name##_t* x) \ + ->const Name* { \ + return x; \ + } \ + extern "C++" inline auto get_##name(wasm::own<Name>& x)->wasm_##name##_t* { \ + return hide_##name(x.get()); \ + } \ + extern "C++" inline auto get_##name(const wasm::own<Name>& x) \ + ->const wasm_##name##_t* { \ + return hide_##name(x.get()); \ + } \ + extern "C++" inline auto release_##name(wasm::own<Name>&& x) \ + ->wasm_##name##_t* { \ + return hide_##name(x.release()); \ + } \ + extern "C++" inline auto adopt_##name(wasm_##name##_t* x)->wasm::own<Name> { \ + return make_own(x); \ } // Vectors -#define WASM_DEFINE_VEC_BASE(name, Name, ptr_or_none) \ - extern "C++" inline auto hide(wasm::vec<Name ptr_or_none>& v) \ - ->wasm_##name##_vec_t* { \ - static_assert(sizeof(wasm_##name##_vec_t) == sizeof(wasm::vec<Name>), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<wasm_##name##_vec_t*>(&v); \ - } \ - extern "C++" inline auto hide(const wasm::vec<Name ptr_or_none>& v) \ - ->const wasm_##name##_vec_t* { \ - static_assert(sizeof(wasm_##name##_vec_t) == sizeof(wasm::vec<Name>), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<const wasm_##name##_vec_t*>(&v); \ - } \ - extern "C++" inline auto hide(Name ptr_or_none* v) \ - ->wasm_##name##_t ptr_or_none* { \ - static_assert( \ - sizeof(wasm_##name##_t ptr_or_none) == sizeof(Name ptr_or_none), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<wasm_##name##_t ptr_or_none*>(v); \ - } \ - extern "C++" inline auto hide(Name ptr_or_none const* v) \ - ->wasm_##name##_t ptr_or_none const* { \ - static_assert( \ - sizeof(wasm_##name##_t ptr_or_none) == sizeof(Name ptr_or_none), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<wasm_##name##_t ptr_or_none const*>(v); \ - } \ - extern "C++" inline auto reveal(wasm_##name##_t ptr_or_none* v) \ - ->Name ptr_or_none* { \ - static_assert( \ - sizeof(wasm_##name##_t ptr_or_none) == sizeof(Name ptr_or_none), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<Name ptr_or_none*>(v); \ - } \ - extern "C++" inline auto reveal(wasm_##name##_t ptr_or_none const* v) \ - ->Name ptr_or_none const* { \ - static_assert( \ - sizeof(wasm_##name##_t ptr_or_none) == sizeof(Name ptr_or_none), \ - "C/C++ incompatibility"); \ - return reinterpret_cast<Name ptr_or_none const*>(v); \ - } \ - extern "C++" inline auto get(wasm::vec<Name ptr_or_none>& v) \ - ->wasm_##name##_vec_t { \ - wasm_##name##_vec_t v2 = {v.size(), hide(v.get())}; \ - return v2; \ - } \ - extern "C++" inline auto get(const wasm::vec<Name ptr_or_none>& v) \ - ->const wasm_##name##_vec_t { \ - wasm_##name##_vec_t v2 = { \ - v.size(), const_cast<wasm_##name##_t ptr_or_none*>(hide(v.get()))}; \ - return v2; \ - } \ - extern "C++" inline auto release(wasm::vec<Name ptr_or_none>&& v) \ - ->wasm_##name##_vec_t { \ - wasm_##name##_vec_t v2 = {v.size(), hide(v.release())}; \ - return v2; \ - } \ - extern "C++" inline auto adopt(wasm_##name##_vec_t* v) \ - ->wasm::vec<Name ptr_or_none> { \ - return wasm::vec<Name ptr_or_none>::adopt(v->size, reveal(v->data)); \ - } \ - extern "C++" inline auto borrow(const wasm_##name##_vec_t* v) \ - ->borrowed_vec<Name ptr_or_none> { \ - return borrowed_vec<Name ptr_or_none>( \ - wasm::vec<Name ptr_or_none>::adopt(v->size, reveal(v->data))); \ - } \ - \ - void wasm_##name##_vec_new_uninitialized(wasm_##name##_vec_t* out, \ - size_t size) { \ - *out = release(wasm::vec<Name ptr_or_none>::make_uninitialized(size)); \ - } \ - void wasm_##name##_vec_new_empty(wasm_##name##_vec_t* out) { \ - wasm_##name##_vec_new_uninitialized(out, 0); \ - } \ - \ - void wasm_##name##_vec_delete(wasm_##name##_vec_t* v) { adopt(v); } +#define WASM_DEFINE_VEC_BASE(name, Name, vec, ptr_or_none) \ + static_assert(sizeof(wasm_##name##_vec_t) == sizeof(vec<Name>), \ + "C/C++ incompatibility"); \ + static_assert( \ + sizeof(wasm_##name##_t ptr_or_none) == sizeof(vec<Name>::elem_type), \ + "C/C++ incompatibility"); \ + extern "C++" inline auto hide_##name##_vec(vec<Name>& v) \ + ->wasm_##name##_vec_t* { \ + return reinterpret_cast<wasm_##name##_vec_t*>(&v); \ + } \ + extern "C++" inline auto hide_##name##_vec(const vec<Name>& v) \ + ->const wasm_##name##_vec_t* { \ + return reinterpret_cast<const wasm_##name##_vec_t*>(&v); \ + } \ + extern "C++" inline auto hide_##name##_vec(vec<Name>::elem_type* v) \ + ->wasm_##name##_t ptr_or_none* { \ + return reinterpret_cast<wasm_##name##_t ptr_or_none*>(v); \ + } \ + extern "C++" inline auto hide_##name##_vec(const vec<Name>::elem_type* v) \ + ->wasm_##name##_t ptr_or_none const* { \ + return reinterpret_cast<wasm_##name##_t ptr_or_none const*>(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec(wasm_##name##_t ptr_or_none* v) \ + ->vec<Name>::elem_type* { \ + return reinterpret_cast<vec<Name>::elem_type*>(v); \ + } \ + extern "C++" inline auto reveal_##name##_vec( \ + wasm_##name##_t ptr_or_none const* v) \ + ->const vec<Name>::elem_type* { \ + return reinterpret_cast<const vec<Name>::elem_type*>(v); \ + } \ + extern "C++" inline auto get_##name##_vec(vec<Name>& v) \ + ->wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = {v.size(), hide_##name##_vec(v.get())}; \ + return v2; \ + } \ + extern "C++" inline auto get_##name##_vec(const vec<Name>& v) \ + ->const wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = { \ + v.size(), \ + const_cast<wasm_##name##_t ptr_or_none*>(hide_##name##_vec(v.get()))}; \ + return v2; \ + } \ + extern "C++" inline auto release_##name##_vec(vec<Name>&& v) \ + ->wasm_##name##_vec_t { \ + wasm_##name##_vec_t v2 = {v.size(), hide_##name##_vec(v.release())}; \ + return v2; \ + } \ + extern "C++" inline auto adopt_##name##_vec(wasm_##name##_vec_t* v) \ + ->vec<Name> { \ + return vec<Name>::adopt(v->size, reveal_##name##_vec(v->data)); \ + } \ + extern "C++" inline auto borrow_##name##_vec(const wasm_##name##_vec_t* v) \ + ->borrowed_vec<vec<Name>::elem_type> { \ + return borrowed_vec<vec<Name>::elem_type>( \ + vec<Name>::adopt(v->size, reveal_##name##_vec(v->data))); \ + } \ + \ + void wasm_##name##_vec_new_uninitialized(wasm_##name##_vec_t* out, \ + size_t size) { \ + *out = release_##name##_vec(vec<Name>::make_uninitialized(size)); \ + } \ + void wasm_##name##_vec_new_empty(wasm_##name##_vec_t* out) { \ + wasm_##name##_vec_new_uninitialized(out, 0); \ + } \ + \ + void wasm_##name##_vec_delete(wasm_##name##_vec_t* v) { \ + adopt_##name##_vec(v); \ + } // Vectors with no ownership management of elements -#define WASM_DEFINE_VEC_PLAIN(name, Name, ptr_or_none) \ - WASM_DEFINE_VEC_BASE(name, Name, ptr_or_none) \ - \ - void wasm_##name##_vec_new(wasm_##name##_vec_t* out, size_t size, \ - wasm_##name##_t ptr_or_none const data[]) { \ - auto v2 = wasm::vec<Name ptr_or_none>::make_uninitialized(size); \ - if (v2.size() != 0) { \ - memcpy(v2.get(), data, size * sizeof(wasm_##name##_t ptr_or_none)); \ - } \ - *out = release(std::move(v2)); \ - } \ - \ - void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ - wasm_##name##_vec_t* v) { \ - wasm_##name##_vec_new(out, v->size, v->data); \ - } - -// Vectors who own their elements -#define WASM_DEFINE_VEC(name, Name, ptr_or_none) \ - WASM_DEFINE_VEC_BASE(name, Name, ptr_or_none) \ - \ - void wasm_##name##_vec_new(wasm_##name##_vec_t* out, size_t size, \ - wasm_##name##_t ptr_or_none const data[]) { \ - auto v2 = wasm::vec<Name ptr_or_none>::make_uninitialized(size); \ - for (size_t i = 0; i < v2.size(); ++i) { \ - v2[i] = adopt(data[i]); \ - } \ - *out = release(std::move(v2)); \ - } \ - \ - void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ - wasm_##name##_vec_t* v) { \ - auto v2 = wasm::vec<Name ptr_or_none>::make_uninitialized(v->size); \ - for (size_t i = 0; i < v2.size(); ++i) { \ - v2[i] = adopt(wasm_##name##_copy(v->data[i])); \ - } \ - *out = release(std::move(v2)); \ +#define WASM_DEFINE_VEC_PLAIN(name, Name) \ + WASM_DEFINE_VEC_BASE(name, Name, \ + wasm::vec, ) /* NOLINT(whitespace/parens) */ \ + \ + void wasm_##name##_vec_new(wasm_##name##_vec_t* out, size_t size, \ + const wasm_##name##_t data[]) { \ + auto v2 = wasm::vec<Name>::make_uninitialized(size); \ + if (v2.size() != 0) { \ + memcpy(v2.get(), data, size * sizeof(wasm_##name##_t)); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ + } \ + \ + void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ + wasm_##name##_vec_t* v) { \ + wasm_##name##_vec_new(out, v->size, v->data); \ + } + +// Vectors that own their elements +#define WASM_DEFINE_VEC_OWN(name, Name) \ + WASM_DEFINE_VEC_BASE(name, Name, wasm::ownvec, *) \ + \ + void wasm_##name##_vec_new(wasm_##name##_vec_t* out, size_t size, \ + wasm_##name##_t* const data[]) { \ + auto v2 = wasm::ownvec<Name>::make_uninitialized(size); \ + for (size_t i = 0; i < v2.size(); ++i) { \ + v2[i] = adopt_##name(data[i]); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ + } \ + \ + void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ + wasm_##name##_vec_t* v) { \ + auto v2 = wasm::ownvec<Name>::make_uninitialized(v->size); \ + for (size_t i = 0; i < v2.size(); ++i) { \ + v2[i] = adopt_##name(wasm_##name##_copy(v->data[i])); \ + } \ + *out = release_##name##_vec(std::move(v2)); \ } extern "C++" { @@ -2056,7 +2234,7 @@ inline auto is_empty(T* p) -> bool { // Byte vectors using byte = byte_t; -WASM_DEFINE_VEC_PLAIN(byte, byte, ) +WASM_DEFINE_VEC_PLAIN(byte, byte) /////////////////////////////////////////////////////////////////////////////// // Runtime Environment @@ -2065,16 +2243,20 @@ WASM_DEFINE_VEC_PLAIN(byte, byte, ) WASM_DEFINE_OWN(config, wasm::Config) -wasm_config_t* wasm_config_new() { return release(wasm::Config::make()); } +wasm_config_t* wasm_config_new() { + return release_config(wasm::Config::make()); +} // Engine WASM_DEFINE_OWN(engine, wasm::Engine) -wasm_engine_t* wasm_engine_new() { return release(wasm::Engine::make()); } +wasm_engine_t* wasm_engine_new() { + return release_engine(wasm::Engine::make()); +} wasm_engine_t* wasm_engine_new_with_config(wasm_config_t* config) { - return release(wasm::Engine::make(adopt(config))); + return release_engine(wasm::Engine::make(adopt_config(config))); } // Stores @@ -2082,7 +2264,7 @@ wasm_engine_t* wasm_engine_new_with_config(wasm_config_t* config) { WASM_DEFINE_OWN(store, wasm::Store) wasm_store_t* wasm_store_new(wasm_engine_t* engine) { - return release(wasm::Store::make(engine)); + return release_store(wasm::Store::make(engine)); } /////////////////////////////////////////////////////////////////////////////// @@ -2090,38 +2272,40 @@ wasm_store_t* wasm_store_new(wasm_engine_t* engine) { // Type attributes -extern "C++" inline auto hide(wasm::Mutability mutability) +extern "C++" inline auto hide_mutability(wasm::Mutability mutability) -> wasm_mutability_t { return static_cast<wasm_mutability_t>(mutability); } -extern "C++" inline auto reveal(wasm_mutability_t mutability) +extern "C++" inline auto reveal_mutability(wasm_mutability_t mutability) -> wasm::Mutability { return static_cast<wasm::Mutability>(mutability); } -extern "C++" inline auto hide(const wasm::Limits& limits) +extern "C++" inline auto hide_limits(const wasm::Limits& limits) -> const wasm_limits_t* { return reinterpret_cast<const wasm_limits_t*>(&limits); } -extern "C++" inline auto reveal(wasm_limits_t limits) -> wasm::Limits { +extern "C++" inline auto reveal_limits(wasm_limits_t limits) -> wasm::Limits { return wasm::Limits(limits.min, limits.max); } -extern "C++" inline auto hide(wasm::ValKind kind) -> wasm_valkind_t { +extern "C++" inline auto hide_valkind(wasm::ValKind kind) -> wasm_valkind_t { return static_cast<wasm_valkind_t>(kind); } -extern "C++" inline auto reveal(wasm_valkind_t kind) -> wasm::ValKind { +extern "C++" inline auto reveal_valkind(wasm_valkind_t kind) -> wasm::ValKind { return static_cast<wasm::ValKind>(kind); } -extern "C++" inline auto hide(wasm::ExternKind kind) -> wasm_externkind_t { +extern "C++" inline auto hide_externkind(wasm::ExternKind kind) + -> wasm_externkind_t { return static_cast<wasm_externkind_t>(kind); } -extern "C++" inline auto reveal(wasm_externkind_t kind) -> wasm::ExternKind { +extern "C++" inline auto reveal_externkind(wasm_externkind_t kind) + -> wasm::ExternKind { return static_cast<wasm::ExternKind>(kind); } @@ -2129,10 +2313,10 @@ extern "C++" inline auto reveal(wasm_externkind_t kind) -> wasm::ExternKind { #define WASM_DEFINE_TYPE(name, Name) \ WASM_DEFINE_OWN(name, Name) \ - WASM_DEFINE_VEC(name, Name, *) \ + WASM_DEFINE_VEC_OWN(name, Name) \ \ wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* t) { \ - return release(t->copy()); \ + return release_##name(t->copy()); \ } // Value Types @@ -2140,11 +2324,11 @@ extern "C++" inline auto reveal(wasm_externkind_t kind) -> wasm::ExternKind { WASM_DEFINE_TYPE(valtype, wasm::ValType) wasm_valtype_t* wasm_valtype_new(wasm_valkind_t k) { - return release(wasm::ValType::make(reveal(k))); + return release_valtype(wasm::ValType::make(reveal_valkind(k))); } wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t* t) { - return hide(t->kind()); + return hide_valkind(t->kind()); } // Function Types @@ -2153,15 +2337,16 @@ WASM_DEFINE_TYPE(functype, wasm::FuncType) wasm_functype_t* wasm_functype_new(wasm_valtype_vec_t* params, wasm_valtype_vec_t* results) { - return release(wasm::FuncType::make(adopt(params), adopt(results))); + return release_functype(wasm::FuncType::make(adopt_valtype_vec(params), + adopt_valtype_vec(results))); } const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t* ft) { - return hide(ft->params()); + return hide_valtype_vec(ft->params()); } const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t* ft) { - return hide(ft->results()); + return hide_valtype_vec(ft->results()); } // Global Types @@ -2170,15 +2355,16 @@ WASM_DEFINE_TYPE(globaltype, wasm::GlobalType) wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t* content, wasm_mutability_t mutability) { - return release(wasm::GlobalType::make(adopt(content), reveal(mutability))); + return release_globaltype(wasm::GlobalType::make( + adopt_valtype(content), reveal_mutability(mutability))); } const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* gt) { - return hide(gt->content()); + return hide_valtype(gt->content()); } wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t* gt) { - return hide(gt->mutability()); + return hide_mutability(gt->mutability()); } // Table Types @@ -2187,15 +2373,16 @@ WASM_DEFINE_TYPE(tabletype, wasm::TableType) wasm_tabletype_t* wasm_tabletype_new(wasm_valtype_t* element, const wasm_limits_t* limits) { - return release(wasm::TableType::make(adopt(element), reveal(*limits))); + return release_tabletype( + wasm::TableType::make(adopt_valtype(element), reveal_limits(*limits))); } const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t* tt) { - return hide(tt->element()); + return hide_valtype(tt->element()); } const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* tt) { - return hide(tt->limits()); + return hide_limits(tt->limits()); } // Memory Types @@ -2203,11 +2390,11 @@ const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* tt) { WASM_DEFINE_TYPE(memorytype, wasm::MemoryType) wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { - return release(wasm::MemoryType::make(reveal(*limits))); + return release_memorytype(wasm::MemoryType::make(reveal_limits(*limits))); } const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* mt) { - return hide(mt->limits()); + return hide_limits(mt->limits()); } // Extern Types @@ -2215,82 +2402,90 @@ const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* mt) { WASM_DEFINE_TYPE(externtype, wasm::ExternType) wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t* et) { - return hide(et->kind()); + return hide_externkind(et->kind()); } wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t* ft) { - return hide(static_cast<wasm::ExternType*>(ft)); + return hide_externtype(static_cast<wasm::ExternType*>(ft)); } wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t* gt) { - return hide(static_cast<wasm::ExternType*>(gt)); + return hide_externtype(static_cast<wasm::ExternType*>(gt)); } wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t* tt) { - return hide(static_cast<wasm::ExternType*>(tt)); + return hide_externtype(static_cast<wasm::ExternType*>(tt)); } wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t* mt) { - return hide(static_cast<wasm::ExternType*>(mt)); + return hide_externtype(static_cast<wasm::ExternType*>(mt)); } const wasm_externtype_t* wasm_functype_as_externtype_const( const wasm_functype_t* ft) { - return hide(static_cast<const wasm::ExternType*>(ft)); + return hide_externtype(static_cast<const wasm::ExternType*>(ft)); } const wasm_externtype_t* wasm_globaltype_as_externtype_const( const wasm_globaltype_t* gt) { - return hide(static_cast<const wasm::ExternType*>(gt)); + return hide_externtype(static_cast<const wasm::ExternType*>(gt)); } const wasm_externtype_t* wasm_tabletype_as_externtype_const( const wasm_tabletype_t* tt) { - return hide(static_cast<const wasm::ExternType*>(tt)); + return hide_externtype(static_cast<const wasm::ExternType*>(tt)); } const wasm_externtype_t* wasm_memorytype_as_externtype_const( const wasm_memorytype_t* mt) { - return hide(static_cast<const wasm::ExternType*>(mt)); + return hide_externtype(static_cast<const wasm::ExternType*>(mt)); } wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_FUNC - ? hide(static_cast<wasm::FuncType*>(reveal(et))) + ? hide_functype( + static_cast<wasm::FuncType*>(reveal_externtype(et))) : nullptr; } wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_GLOBAL - ? hide(static_cast<wasm::GlobalType*>(reveal(et))) + ? hide_globaltype( + static_cast<wasm::GlobalType*>(reveal_externtype(et))) : nullptr; } wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_TABLE - ? hide(static_cast<wasm::TableType*>(reveal(et))) + ? hide_tabletype( + static_cast<wasm::TableType*>(reveal_externtype(et))) : nullptr; } wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_MEMORY - ? hide(static_cast<wasm::MemoryType*>(reveal(et))) + ? hide_memorytype( + static_cast<wasm::MemoryType*>(reveal_externtype(et))) : nullptr; } const wasm_functype_t* wasm_externtype_as_functype_const( const wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_FUNC - ? hide(static_cast<const wasm::FuncType*>(reveal(et))) + ? hide_functype( + static_cast<const wasm::FuncType*>(reveal_externtype(et))) : nullptr; } const wasm_globaltype_t* wasm_externtype_as_globaltype_const( const wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_GLOBAL - ? hide(static_cast<const wasm::GlobalType*>(reveal(et))) + ? hide_globaltype( + static_cast<const wasm::GlobalType*>(reveal_externtype(et))) : nullptr; } const wasm_tabletype_t* wasm_externtype_as_tabletype_const( const wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_TABLE - ? hide(static_cast<const wasm::TableType*>(reveal(et))) + ? hide_tabletype( + static_cast<const wasm::TableType*>(reveal_externtype(et))) : nullptr; } const wasm_memorytype_t* wasm_externtype_as_memorytype_const( const wasm_externtype_t* et) { return et->kind() == wasm::EXTERN_MEMORY - ? hide(static_cast<const wasm::MemoryType*>(reveal(et))) + ? hide_memorytype( + static_cast<const wasm::MemoryType*>(reveal_externtype(et))) : nullptr; } @@ -2300,20 +2495,20 @@ WASM_DEFINE_TYPE(importtype, wasm::ImportType) wasm_importtype_t* wasm_importtype_new(wasm_name_t* module, wasm_name_t* name, wasm_externtype_t* type) { - return release( - wasm::ImportType::make(adopt(module), adopt(name), adopt(type))); + return release_importtype(wasm::ImportType::make( + adopt_byte_vec(module), adopt_byte_vec(name), adopt_externtype(type))); } const wasm_name_t* wasm_importtype_module(const wasm_importtype_t* it) { - return hide(it->module()); + return hide_byte_vec(it->module()); } const wasm_name_t* wasm_importtype_name(const wasm_importtype_t* it) { - return hide(it->name()); + return hide_byte_vec(it->name()); } const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t* it) { - return hide(it->type()); + return hide_externtype(it->type()); } // Export Types @@ -2322,15 +2517,16 @@ WASM_DEFINE_TYPE(exporttype, wasm::ExportType) wasm_exporttype_t* wasm_exporttype_new(wasm_name_t* name, wasm_externtype_t* type) { - return release(wasm::ExportType::make(adopt(name), adopt(type))); + return release_exporttype( + wasm::ExportType::make(adopt_byte_vec(name), adopt_externtype(type))); } const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* et) { - return hide(et->name()); + return hide_byte_vec(et->name()); } const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* et) { - return hide(et->type()); + return hide_externtype(et->type()); } /////////////////////////////////////////////////////////////////////////////// @@ -2342,7 +2538,12 @@ const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* et) { WASM_DEFINE_OWN(name, Name) \ \ wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* t) { \ - return release(t->copy()); \ + return release_##name(t->copy()); \ + } \ + \ + bool wasm_##name##_same(const wasm_##name##_t* t1, \ + const wasm_##name##_t* t2) { \ + return t1->same(t2); \ } \ \ void* wasm_##name##_get_host_info(const wasm_##name##_t* r) { \ @@ -2360,17 +2561,17 @@ const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* et) { WASM_DEFINE_REF_BASE(name, Name) \ \ wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t* r) { \ - return hide(static_cast<wasm::Ref*>(reveal(r))); \ + return hide_ref(static_cast<wasm::Ref*>(reveal_##name(r))); \ } \ wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t* r) { \ - return hide(static_cast<Name*>(reveal(r))); \ + return hide_##name(static_cast<Name*>(reveal_ref(r))); \ } \ \ const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t* r) { \ - return hide(static_cast<const wasm::Ref*>(reveal(r))); \ + return hide_ref(static_cast<const wasm::Ref*>(reveal_##name(r))); \ } \ const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t* r) { \ - return hide(static_cast<const Name*>(reveal(r))); \ + return hide_##name(static_cast<const Name*>(reveal_ref(r))); \ } #define WASM_DEFINE_SHARABLE_REF(name, Name) \ @@ -2384,11 +2585,11 @@ WASM_DEFINE_REF_BASE(ref, wasm::Ref) extern "C++" { inline auto is_empty(wasm_val_t v) -> bool { - return !is_ref(reveal(v.kind)) || !v.of.ref; + return !is_ref(reveal_valkind(v.kind)) || !v.of.ref; } -inline auto hide(wasm::Val v) -> wasm_val_t { - wasm_val_t v2 = {hide(v.kind()), {}}; +inline auto hide_val(wasm::Val v) -> wasm_val_t { + wasm_val_t v2 = {hide_valkind(v.kind()), {}}; switch (v.kind()) { case wasm::I32: v2.of.i32 = v.i32(); @@ -2404,7 +2605,7 @@ inline auto hide(wasm::Val v) -> wasm_val_t { break; case wasm::ANYREF: case wasm::FUNCREF: - v2.of.ref = hide(v.ref()); + v2.of.ref = hide_ref(v.ref()); break; default: UNREACHABLE(); @@ -2412,8 +2613,8 @@ inline auto hide(wasm::Val v) -> wasm_val_t { return v2; } -inline auto release(wasm::Val v) -> wasm_val_t { - wasm_val_t v2 = {hide(v.kind()), {}}; +inline auto release_val(wasm::Val v) -> wasm_val_t { + wasm_val_t v2 = {hide_valkind(v.kind()), {}}; switch (v.kind()) { case wasm::I32: v2.of.i32 = v.i32(); @@ -2429,7 +2630,7 @@ inline auto release(wasm::Val v) -> wasm_val_t { break; case wasm::ANYREF: case wasm::FUNCREF: - v2.of.ref = release(v.release_ref()); + v2.of.ref = release_ref(v.release_ref()); break; default: UNREACHABLE(); @@ -2437,8 +2638,8 @@ inline auto release(wasm::Val v) -> wasm_val_t { return v2; } -inline auto adopt(wasm_val_t v) -> wasm::Val { - switch (reveal(v.kind)) { +inline auto adopt_val(wasm_val_t v) -> wasm::Val { + switch (reveal_valkind(v.kind)) { case wasm::I32: return wasm::Val(v.of.i32); case wasm::I64: @@ -2449,7 +2650,7 @@ inline auto adopt(wasm_val_t v) -> wasm::Val { return wasm::Val(v.of.f64); case wasm::ANYREF: case wasm::FUNCREF: - return wasm::Val(adopt(v.of.ref)); + return wasm::Val(adopt_ref(v.of.ref)); default: UNREACHABLE(); } @@ -2460,13 +2661,13 @@ struct borrowed_val { explicit borrowed_val(wasm::Val&& v) : it(std::move(v)) {} borrowed_val(borrowed_val&& that) : it(std::move(that.it)) {} ~borrowed_val() { - if (it.is_ref()) it.release_ref(); + if (it.is_ref()) it.release_ref().release(); } }; -inline auto borrow(const wasm_val_t* v) -> borrowed_val { +inline auto borrow_val(const wasm_val_t* v) -> borrowed_val { wasm::Val v2; - switch (reveal(v->kind)) { + switch (reveal_valkind(v->kind)) { case wasm::I32: v2 = wasm::Val(v->of.i32); break; @@ -2481,7 +2682,7 @@ inline auto borrow(const wasm_val_t* v) -> borrowed_val { break; case wasm::ANYREF: case wasm::FUNCREF: - v2 = wasm::Val(adopt(v->of.ref)); + v2 = wasm::Val(adopt_ref(v->of.ref)); break; default: UNREACHABLE(); @@ -2491,15 +2692,15 @@ inline auto borrow(const wasm_val_t* v) -> borrowed_val { } // extern "C++" -WASM_DEFINE_VEC_BASE(val, wasm::Val, ) +WASM_DEFINE_VEC_BASE(val, wasm::Val, wasm::vec, ) void wasm_val_vec_new(wasm_val_vec_t* out, size_t size, wasm_val_t const data[]) { auto v2 = wasm::vec<wasm::Val>::make_uninitialized(size); for (size_t i = 0; i < v2.size(); ++i) { - v2[i] = adopt(data[i]); + v2[i] = adopt_val(data[i]); } - *out = release(std::move(v2)); + *out = release_val_vec(std::move(v2)); } void wasm_val_vec_copy(wasm_val_vec_t* out, wasm_val_vec_t* v) { @@ -2507,36 +2708,70 @@ void wasm_val_vec_copy(wasm_val_vec_t* out, wasm_val_vec_t* v) { for (size_t i = 0; i < v2.size(); ++i) { wasm_val_t val; wasm_val_copy(&v->data[i], &val); - v2[i] = adopt(val); + v2[i] = adopt_val(val); } - *out = release(std::move(v2)); + *out = release_val_vec(std::move(v2)); } void wasm_val_delete(wasm_val_t* v) { - if (is_ref(reveal(v->kind))) adopt(v->of.ref); + if (is_ref(reveal_valkind(v->kind))) { + adopt_ref(v->of.ref); + } } void wasm_val_copy(wasm_val_t* out, const wasm_val_t* v) { *out = *v; - if (is_ref(reveal(v->kind))) { - out->of.ref = release(v->of.ref->copy()); + if (is_ref(reveal_valkind(v->kind))) { + out->of.ref = v->of.ref ? release_ref(v->of.ref->copy()) : nullptr; } } /////////////////////////////////////////////////////////////////////////////// // Runtime Objects +// Frames + +WASM_DEFINE_OWN(frame, wasm::Frame) +WASM_DEFINE_VEC_OWN(frame, wasm::Frame) + +wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) { + return release_frame(frame->copy()); +} + +wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame); +// Defined below along with wasm_instance_t. + +uint32_t wasm_frame_func_index(const wasm_frame_t* frame) { + return reveal_frame(frame)->func_index(); +} + +size_t wasm_frame_func_offset(const wasm_frame_t* frame) { + return reveal_frame(frame)->func_offset(); +} + +size_t wasm_frame_module_offset(const wasm_frame_t* frame) { + return reveal_frame(frame)->module_offset(); +} + // Traps WASM_DEFINE_REF(trap, wasm::Trap) wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* message) { - auto message_ = borrow(message); - return release(wasm::Trap::make(store, message_.it)); + auto message_ = borrow_byte_vec(message); + return release_trap(wasm::Trap::make(store, message_.it)); } void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) { - *out = release(reveal(trap)->message()); + *out = release_byte_vec(reveal_trap(trap)->message()); +} + +wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { + return release_frame(reveal_trap(trap)->origin()); +} + +void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) { + *out = release_frame_vec(reveal_trap(trap)->trace()); } // Foreign Objects @@ -2544,7 +2779,7 @@ void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) { WASM_DEFINE_REF(foreign, wasm::Foreign) wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { - return release(wasm::Foreign::make(store)); + return release_foreign(wasm::Foreign::make(store)); } // Modules @@ -2552,43 +2787,43 @@ wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { WASM_DEFINE_SHARABLE_REF(module, wasm::Module) bool wasm_module_validate(wasm_store_t* store, const wasm_byte_vec_t* binary) { - auto binary_ = borrow(binary); + auto binary_ = borrow_byte_vec(binary); return wasm::Module::validate(store, binary_.it); } wasm_module_t* wasm_module_new(wasm_store_t* store, const wasm_byte_vec_t* binary) { - auto binary_ = borrow(binary); - return release(wasm::Module::make(store, binary_.it)); + auto binary_ = borrow_byte_vec(binary); + return release_module(wasm::Module::make(store, binary_.it)); } void wasm_module_imports(const wasm_module_t* module, wasm_importtype_vec_t* out) { - *out = release(reveal(module)->imports()); + *out = release_importtype_vec(reveal_module(module)->imports()); } void wasm_module_exports(const wasm_module_t* module, wasm_exporttype_vec_t* out) { - *out = release(reveal(module)->exports()); + *out = release_exporttype_vec(reveal_module(module)->exports()); } void wasm_module_serialize(const wasm_module_t* module, wasm_byte_vec_t* out) { - *out = release(reveal(module)->serialize()); + *out = release_byte_vec(reveal_module(module)->serialize()); } wasm_module_t* wasm_module_deserialize(wasm_store_t* store, const wasm_byte_vec_t* binary) { - auto binary_ = borrow(binary); - return release(wasm::Module::deserialize(store, binary_.it)); + auto binary_ = borrow_byte_vec(binary); + return release_module(wasm::Module::deserialize(store, binary_.it)); } wasm_shared_module_t* wasm_module_share(const wasm_module_t* module) { - return release(reveal(module)->share()); + return release_shared_module(reveal_module(module)->share()); } wasm_module_t* wasm_module_obtain(wasm_store_t* store, const wasm_shared_module_t* shared) { - return release(wasm::Module::obtain(store, shared)); + return release_module(wasm::Module::obtain(store, shared)); } // Function Instances @@ -2598,9 +2833,9 @@ WASM_DEFINE_REF(func, wasm::Func) extern "C++" { auto wasm_callback(void* env, const wasm::Val args[], wasm::Val results[]) - -> wasm::own<wasm::Trap*> { + -> wasm::own<wasm::Trap> { auto f = reinterpret_cast<wasm_func_callback_t>(env); - return adopt(f(hide(args), hide(results))); + return adopt_trap(f(hide_val_vec(args), hide_val_vec(results))); } struct wasm_callback_env_t { @@ -2610,9 +2845,10 @@ struct wasm_callback_env_t { }; auto wasm_callback_with_env(void* env, const wasm::Val args[], - wasm::Val results[]) -> wasm::own<wasm::Trap*> { + wasm::Val results[]) -> wasm::own<wasm::Trap> { auto t = static_cast<wasm_callback_env_t*>(env); - return adopt(t->callback(t->env, hide(args), hide(results))); + return adopt_trap( + t->callback(t->env, hide_val_vec(args), hide_val_vec(results))); } void wasm_callback_env_finalizer(void* env) { @@ -2625,8 +2861,8 @@ void wasm_callback_env_finalizer(void* env) { wasm_func_t* wasm_func_new(wasm_store_t* store, const wasm_functype_t* type, wasm_func_callback_t callback) { - return release(wasm::Func::make(store, type, wasm_callback, - reinterpret_cast<void*>(callback))); + return release_func(wasm::Func::make(store, type, wasm_callback, + reinterpret_cast<void*>(callback))); } wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, @@ -2634,12 +2870,12 @@ wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, wasm_func_callback_with_env_t callback, void* env, void (*finalizer)(void*)) { auto env2 = new wasm_callback_env_t{callback, env, finalizer}; - return release(wasm::Func::make(store, type, wasm_callback_with_env, env2, - wasm_callback_env_finalizer)); + return release_func(wasm::Func::make(store, type, wasm_callback_with_env, + env2, wasm_callback_env_finalizer)); } wasm_functype_t* wasm_func_type(const wasm_func_t* func) { - return release(func->type()); + return release_functype(func->type()); } size_t wasm_func_param_arity(const wasm_func_t* func) { @@ -2652,7 +2888,8 @@ size_t wasm_func_result_arity(const wasm_func_t* func) { wasm_trap_t* wasm_func_call(const wasm_func_t* func, const wasm_val_t args[], wasm_val_t results[]) { - return release(func->call(reveal(args), reveal(results))); + return release_trap( + func->call(reveal_val_vec(args), reveal_val_vec(results))); } // Global Instances @@ -2662,20 +2899,20 @@ WASM_DEFINE_REF(global, wasm::Global) wasm_global_t* wasm_global_new(wasm_store_t* store, const wasm_globaltype_t* type, const wasm_val_t* val) { - auto val_ = borrow(val); - return release(wasm::Global::make(store, type, val_.it)); + auto val_ = borrow_val(val); + return release_global(wasm::Global::make(store, type, val_.it)); } wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { - return release(global->type()); + return release_globaltype(global->type()); } void wasm_global_get(const wasm_global_t* global, wasm_val_t* out) { - *out = release(global->get()); + *out = release_val(global->get()); } void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { - auto val_ = borrow(val); + auto val_ = borrow_val(val); global->set(val_.it); } @@ -2685,15 +2922,15 @@ WASM_DEFINE_REF(table, wasm::Table) wasm_table_t* wasm_table_new(wasm_store_t* store, const wasm_tabletype_t* type, wasm_ref_t* ref) { - return release(wasm::Table::make(store, type, ref)); + return release_table(wasm::Table::make(store, type, ref)); } wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { - return release(table->type()); + return release_tabletype(table->type()); } wasm_ref_t* wasm_table_get(const wasm_table_t* table, wasm_table_size_t index) { - return release(table->get(index)); + return release_ref(table->get(index)); } bool wasm_table_set(wasm_table_t* table, wasm_table_size_t index, @@ -2716,11 +2953,11 @@ WASM_DEFINE_REF(memory, wasm::Memory) wasm_memory_t* wasm_memory_new(wasm_store_t* store, const wasm_memorytype_t* type) { - return release(wasm::Memory::make(store, type)); + return release_memory(wasm::Memory::make(store, type)); } wasm_memorytype_t* wasm_memory_type(const wasm_memory_t* memory) { - return release(memory->type()); + return release_memorytype(memory->type()); } wasm_byte_t* wasm_memory_data(wasm_memory_t* memory) { return memory->data(); } @@ -2740,67 +2977,67 @@ bool wasm_memory_grow(wasm_memory_t* memory, wasm_memory_pages_t delta) { // Externals WASM_DEFINE_REF(extern, wasm::Extern) -WASM_DEFINE_VEC(extern, wasm::Extern, *) +WASM_DEFINE_VEC_OWN(extern, wasm::Extern) wasm_externkind_t wasm_extern_kind(const wasm_extern_t* external) { - return hide(external->kind()); + return hide_externkind(external->kind()); } wasm_externtype_t* wasm_extern_type(const wasm_extern_t* external) { - return release(external->type()); + return release_externtype(external->type()); } wasm_extern_t* wasm_func_as_extern(wasm_func_t* func) { - return hide(static_cast<wasm::Extern*>(reveal(func))); + return hide_extern(static_cast<wasm::Extern*>(reveal_func(func))); } wasm_extern_t* wasm_global_as_extern(wasm_global_t* global) { - return hide(static_cast<wasm::Extern*>(reveal(global))); + return hide_extern(static_cast<wasm::Extern*>(reveal_global(global))); } wasm_extern_t* wasm_table_as_extern(wasm_table_t* table) { - return hide(static_cast<wasm::Extern*>(reveal(table))); + return hide_extern(static_cast<wasm::Extern*>(reveal_table(table))); } wasm_extern_t* wasm_memory_as_extern(wasm_memory_t* memory) { - return hide(static_cast<wasm::Extern*>(reveal(memory))); + return hide_extern(static_cast<wasm::Extern*>(reveal_memory(memory))); } const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t* func) { - return hide(static_cast<const wasm::Extern*>(reveal(func))); + return hide_extern(static_cast<const wasm::Extern*>(reveal_func(func))); } const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t* global) { - return hide(static_cast<const wasm::Extern*>(reveal(global))); + return hide_extern(static_cast<const wasm::Extern*>(reveal_global(global))); } const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t* table) { - return hide(static_cast<const wasm::Extern*>(reveal(table))); + return hide_extern(static_cast<const wasm::Extern*>(reveal_table(table))); } const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t* memory) { - return hide(static_cast<const wasm::Extern*>(reveal(memory))); + return hide_extern(static_cast<const wasm::Extern*>(reveal_memory(memory))); } wasm_func_t* wasm_extern_as_func(wasm_extern_t* external) { - return hide(external->func()); + return hide_func(external->func()); } wasm_global_t* wasm_extern_as_global(wasm_extern_t* external) { - return hide(external->global()); + return hide_global(external->global()); } wasm_table_t* wasm_extern_as_table(wasm_extern_t* external) { - return hide(external->table()); + return hide_table(external->table()); } wasm_memory_t* wasm_extern_as_memory(wasm_extern_t* external) { - return hide(external->memory()); + return hide_memory(external->memory()); } const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t* external) { - return hide(external->func()); + return hide_func(external->func()); } const wasm_global_t* wasm_extern_as_global_const( const wasm_extern_t* external) { - return hide(external->global()); + return hide_global(external->global()); } const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t* external) { - return hide(external->table()); + return hide_table(external->table()); } const wasm_memory_t* wasm_extern_as_memory_const( const wasm_extern_t* external) { - return hide(external->memory()); + return hide_memory(external->memory()); } // Module Instances @@ -2809,20 +3046,29 @@ WASM_DEFINE_REF(instance, wasm::Instance) wasm_instance_t* wasm_instance_new(wasm_store_t* store, const wasm_module_t* module, - const wasm_extern_t* const imports[]) { - return release(wasm::Instance::make( - store, module, reinterpret_cast<const wasm::Extern* const*>(imports))); + const wasm_extern_t* const imports[], + wasm_trap_t** trap) { + wasm::own<wasm::Trap> error; + wasm_instance_t* instance = release_instance(wasm::Instance::make( + store, module, reinterpret_cast<const wasm::Extern* const*>(imports), + &error)); + if (trap) *trap = hide_trap(error.release()); + return instance; } void wasm_instance_exports(const wasm_instance_t* instance, wasm_extern_vec_t* out) { - *out = release(instance->exports()); + *out = release_extern_vec(instance->exports()); +} + +wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) { + return hide_instance(reveal_frame(frame)->instance()); } #undef WASM_DEFINE_OWN #undef WASM_DEFINE_VEC_BASE #undef WASM_DEFINE_VEC_PLAIN -#undef WASM_DEFINE_VEC +#undef WASM_DEFINE_VEC_OWN #undef WASM_DEFINE_TYPE #undef WASM_DEFINE_REF_BASE #undef WASM_DEFINE_REF diff --git a/chromium/v8/src/wasm/c-api.h b/chromium/v8/src/wasm/c-api.h index c1a914a16eb..43a0fb73b2d 100644 --- a/chromium/v8/src/wasm/c-api.h +++ b/chromium/v8/src/wasm/c-api.h @@ -7,8 +7,17 @@ #include "include/v8.h" #include "src/common/globals.h" +#include "src/handles/handles.h" #include "third_party/wasm-api/wasm.hh" +namespace v8 { +namespace internal { + +class JSWeakMap; + +} // namespace internal +} // namespace v8 + namespace wasm { class StoreImpl { @@ -27,14 +36,19 @@ class StoreImpl { reinterpret_cast<v8::Isolate*>(isolate)->GetData(0)); } + void SetHostInfo(i::Handle<i::Object> object, void* info, + void (*finalizer)(void*)); + void* GetHostInfo(i::Handle<i::Object> key); + private: - friend own<Store*> Store::make(Engine*); + friend own<Store> Store::make(Engine*); StoreImpl() {} v8::Isolate::CreateParams create_params_; v8::Isolate* isolate_ = nullptr; v8::Eternal<v8::Context> context_; + i::Handle<i::JSWeakMap> host_info_map_; }; } // namespace wasm diff --git a/chromium/v8/src/wasm/function-body-decoder-impl.h b/chromium/v8/src/wasm/function-body-decoder-impl.h index 9f1ca23c623..582934e19f0 100644 --- a/chromium/v8/src/wasm/function-body-decoder-impl.h +++ b/chromium/v8/src/wasm/function-body-decoder-impl.h @@ -339,35 +339,6 @@ struct BranchOnExceptionImmediate { }; template <Decoder::ValidateFlag validate> -struct CallIndirectImmediate { - uint32_t table_index; - uint32_t sig_index; - FunctionSig* sig = nullptr; - uint32_t length = 0; - inline CallIndirectImmediate(const WasmFeatures enabled, Decoder* decoder, - const byte* pc) { - uint32_t len = 0; - sig_index = decoder->read_u32v<validate>(pc + 1, &len, "signature index"); - table_index = decoder->read_u8<validate>(pc + 1 + len, "table index"); - if (!VALIDATE(table_index == 0 || enabled.anyref)) { - decoder->errorf(pc + 1 + len, "expected table index 0, found %u", - table_index); - } - length = 1 + len; - } -}; - -template <Decoder::ValidateFlag validate> -struct CallFunctionImmediate { - uint32_t index; - FunctionSig* sig = nullptr; - uint32_t length; - inline CallFunctionImmediate(Decoder* decoder, const byte* pc) { - index = decoder->read_u32v<validate>(pc + 1, &length, "function index"); - } -}; - -template <Decoder::ValidateFlag validate> struct FunctionIndexImmediate { uint32_t index = 0; uint32_t length = 1; @@ -395,7 +366,37 @@ struct TableIndexImmediate { unsigned length = 1; inline TableIndexImmediate() = default; inline TableIndexImmediate(Decoder* decoder, const byte* pc) { - index = decoder->read_u8<validate>(pc + 1, "table index"); + index = decoder->read_u32v<validate>(pc + 1, &length, "table index"); + } +}; + +template <Decoder::ValidateFlag validate> +struct CallIndirectImmediate { + uint32_t table_index; + uint32_t sig_index; + FunctionSig* sig = nullptr; + uint32_t length = 0; + inline CallIndirectImmediate(const WasmFeatures enabled, Decoder* decoder, + const byte* pc) { + uint32_t len = 0; + sig_index = decoder->read_u32v<validate>(pc + 1, &len, "signature index"); + TableIndexImmediate<validate> table(decoder, pc + len); + if (!VALIDATE((table.index == 0 && table.length == 1) || enabled.anyref)) { + decoder->errorf(pc + 1 + len, "expected table index 0, found %u", + table.index); + } + table_index = table.index; + length = len + table.length; + } +}; + +template <Decoder::ValidateFlag validate> +struct CallFunctionImmediate { + uint32_t index; + FunctionSig* sig = nullptr; + uint32_t length; + inline CallFunctionImmediate(Decoder* decoder, const byte* pc) { + index = decoder->read_u32v<validate>(pc + 1, &length, "function index"); } }; @@ -748,8 +749,6 @@ struct ControlBase { F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \ F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \ const Vector<Value> inputs, Value* result) \ - F(SimdShiftOp, WasmOpcode opcode, const SimdShiftImmediate<validate>& imm, \ - const Value& input, Value* result) \ F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \ const Value& input0, const Value& input1, Value* result) \ F(Throw, const ExceptionIndexImmediate<validate>& imm, \ @@ -849,7 +848,9 @@ class WasmDecoder : public Decoder { type = kWasmAnyRef; break; } - decoder->error(decoder->pc() - 1, "invalid local type"); + decoder->error(decoder->pc() - 1, + "invalid local type 'anyref', enable with " + "--experimental-wasm-anyref"); return false; case kLocalFuncRef: if (enabled.anyref) { @@ -857,7 +858,7 @@ class WasmDecoder : public Decoder { break; } decoder->error(decoder->pc() - 1, - "local type 'funcref' is not enabled with " + "invalid local type 'funcref', enable with " "--experimental-wasm-anyref"); return false; case kLocalExnRef: @@ -865,14 +866,19 @@ class WasmDecoder : public Decoder { type = kWasmExnRef; break; } - decoder->error(decoder->pc() - 1, "invalid local type"); + decoder->error(decoder->pc() - 1, + "invalid local type 'exception ref', enable with " + "--experimental-wasm-eh"); return false; case kLocalS128: if (enabled.simd) { type = kWasmS128; break; } - V8_FALLTHROUGH; + decoder->error(decoder->pc() - 1, + "invalid local type 'Simd128', enable with " + "--experimental-wasm-simd"); + return false; default: decoder->error(decoder->pc() - 1, "invalid local type"); return false; @@ -2666,16 +2672,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { return imm.length; } - uint32_t SimdShiftOp(WasmOpcode opcode) { - SimdShiftImmediate<validate> imm(this, this->pc_); - if (this->Validate(this->pc_, opcode, imm)) { - auto input = Pop(0, kWasmS128); - auto* result = Push(kWasmS128); - CALL_INTERFACE_IF_REACHABLE(SimdShiftOp, opcode, imm, input, result); - } - return imm.length; - } - uint32_t Simd8x16ShuffleOp() { Simd8x16ShuffleImmediate<validate> imm(this, this->pc_); if (this->Validate(this->pc_, imm)) { @@ -2727,21 +2723,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { len = SimdReplaceLane(opcode, kWasmI32); break; } - case kExprI64x2Shl: - case kExprI64x2ShrS: - case kExprI64x2ShrU: - case kExprI32x4Shl: - case kExprI32x4ShrS: - case kExprI32x4ShrU: - case kExprI16x8Shl: - case kExprI16x8ShrS: - case kExprI16x8ShrU: - case kExprI8x16Shl: - case kExprI8x16ShrS: - case kExprI8x16ShrU: { - len = SimdShiftOp(opcode); - break; - } case kExprS8x16Shuffle: { len = Simd8x16ShuffleOp(); break; diff --git a/chromium/v8/src/wasm/function-compiler.cc b/chromium/v8/src/wasm/function-compiler.cc index 7df5abf5c87..4940134d53c 100644 --- a/chromium/v8/src/wasm/function-compiler.cc +++ b/chromium/v8/src/wasm/function-compiler.cc @@ -262,21 +262,18 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate, } } -JSToWasmWrapperCompilationUnit::JSToWasmWrapperCompilationUnit(Isolate* isolate, - FunctionSig* sig, - bool is_import) - : job_(compiler::NewJSToWasmCompilationJob(isolate, sig, is_import)) {} +JSToWasmWrapperCompilationUnit::JSToWasmWrapperCompilationUnit( + Isolate* isolate, WasmEngine* wasm_engine, FunctionSig* sig, bool is_import, + const WasmFeatures& enabled_features) + : is_import_(is_import), + sig_(sig), + job_(compiler::NewJSToWasmCompilationJob(isolate, wasm_engine, sig, + is_import, enabled_features)) {} JSToWasmWrapperCompilationUnit::~JSToWasmWrapperCompilationUnit() = default; -void JSToWasmWrapperCompilationUnit::Prepare(Isolate* isolate) { - CompilationJob::Status status = job_->PrepareJob(isolate); - CHECK_EQ(status, CompilationJob::SUCCEEDED); -} - void JSToWasmWrapperCompilationUnit::Execute() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "CompileJSToWasmWrapper"); - DCHECK_EQ(job_->state(), CompilationJob::State::kReadyToExecute); CompilationJob::Status status = job_->ExecuteJob(); CHECK_EQ(status, CompilationJob::SUCCEEDED); } @@ -296,8 +293,9 @@ Handle<Code> JSToWasmWrapperCompilationUnit::Finalize(Isolate* isolate) { Handle<Code> JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper( Isolate* isolate, FunctionSig* sig, bool is_import) { // Run the compilation unit synchronously. - JSToWasmWrapperCompilationUnit unit(isolate, sig, is_import); - unit.Prepare(isolate); + WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate); + JSToWasmWrapperCompilationUnit unit(isolate, isolate->wasm_engine(), sig, + is_import, enabled_features); unit.Execute(); return unit.Finalize(isolate); } diff --git a/chromium/v8/src/wasm/function-compiler.h b/chromium/v8/src/wasm/function-compiler.h index d0b47b91aa8..2da028a047e 100644 --- a/chromium/v8/src/wasm/function-compiler.h +++ b/chromium/v8/src/wasm/function-compiler.h @@ -108,19 +108,24 @@ STATIC_ASSERT(sizeof(WasmCompilationUnit) <= 2 * kSystemPointerSize); class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final { public: - JSToWasmWrapperCompilationUnit(Isolate* isolate, FunctionSig* sig, - bool is_import); + JSToWasmWrapperCompilationUnit(Isolate* isolate, WasmEngine* wasm_engine, + FunctionSig* sig, bool is_import, + const WasmFeatures& enabled_features); ~JSToWasmWrapperCompilationUnit(); - void Prepare(Isolate* isolate); void Execute(); Handle<Code> Finalize(Isolate* isolate); + bool is_import() const { return is_import_; } + FunctionSig* sig() const { return sig_; } + // Run a compilation unit synchronously. static Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, FunctionSig* sig, bool is_import); private: + bool is_import_; + FunctionSig* sig_; std::unique_ptr<OptimizedCompilationJob> job_; }; diff --git a/chromium/v8/src/wasm/graph-builder-interface.cc b/chromium/v8/src/wasm/graph-builder-interface.cc index 8efac18787e..923e1154ea0 100644 --- a/chromium/v8/src/wasm/graph-builder-interface.cc +++ b/chromium/v8/src/wasm/graph-builder-interface.cc @@ -258,8 +258,8 @@ class WasmGraphBuildingInterface { void Drop(FullDecoder* decoder, const Value& value) {} void DoReturn(FullDecoder* decoder, Vector<Value> values) { - TFNode** nodes = GetNodes(values); - BUILD(Return, static_cast<uint32_t>(values.size()), nodes); + Vector<TFNode*> nodes = GetNodes(values); + BUILD(Return, nodes); } void GetLocal(FullDecoder* decoder, Value* result, @@ -319,10 +319,10 @@ class WasmGraphBuildingInterface { void BrOrRet(FullDecoder* decoder, uint32_t depth) { if (depth == decoder->control_depth() - 1) { uint32_t ret_count = static_cast<uint32_t>(decoder->sig_->return_count()); - TFNode** values = - ret_count == 0 ? nullptr + Vector<TFNode*> values = + ret_count == 0 ? Vector<TFNode*>{} : GetNodes(decoder->stack_value(ret_count), ret_count); - BUILD(Return, ret_count, values); + BUILD(Return, values); } else { Br(decoder, decoder->control_at(depth)); } @@ -431,23 +431,16 @@ class WasmGraphBuildingInterface { void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args, Value* result) { - TFNode** inputs = GetNodes(args); - TFNode* node = BUILD(SimdOp, opcode, inputs); + Vector<TFNode*> inputs = GetNodes(args); + TFNode* node = BUILD(SimdOp, opcode, inputs.begin()); if (result) result->node = node; } void SimdLaneOp(FullDecoder* decoder, WasmOpcode opcode, const SimdLaneImmediate<validate> imm, Vector<Value> inputs, Value* result) { - TFNode** nodes = GetNodes(inputs); - result->node = BUILD(SimdLaneOp, opcode, imm.lane, nodes); - } - - void SimdShiftOp(FullDecoder* decoder, WasmOpcode opcode, - const SimdShiftImmediate<validate> imm, const Value& input, - Value* result) { - TFNode* inputs[] = {input.node}; - result->node = BUILD(SimdShiftOp, opcode, imm.shift, inputs); + Vector<TFNode*> nodes = GetNodes(inputs); + result->node = BUILD(SimdLaneOp, opcode, imm.lane, nodes.begin()); } void Simd8x16ShuffleOp(FullDecoder* decoder, @@ -495,7 +488,7 @@ class WasmGraphBuildingInterface { SetEnv(if_match_env); // TODO(mstarzinger): Can't use BUILD() here, GetExceptionValues() returns // TFNode** rather than TFNode*. Fix to add landing pads. - TFNode** caught_values = + Vector<TFNode*> caught_values = builder_->GetExceptionValues(exception.node, imm.exception); for (size_t i = 0, e = values.size(); i < e; ++i) { values[i].node = caught_values[i]; @@ -526,9 +519,9 @@ class WasmGraphBuildingInterface { void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args, const MemoryAccessImmediate<validate>& imm, Value* result) { - TFNode** inputs = GetNodes(args); - TFNode* node = BUILD(AtomicOp, opcode, inputs, imm.alignment, imm.offset, - decoder->position()); + Vector<TFNode*> inputs = GetNodes(args); + TFNode* node = BUILD(AtomicOp, opcode, inputs.begin(), imm.alignment, + imm.offset, decoder->position()); if (result) result->node = node; } @@ -598,15 +591,15 @@ class WasmGraphBuildingInterface { ->try_info; } - TFNode** GetNodes(Value* values, size_t count) { - TFNode** nodes = builder_->Buffer(count); + Vector<TFNode*> GetNodes(Value* values, size_t count) { + Vector<TFNode*> nodes = builder_->Buffer(count); for (size_t i = 0; i < count; ++i) { nodes[i] = values[i].node; } return nodes; } - TFNode** GetNodes(Vector<Value> values) { + Vector<TFNode*> GetNodes(Vector<Value> values) { return GetNodes(values.begin(), values.size()); } @@ -885,17 +878,17 @@ class WasmGraphBuildingInterface { FunctionSig* sig, uint32_t sig_index, const Value args[], Value returns[]) { int param_count = static_cast<int>(sig->parameter_count()); - TFNode** arg_nodes = builder_->Buffer(param_count + 1); + Vector<TFNode*> arg_nodes = builder_->Buffer(param_count + 1); TFNode** return_nodes = nullptr; arg_nodes[0] = index_node; for (int i = 0; i < param_count; ++i) { arg_nodes[i + 1] = args[i].node; } if (index_node) { - BUILD(CallIndirect, table_index, sig_index, arg_nodes, &return_nodes, - decoder->position()); + BUILD(CallIndirect, table_index, sig_index, arg_nodes.begin(), + &return_nodes, decoder->position()); } else { - BUILD(CallDirect, sig_index, arg_nodes, &return_nodes, + BUILD(CallDirect, sig_index, arg_nodes.begin(), &return_nodes, decoder->position()); } int return_count = static_cast<int>(sig->return_count()); @@ -911,16 +904,16 @@ class WasmGraphBuildingInterface { TFNode* index_node, FunctionSig* sig, uint32_t sig_index, const Value args[]) { int arg_count = static_cast<int>(sig->parameter_count()); - TFNode** arg_nodes = builder_->Buffer(arg_count + 1); + Vector<TFNode*> arg_nodes = builder_->Buffer(arg_count + 1); arg_nodes[0] = index_node; for (int i = 0; i < arg_count; ++i) { arg_nodes[i + 1] = args[i].node; } if (index_node) { - BUILD(ReturnCallIndirect, table_index, sig_index, arg_nodes, + BUILD(ReturnCallIndirect, table_index, sig_index, arg_nodes.begin(), decoder->position()); } else { - BUILD(ReturnCall, sig_index, arg_nodes, decoder->position()); + BUILD(ReturnCall, sig_index, arg_nodes.begin(), decoder->position()); } } }; diff --git a/chromium/v8/src/wasm/jump-table-assembler.h b/chromium/v8/src/wasm/jump-table-assembler.h index 379a547b559..8889c18e9c5 100644 --- a/chromium/v8/src/wasm/jump-table-assembler.h +++ b/chromium/v8/src/wasm/jump-table-assembler.h @@ -37,6 +37,21 @@ namespace wasm { // The above illustrates jump table lines {Li} containing slots {Si} with each // line containing {n} slots and some padding {x} for alignment purposes. // Other jump tables are just consecutive. +// +// The main jump table will be patched concurrently while other threads execute +// it. The code at the new target might also have been emitted concurrently, so +// we need to ensure that there is proper synchronization between code emission, +// jump table patching and code execution. +// On Intel platforms, this all works out of the box because there is cache +// coherency between i-cache and d-cache. +// On ARM, it is safe because the i-cache flush after code emission executes an +// "ic ivau" (Instruction Cache line Invalidate by Virtual Address to Point of +// Unification), which broadcasts to all cores. A core which sees the jump table +// update thus also sees the new code. Since the other core does not explicitly +// execute an "isb" (Instruction Synchronization Barrier), it might still +// execute the old code afterwards, which is no problem, since that code remains +// available until it is garbage collected. Garbage collection itself is a +// synchronization barrier though. class V8_EXPORT_PRIVATE JumpTableAssembler : public MacroAssembler { public: // Translate an offset into the continuous jump table to a jump table index. diff --git a/chromium/v8/src/wasm/module-compiler.cc b/chromium/v8/src/wasm/module-compiler.cc index b5a58d4f273..c264bac96e8 100644 --- a/chromium/v8/src/wasm/module-compiler.cc +++ b/chromium/v8/src/wasm/module-compiler.cc @@ -381,7 +381,7 @@ class CompilationStateImpl { // Initialize compilation progress. Set compilation tiers to expect for // baseline and top tier compilation. Must be set before {AddCompilationUnits} // is invoked which triggers background compilation. - void InitializeCompilationProgress(bool lazy_module, int num_import_wrappers); + void InitializeCompilationProgress(bool lazy_module, int num_wrappers); // Add the callback function to be called on compilation events. Needs to be // set before {AddCompilationUnits} is run to ensure that it receives all @@ -389,13 +389,24 @@ class CompilationStateImpl { void AddCallback(CompilationState::callback_t); // Inserts new functions to compile and kicks off compilation. - void AddCompilationUnits(Vector<WasmCompilationUnit> baseline_units, - Vector<WasmCompilationUnit> top_tier_units); + void AddCompilationUnits( + Vector<WasmCompilationUnit> baseline_units, + Vector<WasmCompilationUnit> top_tier_units, + Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> + js_to_wasm_wrapper_units); void AddTopTierCompilationUnit(WasmCompilationUnit); base::Optional<WasmCompilationUnit> GetNextCompilationUnit( int task_id, CompileBaselineOnly baseline_only); + std::shared_ptr<JSToWasmWrapperCompilationUnit> + GetNextJSToWasmWrapperCompilationUnit(); + void FinalizeJSToWasmWrappers(Isolate* isolate, const WasmModule* module, + Handle<FixedArray>* export_wrappers_out); + void OnFinishedUnits(Vector<WasmCode*>); + void OnFinishedJSToWasmWrapperUnits(int num); + void TriggerCallbacks(bool completes_baseline_compilation, + bool completes_top_tier_compilation); void OnBackgroundTaskStopped(int task_id, const WasmFeatures& detected); void UpdateDetectedFeatures(const WasmFeatures& detected); @@ -483,6 +494,13 @@ class CompilationStateImpl { // tasks a fair chance to utilize the worker threads on a regular basis. std::atomic<double> next_compilation_deadline_{0}; + // Index of the next wrapper to compile in {js_to_wasm_wrapper_units_}. + std::atomic<int> js_to_wasm_wrapper_id_{0}; + // Wrapper compilation units are stored in shared_ptrs so that they are kept + // alive by the tasks even if the NativeModule dies. + std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> + js_to_wasm_wrapper_units_; + // This mutex protects all information of this {CompilationStateImpl} which is // being accessed concurrently. mutable base::Mutex mutex_; @@ -524,9 +542,9 @@ class CompilationStateImpl { ////////////////////////////////////////////////////////////////////////////// // Encoding of fields in the {compilation_progress_} vector. - class RequiredBaselineTierField : public BitField8<ExecutionTier, 0, 2> {}; - class RequiredTopTierField : public BitField8<ExecutionTier, 2, 2> {}; - class ReachedTierField : public BitField8<ExecutionTier, 4, 2> {}; + using RequiredBaselineTierField = BitField8<ExecutionTier, 0, 2>; + using RequiredTopTierField = BitField8<ExecutionTier, 2, 2>; + using ReachedTierField = BitField8<ExecutionTier, 4, 2>; }; CompilationStateImpl* Impl(CompilationState* compilation_state) { @@ -711,6 +729,11 @@ class CompilationUnitBuilder { } } + void AddJSToWasmWrapperUnit( + std::shared_ptr<JSToWasmWrapperCompilationUnit> unit) { + js_to_wasm_wrapper_units_.emplace_back(std::move(unit)); + } + void AddTopTierUnit(int func_index) { ExecutionTierPair tiers = GetRequestedExecutionTiers( native_module_->module(), compilation_state()->compile_mode(), @@ -729,9 +752,13 @@ class CompilationUnitBuilder { } bool Commit() { - if (baseline_units_.empty() && tiering_units_.empty()) return false; - compilation_state()->AddCompilationUnits(VectorOf(baseline_units_), - VectorOf(tiering_units_)); + if (baseline_units_.empty() && tiering_units_.empty() && + js_to_wasm_wrapper_units_.empty()) { + return false; + } + compilation_state()->AddCompilationUnits( + VectorOf(baseline_units_), VectorOf(tiering_units_), + VectorOf(js_to_wasm_wrapper_units_)); Clear(); return true; } @@ -739,6 +766,7 @@ class CompilationUnitBuilder { void Clear() { baseline_units_.clear(); tiering_units_.clear(); + js_to_wasm_wrapper_units_.clear(); } private: @@ -750,6 +778,8 @@ class CompilationUnitBuilder { const ExecutionTier default_tier_; std::vector<WasmCompilationUnit> baseline_units_; std::vector<WasmCompilationUnit> tiering_units_; + std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> + js_to_wasm_wrapper_units_; }; void SetCompileError(ErrorThrower* thrower, ModuleWireBytes wire_bytes, @@ -909,6 +939,33 @@ void RecordStats(const Code code, Counters* counters) { constexpr int kMainThreadTaskId = -1; +bool ExecuteJSToWasmWrapperCompilationUnits( + const std::shared_ptr<BackgroundCompileToken>& token) { + std::shared_ptr<JSToWasmWrapperCompilationUnit> wrapper_unit = nullptr; + int num_processed_wrappers = 0; + do { + // TODO(thibaudm): Reschedule the compilation task if it takes too long, so + // that the background thread is not blocked. + { + BackgroundCompileScope compile_scope(token); + if (compile_scope.cancelled()) return false; + wrapper_unit = compile_scope.compilation_state() + ->GetNextJSToWasmWrapperCompilationUnit(); + } + if (wrapper_unit) { + wrapper_unit->Execute(); + ++num_processed_wrappers; + } + } while (wrapper_unit); + { + BackgroundCompileScope compile_scope(token); + if (compile_scope.cancelled()) return false; + compile_scope.compilation_state()->OnFinishedJSToWasmWrapperUnits( + num_processed_wrappers); + } + return true; +} + // Run by the main thread and background tasks to take part in compilation. // Returns whether any units were executed. bool ExecuteCompilationUnits( @@ -917,6 +974,13 @@ bool ExecuteCompilationUnits( TRACE_COMPILE("Compiling (task %d)...\n", task_id); TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "ExecuteCompilationUnits"); + // Execute JS to WASM wrapper units first, so that they are ready to be + // finalized by the main thread when the kFinishedBaselineCompilation event is + // triggered. + if (!ExecuteJSToWasmWrapperCompilationUnits(token)) { + return false; + } + const bool is_foreground = task_id == kMainThreadTaskId; // The main thread uses task id 0, which might collide with one of the // background tasks. This is fine, as it will only cause some contention on @@ -1049,6 +1113,35 @@ bool ExecuteCompilationUnits( return true; } +using JSToWasmWrapperKey = std::pair<bool, FunctionSig>; + +// Returns the number of units added. +int AddExportWrapperUnits(Isolate* isolate, WasmEngine* wasm_engine, + NativeModule* native_module, + CompilationUnitBuilder* builder, + const WasmFeatures& enabled_features) { +// Disable asynchronous wrapper compilation when builtins are not embedded, +// otherwise the isolate might be used after tear down to access builtins. +#ifdef V8_EMBEDDED_BUILTINS + std::unordered_set<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>> keys; + for (auto exp : native_module->module()->export_table) { + if (exp.kind != kExternalFunction) continue; + auto& function = native_module->module()->functions[exp.index]; + JSToWasmWrapperKey key(function.imported, *function.sig); + if (keys.insert(key).second) { + auto unit = std::make_shared<JSToWasmWrapperCompilationUnit>( + isolate, wasm_engine, function.sig, function.imported, + enabled_features); + builder->AddJSToWasmWrapperUnit(std::move(unit)); + } + } + + return static_cast<int>(keys.size()); +#else + return 0; +#endif +} + // Returns the number of units added. int AddImportWrapperUnits(NativeModule* native_module, CompilationUnitBuilder* builder) { @@ -1058,8 +1151,7 @@ int AddImportWrapperUnits(NativeModule* native_module, int num_imported_functions = native_module->num_imported_functions(); for (int func_index = 0; func_index < num_imported_functions; func_index++) { FunctionSig* sig = native_module->module()->functions[func_index].sig; - bool has_bigint_feature = native_module->enabled_features().bigint; - if (!IsJSCompatibleSignature(sig, has_bigint_feature)) { + if (!IsJSCompatibleSignature(sig, native_module->enabled_features())) { continue; } WasmImportWrapperCache::CacheKey key(compiler::kDefaultImportCallKind, sig); @@ -1074,7 +1166,7 @@ int AddImportWrapperUnits(NativeModule* native_module, return static_cast<int>(keys.size()); } -void InitializeCompilationUnits(NativeModule* native_module) { +void InitializeCompilationUnits(Isolate* isolate, NativeModule* native_module) { CompilationStateImpl* compilation_state = Impl(native_module->compilation_state()); const bool lazy_module = IsLazyModule(native_module->module()); @@ -1098,8 +1190,11 @@ void InitializeCompilationUnits(NativeModule* native_module) { } } int num_import_wrappers = AddImportWrapperUnits(native_module, &builder); - compilation_state->InitializeCompilationProgress(lazy_module, - num_import_wrappers); + int num_export_wrappers = + AddExportWrapperUnits(isolate, isolate->wasm_engine(), native_module, + &builder, WasmFeaturesFromIsolate(isolate)); + compilation_state->InitializeCompilationProgress( + lazy_module, num_import_wrappers + num_export_wrappers); builder.Commit(); } @@ -1201,7 +1296,7 @@ void CompileNativeModule(Isolate* isolate, ErrorThrower* thrower, } // Initialize the compilation units and kick off background compile tasks. - InitializeCompilationUnits(native_module); + InitializeCompilationUnits(isolate, native_module); // If tiering is disabled, the main thread can execute any unit (all of them // are part of initial compilation). Otherwise, just execute baseline units. @@ -1273,26 +1368,23 @@ std::shared_ptr<NativeModule> CompileToNativeModule( OwnedVector<uint8_t> wire_bytes_copy = OwnedVector<uint8_t>::Of(wire_bytes.module_bytes()); - // Create and compile the native module. - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get()); - // Create a new {NativeModule} first. auto native_module = isolate->wasm_engine()->NewNativeModule( - isolate, enabled, code_size_estimate, - wasm::NativeModule::kCanAllocateMoreMemory, std::move(module)); + isolate, enabled, std::move(module)); native_module->SetWireBytes(std::move(wire_bytes_copy)); native_module->SetRuntimeStubs(isolate); CompileNativeModule(isolate, thrower, wasm_module, native_module.get()); if (thrower->error()) return {}; - // Compile JS->wasm wrappers for exported functions. - int num_wrappers = MaxNumExportWrappers(native_module->module()); - *export_wrappers_out = - isolate->factory()->NewFixedArray(num_wrappers, AllocationType::kOld); +#ifdef V8_EMBEDDED_BUILTINS + Impl(native_module->compilation_state()) + ->FinalizeJSToWasmWrappers(isolate, native_module->module(), + export_wrappers_out); +#else CompileJsToWasmWrappers(isolate, native_module->module(), - *export_wrappers_out); + export_wrappers_out); +#endif // Log the code within the generated module for profiling. native_module->LogWasmCodes(isolate); @@ -1366,7 +1458,9 @@ class AsyncStreamingProcessor final : public StreamingProcessor { ModuleDecoder decoder_; AsyncCompileJob* job_; + WasmEngine* wasm_engine_; std::unique_ptr<CompilationUnitBuilder> compilation_unit_builder_; + base::TimeTicks start_time_; int num_functions_ = 0; }; @@ -1414,11 +1508,8 @@ void AsyncCompileJob::CreateNativeModule( // breakpoints on a (potentially empty) subset of the instances. // Create the module object. - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get()); native_module_ = isolate_->wasm_engine()->NewNativeModule( - isolate_, enabled_features_, code_size_estimate, - wasm::NativeModule::kCanAllocateMoreMemory, std::move(module)); + isolate_, enabled_features_, std::move(module)); native_module_->SetWireBytes({std::move(bytes_copy_), wire_bytes_.length()}); native_module_->SetRuntimeStubs(isolate_); @@ -1432,10 +1523,8 @@ void AsyncCompileJob::PrepareRuntimeObjects() { Handle<Script> script = CreateWasmScript(isolate_, wire_bytes_, module->source_map_url); - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module); - Handle<WasmModuleObject> module_object = WasmModuleObject::New( - isolate_, native_module_, script, code_size_estimate); + Handle<WasmModuleObject> module_object = + WasmModuleObject::New(isolate_, native_module_, script); module_object_ = isolate_->global_handles()->Create(*module_object); } @@ -1461,17 +1550,25 @@ void AsyncCompileJob::FinishCompile() { } isolate_->debug()->OnAfterCompile(script); - // We can only update the feature counts once the entire compile is done. auto compilation_state = Impl(module_object_->native_module()->compilation_state()); - compilation_state->PublishDetectedFeatures(isolate_); - // TODO(bbudge) Allow deserialization without wrapper compilation, so we can // just compile wrappers here. if (!is_after_deserialization) { - // TODO(wasm): compiling wrappers should be made async. - CompileWrappers(); +#ifdef V8_EMBEDDED_BUILTINS + Handle<FixedArray> export_wrappers; + compilation_state->FinalizeJSToWasmWrappers( + isolate_, module_object_->module(), &export_wrappers); + module_object_->set_export_wrappers(*export_wrappers); +#else + Handle<FixedArray> export_wrappers; + CompileJsToWasmWrappers(isolate_, module_object_->module(), + &export_wrappers); + module_object_->set_export_wrappers(*export_wrappers); +#endif } + // We can only update the feature counts once the entire compile is done. + compilation_state->PublishDetectedFeatures(isolate_); FinishModule(); } @@ -1791,7 +1888,7 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep { // then DoAsync would do the same as NextStep already. // Add compilation units and kick off compilation. - InitializeCompilationUnits(job->native_module_.get()); + InitializeCompilationUnits(job->isolate(), job->native_module_.get()); } } }; @@ -1852,17 +1949,8 @@ class AsyncCompileJob::CompileFinished : public CompileStep { } }; -void AsyncCompileJob::CompileWrappers() { - // TODO(wasm): Compile all wrappers here, including the start function wrapper - // and the wrappers for the function table elements. - TRACE_COMPILE("(5) Compile wrappers...\n"); - // Compile JS->wasm wrappers for exported functions. - CompileJsToWasmWrappers(isolate_, module_object_->native_module()->module(), - handle(module_object_->export_wrappers(), isolate_)); -} - void AsyncCompileJob::FinishModule() { - TRACE_COMPILE("(6) Finish module...\n"); + TRACE_COMPILE("(4) Finish module...\n"); AsyncCompileSucceeded(module_object_); isolate_->wasm_engine()->RemoveCompileJob(this); } @@ -1870,7 +1958,9 @@ void AsyncCompileJob::FinishModule() { AsyncStreamingProcessor::AsyncStreamingProcessor(AsyncCompileJob* job) : decoder_(job->enabled_features_), job_(job), - compilation_unit_builder_(nullptr) {} + wasm_engine_(job_->isolate_->wasm_engine()), + compilation_unit_builder_(nullptr), + start_time_(base::TimeTicks::Now()) {} void AsyncStreamingProcessor::FinishAsyncCompileJobWithError( const WasmError& error) { @@ -1972,8 +2062,11 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader( int num_import_wrappers = AddImportWrapperUnits(native_module, compilation_unit_builder_.get()); - compilation_state->InitializeCompilationProgress(lazy_module, - num_import_wrappers); + int num_export_wrappers = AddExportWrapperUnits( + job_->isolate_, wasm_engine_, native_module, + compilation_unit_builder_.get(), job_->enabled_features_); + compilation_state->InitializeCompilationProgress( + lazy_module, num_import_wrappers + num_export_wrappers); return true; } @@ -2096,6 +2189,13 @@ bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes, MaybeHandle<WasmModuleObject> result = DeserializeNativeModule(job_->isolate_, module_bytes, wire_bytes); + if (base::TimeTicks::IsHighResolution()) { + base::TimeDelta duration = base::TimeTicks::Now() - start_time_; + auto* histogram = job_->isolate_->counters() + ->wasm_streaming_deserialize_wasm_module_time(); + histogram->AddSample(static_cast<int>(duration.InMicroseconds())); + } + if (result.is_null()) return false; job_->module_object_ = @@ -2144,8 +2244,8 @@ void CompilationStateImpl::AbortCompilation() { callbacks_.clear(); } -void CompilationStateImpl::InitializeCompilationProgress( - bool lazy_module, int num_import_wrappers) { +void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module, + int num_wrappers) { DCHECK(!failed()); auto enabled_features = native_module_->enabled_features(); auto* module = native_module_->module(); @@ -2189,7 +2289,7 @@ void CompilationStateImpl::InitializeCompilationProgress( DCHECK_IMPLIES(lazy_module, outstanding_top_tier_functions_ == 0); DCHECK_LE(0, outstanding_baseline_units_); DCHECK_LE(outstanding_baseline_units_, outstanding_top_tier_functions_); - outstanding_baseline_units_ += num_import_wrappers; + outstanding_baseline_units_ += num_wrappers; // Trigger callbacks if module needs no baseline or top tier compilation. This // can be the case for an empty or fully lazy module. @@ -2214,15 +2314,52 @@ void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) { void CompilationStateImpl::AddCompilationUnits( Vector<WasmCompilationUnit> baseline_units, - Vector<WasmCompilationUnit> top_tier_units) { - compilation_unit_queues_.AddUnits(baseline_units, top_tier_units, - native_module_->module()); + Vector<WasmCompilationUnit> top_tier_units, + Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> + js_to_wasm_wrapper_units) { + if (!baseline_units.empty() || !top_tier_units.empty()) { + compilation_unit_queues_.AddUnits(baseline_units, top_tier_units, + native_module_->module()); + } + js_to_wasm_wrapper_units_.insert(js_to_wasm_wrapper_units_.end(), + js_to_wasm_wrapper_units.begin(), + js_to_wasm_wrapper_units.end()); RestartBackgroundTasks(); } void CompilationStateImpl::AddTopTierCompilationUnit(WasmCompilationUnit unit) { - AddCompilationUnits({}, {&unit, 1}); + AddCompilationUnits({}, {&unit, 1}, {}); +} + +std::shared_ptr<JSToWasmWrapperCompilationUnit> +CompilationStateImpl::GetNextJSToWasmWrapperCompilationUnit() { + int wrapper_id = + js_to_wasm_wrapper_id_.fetch_add(1, std::memory_order_relaxed); + if (wrapper_id < static_cast<int>(js_to_wasm_wrapper_units_.size())) { + return js_to_wasm_wrapper_units_[wrapper_id]; + } + return nullptr; +} + +void CompilationStateImpl::FinalizeJSToWasmWrappers( + Isolate* isolate, const WasmModule* module, + Handle<FixedArray>* export_wrappers_out) { + *export_wrappers_out = isolate->factory()->NewFixedArray( + MaxNumExportWrappers(module), AllocationType::kOld); + // TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an + // optimization we keep the code space unlocked to avoid repeated unlocking + // because many such wrapper are allocated in sequence below. + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), + "FinalizeJSToWasmWrappers"); + CodeSpaceMemoryModificationScope modification_scope(isolate->heap()); + for (auto& unit : js_to_wasm_wrapper_units_) { + Handle<Code> code = unit->Finalize(isolate); + int wrapper_index = + GetExportWrapperIndex(module, unit->sig(), unit->is_import()); + (*export_wrappers_out)->set(wrapper_index, *code); + RecordStats(*code, isolate->counters()); + } } base::Optional<WasmCompilationUnit> @@ -2312,24 +2449,37 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) { DCHECK_LE(0, outstanding_baseline_units_); } - // Trigger callbacks. - if (completes_baseline_compilation) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "BaselineFinished"); - for (auto& callback : callbacks_) { - callback(CompilationEvent::kFinishedBaselineCompilation); - } - if (outstanding_top_tier_functions_ == 0) { - completes_top_tier_compilation = true; - } + TriggerCallbacks(completes_baseline_compilation, + completes_top_tier_compilation); + } +} + +void CompilationStateImpl::OnFinishedJSToWasmWrapperUnits(int num) { + if (num == 0) return; + base::MutexGuard guard(&callbacks_mutex_); + outstanding_baseline_units_ -= num; + bool completes_baseline_compilation = outstanding_baseline_units_ == 0; + TriggerCallbacks(completes_baseline_compilation, false); +} + +void CompilationStateImpl::TriggerCallbacks( + bool completes_baseline_compilation, bool completes_top_tier_compilation) { + if (completes_baseline_compilation) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "BaselineFinished"); + for (auto& callback : callbacks_) { + callback(CompilationEvent::kFinishedBaselineCompilation); } - if (outstanding_baseline_units_ == 0 && completes_top_tier_compilation) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "TopTierFinished"); - for (auto& callback : callbacks_) { - callback(CompilationEvent::kFinishedTopTierCompilation); - } - // Clear the callbacks because no more events will be delivered. - callbacks_.clear(); + if (outstanding_top_tier_functions_ == 0) { + completes_top_tier_compilation = true; + } + } + if (outstanding_baseline_units_ == 0 && completes_top_tier_compilation) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "TopTierFinished"); + for (auto& callback : callbacks_) { + callback(CompilationEvent::kFinishedTopTierCompilation); } + // Clear the callbacks because no more events will be delivered. + callbacks_.clear(); } } @@ -2378,6 +2528,11 @@ void CompilationStateImpl::RestartBackgroundTasks() { if (failed()) return; size_t max_num_restart = compilation_unit_queues_.GetTotalSize(); + if (js_to_wasm_wrapper_id_ < + static_cast<int>(js_to_wasm_wrapper_units_.size())) { + max_num_restart += + js_to_wasm_wrapper_units_.size() - js_to_wasm_wrapper_id_; + } while (!available_task_ids_.empty() && max_num_restart-- > 0) { int task_id = available_task_ids_.back(); @@ -2417,7 +2572,6 @@ void CompilationStateImpl::SetError() { } namespace { -using JSToWasmWrapperKey = std::pair<bool, FunctionSig>; using JSToWasmWrapperQueue = WrapperQueue<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>>; using JSToWasmWrapperUnitMap = @@ -2448,9 +2602,13 @@ class CompileJSToWasmWrapperTask final : public CancelableTask { } // namespace void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, - Handle<FixedArray> export_wrappers) { + Handle<FixedArray>* export_wrappers_out) { + *export_wrappers_out = isolate->factory()->NewFixedArray( + MaxNumExportWrappers(module), AllocationType::kOld); + JSToWasmWrapperQueue queue; JSToWasmWrapperUnitMap compilation_units; + WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate); // Prepare compilation units in the main thread. for (auto exp : module->export_table) { @@ -2459,8 +2617,8 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, JSToWasmWrapperKey key(function.imported, *function.sig); if (queue.insert(key)) { auto unit = base::make_unique<JSToWasmWrapperCompilationUnit>( - isolate, function.sig, function.imported); - unit->Prepare(isolate); + isolate, isolate->wasm_engine(), function.sig, function.imported, + enabled_features); compilation_units.emplace(key, std::move(unit)); } } @@ -2491,7 +2649,7 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, JSToWasmWrapperCompilationUnit* unit = pair.second.get(); Handle<Code> code = unit->Finalize(isolate); int wrapper_index = GetExportWrapperIndex(module, &key.second, key.first); - export_wrappers->set(wrapper_index, *code); + (*export_wrappers_out)->set(wrapper_index, *code); RecordStats(*code, isolate->counters()); } } diff --git a/chromium/v8/src/wasm/module-compiler.h b/chromium/v8/src/wasm/module-compiler.h index 27c7bff8683..69eb6bb62c4 100644 --- a/chromium/v8/src/wasm/module-compiler.h +++ b/chromium/v8/src/wasm/module-compiler.h @@ -46,7 +46,7 @@ std::shared_ptr<NativeModule> CompileToNativeModule( V8_EXPORT_PRIVATE void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module, - Handle<FixedArray> export_wrappers); + Handle<FixedArray>* export_wrappers_out); // Compiles the wrapper for this (kind, sig) pair and sets the corresponding // cache entry. Assumes the key already exists in the cache but has not been @@ -153,8 +153,6 @@ class AsyncCompileJob { void AsyncCompileSucceeded(Handle<WasmModuleObject> result); - void CompileWrappers(); - void FinishModule(); void StartForegroundTask(); diff --git a/chromium/v8/src/wasm/module-instantiate.cc b/chromium/v8/src/wasm/module-instantiate.cc index a4b0139ea43..976c3cde001 100644 --- a/chromium/v8/src/wasm/module-instantiate.cc +++ b/chromium/v8/src/wasm/module-instantiate.cc @@ -656,6 +656,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) { uint32_t size = segment.source.length(); if (enabled_.bulk_memory) { + if (size == 0) continue; // Passive segments are not copied during instantiation. if (!segment.active) continue; @@ -834,10 +835,18 @@ bool InstanceBuilder::ProcessImportedFunction( module_name, import_name); return false; } + // Store any {WasmExternalFunction} callable in the instance before the call + // is resolved to preserve its identity. This handles exported functions as + // well as functions constructed via other means (e.g. WebAssembly.Function). + if (WasmExternalFunction::IsWasmExternalFunction(*value)) { + WasmInstanceObject::SetWasmExternalFunction( + isolate_, instance, func_index, + Handle<WasmExternalFunction>::cast(value)); + } auto js_receiver = Handle<JSReceiver>::cast(value); FunctionSig* expected_sig = module_->functions[func_index].sig; - auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig, - enabled_.bigint); + auto resolved = + compiler::ResolveWasmImportCall(js_receiver, expected_sig, enabled_); compiler::WasmImportCallKind kind = resolved.first; js_receiver = resolved.second; switch (kind) { @@ -854,10 +863,6 @@ bool InstanceBuilder::ProcessImportedFunction( Address imported_target = imported_function->GetWasmCallTarget(); ImportedFunctionEntry entry(instance, func_index); entry.SetWasmToWasm(*imported_instance, imported_target); - // Also store the {WasmExportedFunction} in the instance to preserve its - // identity. - WasmInstanceObject::SetWasmExportedFunction( - isolate_, instance, func_index, imported_function); break; } case compiler::WasmImportCallKind::kWasmToCapi: { @@ -1218,8 +1223,7 @@ void InstanceBuilder::CompileImportWrappers( auto js_receiver = Handle<JSReceiver>::cast(value); uint32_t func_index = module_->import_table[index].index; FunctionSig* sig = module_->functions[func_index].sig; - auto resolved = - compiler::ResolveWasmImportCall(js_receiver, sig, enabled_.bigint); + auto resolved = compiler::ResolveWasmImportCall(js_receiver, sig, enabled_); compiler::WasmImportCallKind kind = resolved.first; if (kind == compiler::WasmImportCallKind::kWasmToWasm || kind == compiler::WasmImportCallKind::kLinkError || @@ -1373,7 +1377,7 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) { break; case WasmInitExpr::kRefFuncConst: { DCHECK(enabled_.anyref); - auto function = WasmInstanceObject::GetOrCreateWasmExportedFunction( + auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction( isolate_, instance, global.init.val.function_index); tagged_globals_->set(global.offset, *function); break; @@ -1450,10 +1454,10 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { const WasmImport& import = module_->import_table[index]; if (import.kind == kExternalFunction) { Handle<Object> value = sanitized_imports_[index].value; - if (WasmExportedFunction::IsWasmExportedFunction(*value)) { - WasmInstanceObject::SetWasmExportedFunction( + if (WasmExternalFunction::IsWasmExternalFunction(*value)) { + WasmInstanceObject::SetWasmExternalFunction( isolate_, instance, import.index, - Handle<WasmExportedFunction>::cast(value)); + Handle<WasmExternalFunction>::cast(value)); } } } @@ -1498,10 +1502,10 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { case kExternalFunction: { // Wrap and export the code as a JSFunction. // TODO(wasm): reduce duplication with LoadElemSegment() further below - MaybeHandle<WasmExportedFunction> wasm_exported_function = - WasmInstanceObject::GetOrCreateWasmExportedFunction( + Handle<WasmExternalFunction> wasm_external_function = + WasmInstanceObject::GetOrCreateWasmExternalFunction( isolate_, instance, exp.index); - desc.set_value(wasm_exported_function.ToHandleChecked()); + desc.set_value(wasm_external_function); if (is_asm_js && String::Equals(isolate_, name, @@ -1629,6 +1633,7 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance, uint32_t table_index, const WasmElemSegment& elem_segment, uint32_t dst, uint32_t src, size_t count) { + if (count == 0) return true; // TODO(wasm): Move this functionality into wasm-objects, since it is used // for both instantiation and in the implementation of the table.init // instruction. @@ -1660,27 +1665,27 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance, .Set(sig_id, instance, func_index); } - // For AnyRef tables, we have to generate the WasmExportedFunction eagerly. + // For AnyRef tables, we have to generate the WasmExternalFunction eagerly. // Later we cannot know if an entry is a placeholder or not. if (table_object->type() == kWasmAnyRef) { - Handle<WasmExportedFunction> wasm_exported_function = - WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance, + Handle<WasmExternalFunction> wasm_external_function = + WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance, func_index); WasmTableObject::Set(isolate, table_object, entry_index, - wasm_exported_function); + wasm_external_function); } else { // Update the table object's other dispatch tables. - MaybeHandle<WasmExportedFunction> wasm_exported_function = - WasmInstanceObject::GetWasmExportedFunction(isolate, instance, + MaybeHandle<WasmExternalFunction> wasm_external_function = + WasmInstanceObject::GetWasmExternalFunction(isolate, instance, func_index); - if (wasm_exported_function.is_null()) { + if (wasm_external_function.is_null()) { // No JSFunction entry yet exists for this function. Create a {Tuple2} // holding the information to lazily allocate one. WasmTableObject::SetFunctionTablePlaceholder( isolate, table_object, entry_index, instance, func_index); } else { table_object->entries().set(entry_index, - *wasm_exported_function.ToHandleChecked()); + *wasm_external_function.ToHandleChecked()); } // UpdateDispatchTables() updates all other dispatch tables, since // we have not yet added the dispatch table we are currently building. @@ -1701,6 +1706,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) { uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset); uint32_t src = 0; size_t count = elem_segment.entries.size(); + if (enabled_.bulk_memory && count == 0) continue; bool success = LoadElemSegmentImpl( isolate_, instance, diff --git a/chromium/v8/src/wasm/wasm-code-manager.cc b/chromium/v8/src/wasm/wasm-code-manager.cc index 3d0cde0cced..91cfc01ceae 100644 --- a/chromium/v8/src/wasm/wasm-code-manager.cc +++ b/chromium/v8/src/wasm/wasm-code-manager.cc @@ -25,13 +25,14 @@ #include "src/wasm/function-compiler.h" #include "src/wasm/jump-table-assembler.h" #include "src/wasm/wasm-import-wrapper-cache.h" +#include "src/wasm/wasm-module-sourcemap.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects.h" -#if defined(V8_OS_WIN_X64) +#if defined(V8_OS_WIN64) #include "src/diagnostics/unwinding-info-win64.h" -#endif +#endif // V8_OS_WIN64 #define TRACE_HEAP(...) \ do { \ @@ -88,13 +89,30 @@ base::AddressRegion DisjointAllocationPool::Merge(base::AddressRegion region) { } base::AddressRegion DisjointAllocationPool::Allocate(size_t size) { + return AllocateInRegion(size, + {kNullAddress, std::numeric_limits<size_t>::max()}); +} + +base::AddressRegion DisjointAllocationPool::AllocateInRegion( + size_t size, base::AddressRegion region) { for (auto it = regions_.begin(), end = regions_.end(); it != end; ++it) { - if (size > it->size()) continue; - base::AddressRegion ret{it->begin(), size}; + base::AddressRegion overlap = it->GetOverlap(region); + if (size > overlap.size()) continue; + base::AddressRegion ret{overlap.begin(), size}; if (size == it->size()) { + // We use the full region --> erase the region from {regions_}. regions_.erase(it); - } else { + } else if (ret.begin() == it->begin()) { + // We return a region at the start --> shrink remaining region from front. *it = base::AddressRegion{it->begin() + size, it->size() - size}; + } else if (ret.end() == it->end()) { + // We return a region at the end --> shrink remaining region. + *it = base::AddressRegion{it->begin(), it->size() - size}; + } else { + // We return something in the middle --> split the remaining region. + regions_.insert( + it, base::AddressRegion{it->begin(), ret.begin() - it->begin()}); + *it = base::AddressRegion{ret.end(), it->end() - ret.end()}; } return ret; } @@ -164,6 +182,19 @@ void WasmCode::LogCode(Isolate* isolate) const { WireBytesRef name_ref = native_module()->module()->LookupFunctionName(wire_bytes, index()); WasmName name_vec = wire_bytes.GetNameOrNull(name_ref); + + const std::string& source_map_url = native_module()->module()->source_map_url; + auto load_wasm_source_map = isolate->wasm_load_source_map_callback(); + auto source_map = native_module()->GetWasmSourceMap(); + if (!source_map && !source_map_url.empty() && load_wasm_source_map) { + HandleScope scope(isolate); + v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); + Local<v8::String> source_map_str = + load_wasm_source_map(v8_isolate, source_map_url.c_str()); + native_module()->SetWasmSourceMap( + base::make_unique<WasmModuleSourceMap>(v8_isolate, source_map_str)); + } + if (!name_vec.empty()) { HandleScope scope(isolate); MaybeHandle<String> maybe_name = isolate->factory()->NewStringFromUtf8( @@ -204,11 +235,7 @@ void WasmCode::Validate() const { switch (mode) { case RelocInfo::WASM_CALL: { Address target = it.rinfo()->wasm_call_address(); - WasmCode* code = native_module_->Lookup(target); - CHECK_NOT_NULL(code); - CHECK_EQ(WasmCode::kJumpTable, code->kind()); - CHECK_EQ(native_module()->jump_table_, code); - CHECK(code->contains(target)); + DCHECK(native_module_->is_jump_table_slot(target)); break; } case RelocInfo::WASM_STUB_CALL: { @@ -464,32 +491,51 @@ base::SmallVector<base::AddressRegion, 1> SplitRangeByReservationsIfNeeded( Vector<byte> WasmCodeAllocator::AllocateForCode(NativeModule* native_module, size_t size) { + return AllocateForCodeInRegion( + native_module, size, {kNullAddress, std::numeric_limits<size_t>::max()}); +} + +Vector<byte> WasmCodeAllocator::AllocateForCodeInRegion( + NativeModule* native_module, size_t size, base::AddressRegion region) { base::MutexGuard lock(&mutex_); DCHECK_EQ(code_manager_, native_module->engine()->code_manager()); DCHECK_LT(0, size); v8::PageAllocator* page_allocator = GetPlatformPageAllocator(); - // This happens under a lock assumed by the caller. size = RoundUp<kCodeAlignment>(size); - base::AddressRegion code_space = free_code_space_.Allocate(size); + base::AddressRegion code_space = + free_code_space_.AllocateInRegion(size, region); if (code_space.is_empty()) { - if (!can_request_more_memory_) { - V8::FatalProcessOutOfMemory(nullptr, "wasm code reservation"); + const bool in_specific_region = + region.size() < std::numeric_limits<size_t>::max(); + if (!can_request_more_memory_ || in_specific_region) { + auto error = in_specific_region ? "wasm code reservation in region" + : "wasm code reservation"; + V8::FatalProcessOutOfMemory(nullptr, error); UNREACHABLE(); } Address hint = owned_code_space_.empty() ? kNullAddress : owned_code_space_.back().end(); + // Reserve at least 20% of the total generated code size so far, and of + // course at least {size}. Round up to the next power of two. + size_t total_reserved = 0; + for (auto& vmem : owned_code_space_) total_reserved += vmem.size(); + size_t reserve_size = + base::bits::RoundUpToPowerOfTwo(std::max(size, total_reserved / 5)); VirtualMemory new_mem = - code_manager_->TryAllocate(size, reinterpret_cast<void*>(hint)); + code_manager_->TryAllocate(reserve_size, reinterpret_cast<void*>(hint)); if (!new_mem.IsReserved()) { V8::FatalProcessOutOfMemory(nullptr, "wasm code reservation"); UNREACHABLE(); } - code_manager_->AssignRange(new_mem.region(), native_module); - free_code_space_.Merge(new_mem.region()); + base::AddressRegion new_region = new_mem.region(); + code_manager_->AssignRange(new_region, native_module); + free_code_space_.Merge(new_region); owned_code_space_.emplace_back(std::move(new_mem)); + native_module->AddCodeSpace(new_region); + code_space = free_code_space_.Allocate(size); DCHECK(!code_space.is_empty()); async_counters_->wasm_module_num_code_spaces()->AddSample( @@ -614,6 +660,12 @@ void WasmCodeAllocator::FreeCode(Vector<WasmCode* const> codes) { } } +base::AddressRegion WasmCodeAllocator::GetSingleCodeRegion() const { + base::MutexGuard lock(&mutex_); + DCHECK_EQ(1, owned_code_space_.size()); + return owned_code_space_[0].region(); +} + NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled, bool can_request_more, VirtualMemory code_space, std::shared_ptr<const WasmModule> module, @@ -636,27 +688,10 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled, compilation_state_ = CompilationState::New(*shared_this, std::move(async_counters)); DCHECK_NOT_NULL(module_); - -#if defined(V8_OS_WIN_X64) - // On some platforms, specifically Win64, we need to reserve some pages at - // the beginning of an executable space. - // See src/heap/spaces.cc, MemoryAllocator::InitializeCodePageAllocator() and - // https://cs.chromium.org/chromium/src/components/crash/content/app/crashpad_win.cc?rcl=fd680447881449fba2edcf0589320e7253719212&l=204 - // for details. - if (engine_->code_manager() - ->CanRegisterUnwindInfoForNonABICompliantCodeRange()) { - code_allocator_.AllocateForCode(this, Heap::GetCodeRangeReservedAreaSize()); - } -#endif - - uint32_t num_wasm_functions = module_->num_declared_functions; - if (num_wasm_functions > 0) { - code_table_.reset(new WasmCode* [num_wasm_functions] {}); - - WasmCodeRefScope code_ref_scope; - jump_table_ = CreateEmptyJumpTable( - JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions)); + if (module_->num_declared_functions > 0) { + code_table_.reset(new WasmCode* [module_->num_declared_functions] {}); } + AddCodeSpace(code_allocator_.GetSingleCodeRegion()); } void NativeModule::ReserveCodeTableForTesting(uint32_t max_functions) { @@ -669,9 +704,12 @@ void NativeModule::ReserveCodeTableForTesting(uint32_t max_functions) { } code_table_.reset(new_table); + CHECK_EQ(1, code_space_data_.size()); // Re-allocate jump table. - jump_table_ = CreateEmptyJumpTable( - JumpTableAssembler::SizeForNumberOfSlots(max_functions)); + code_space_data_[0].jump_table = CreateEmptyJumpTableInRegion( + JumpTableAssembler::SizeForNumberOfSlots(max_functions), + code_space_data_[0].region); + main_jump_table_ = code_space_data_[0].jump_table; } void NativeModule::LogWasmCodes(Isolate* isolate) { @@ -704,8 +742,10 @@ void NativeModule::UseLazyStub(uint32_t func_index) { if (!lazy_compile_table_) { uint32_t num_slots = module_->num_declared_functions; WasmCodeRefScope code_ref_scope; - lazy_compile_table_ = CreateEmptyJumpTable( - JumpTableAssembler::SizeForNumberOfLazyFunctions(num_slots)); + DCHECK_EQ(1, code_space_data_.size()); + lazy_compile_table_ = CreateEmptyJumpTableInRegion( + JumpTableAssembler::SizeForNumberOfLazyFunctions(num_slots), + code_space_data_[0].region); JumpTableAssembler::GenerateLazyCompileTable( lazy_compile_table_->instruction_start(), num_slots, module_->num_imported_functions, @@ -718,7 +758,7 @@ void NativeModule::UseLazyStub(uint32_t func_index) { Address lazy_compile_target = lazy_compile_table_->instruction_start() + JumpTableAssembler::LazyCompileSlotIndexToOffset(slot_index); - JumpTableAssembler::PatchJumpTableSlot(jump_table_->instruction_start(), + JumpTableAssembler::PatchJumpTableSlot(main_jump_table_->instruction_start(), slot_index, lazy_compile_target, WasmCode::kFlushICache); } @@ -729,9 +769,10 @@ void NativeModule::SetRuntimeStubs(Isolate* isolate) { DCHECK_EQ(kNullAddress, runtime_stub_entries_[0]); // Only called once. #ifdef V8_EMBEDDED_BUILTINS WasmCodeRefScope code_ref_scope; - WasmCode* jump_table = - CreateEmptyJumpTable(JumpTableAssembler::SizeForNumberOfStubSlots( - WasmCode::kRuntimeStubCount)); + DCHECK_EQ(1, code_space_data_.size()); + WasmCode* jump_table = CreateEmptyJumpTableInRegion( + JumpTableAssembler::SizeForNumberOfStubSlots(WasmCode::kRuntimeStubCount), + code_space_data_[0].region); Address base = jump_table->instruction_start(); EmbeddedData embedded_data = EmbeddedData::FromBlob(); #define RUNTIME_STUB(Name) Builtins::k##Name, @@ -995,8 +1036,12 @@ WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) { // Populate optimized code to the jump table unless there is an active // redirection to the interpreter that should be preserved. - bool update_jump_table = - update_code_table && !has_interpreter_redirection(code->index()); + DCHECK_IMPLIES( + main_jump_table_ == nullptr, + engine_->code_manager()->IsImplicitAllocationsDisabledForTesting()); + bool update_jump_table = update_code_table && + !has_interpreter_redirection(code->index()) && + main_jump_table_; // Ensure that interpreter entries always populate to the jump table. if (code->kind_ == WasmCode::Kind::kInterpreterEntry) { @@ -1006,8 +1051,8 @@ WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) { if (update_jump_table) { JumpTableAssembler::PatchJumpTableSlot( - jump_table_->instruction_start(), slot_idx, code->instruction_start(), - WasmCode::kFlushICache); + main_jump_table_->instruction_start(), slot_idx, + code->instruction_start(), WasmCode::kFlushICache); } } WasmCodeRefScope::AddRef(code.get()); @@ -1065,11 +1110,22 @@ bool NativeModule::HasCode(uint32_t index) const { return code_table_[index - module_->num_imported_functions] != nullptr; } -WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) { +void NativeModule::SetWasmSourceMap( + std::unique_ptr<WasmModuleSourceMap> source_map) { + source_map_ = std::move(source_map); +} + +WasmModuleSourceMap* NativeModule::GetWasmSourceMap() const { + return source_map_.get(); +} + +WasmCode* NativeModule::CreateEmptyJumpTableInRegion( + uint32_t jump_table_size, base::AddressRegion region) { // Only call this if we really need a jump table. DCHECK_LT(0, jump_table_size); Vector<uint8_t> code_space = - code_allocator_.AllocateForCode(this, jump_table_size); + code_allocator_.AllocateForCodeInRegion(this, jump_table_size, region); + DCHECK(!code_space.empty()); ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size()); std::unique_ptr<WasmCode> code{new WasmCode{ this, // native_module @@ -1090,6 +1146,48 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) { return PublishCode(std::move(code)); } +void NativeModule::AddCodeSpace(base::AddressRegion region) { + // Each code space must be at least twice as large as the overhead per code + // space. Otherwise, we are wasting too much memory. + const bool is_first_code_space = code_space_data_.empty(); + const bool implicit_alloc_disabled = + engine_->code_manager()->IsImplicitAllocationsDisabledForTesting(); + +#if defined(V8_OS_WIN64) + // On some platforms, specifically Win64, we need to reserve some pages at + // the beginning of an executable space. + // See src/heap/spaces.cc, MemoryAllocator::InitializeCodePageAllocator() and + // https://cs.chromium.org/chromium/src/components/crash/content/app/crashpad_win.cc?rcl=fd680447881449fba2edcf0589320e7253719212&l=204 + // for details. + if (engine_->code_manager() + ->CanRegisterUnwindInfoForNonABICompliantCodeRange() && + !implicit_alloc_disabled) { + size_t size = Heap::GetCodeRangeReservedAreaSize(); + DCHECK_LT(0, size); + Vector<byte> padding = code_allocator_.AllocateForCode(this, size); + CHECK(region.contains(reinterpret_cast<Address>(padding.begin()), + padding.size())); + } +#endif // V8_OS_WIN64 + + WasmCodeRefScope code_ref_scope; + WasmCode* jump_table = nullptr; + const uint32_t num_wasm_functions = module_->num_declared_functions; + const bool has_functions = num_wasm_functions > 0; + const bool needs_jump_table = + has_functions && is_first_code_space && !implicit_alloc_disabled; + + if (needs_jump_table) { + jump_table = CreateEmptyJumpTableInRegion( + JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions), region); + CHECK(region.contains(jump_table->instruction_start())); + } + + if (is_first_code_space) main_jump_table_ = jump_table; + + code_space_data_.push_back(CodeSpaceData{region, jump_table}); +} + namespace { class NativeModuleWireBytesStorage final : public WireBytesStorage { public: @@ -1137,17 +1235,17 @@ uint32_t NativeModule::GetJumpTableOffset(uint32_t func_index) const { Address NativeModule::GetCallTargetForFunction(uint32_t func_index) const { // Return the jump table slot for that function index. - DCHECK_NOT_NULL(jump_table_); + DCHECK_NOT_NULL(main_jump_table_); uint32_t slot_offset = GetJumpTableOffset(func_index); - DCHECK_LT(slot_offset, jump_table_->instructions().size()); - return jump_table_->instruction_start() + slot_offset; + DCHECK_LT(slot_offset, main_jump_table_->instructions().size()); + return main_jump_table_->instruction_start() + slot_offset; } uint32_t NativeModule::GetFunctionIndexFromJumpTableSlot( Address slot_address) const { DCHECK(is_jump_table_slot(slot_address)); - uint32_t slot_offset = - static_cast<uint32_t>(slot_address - jump_table_->instruction_start()); + uint32_t slot_offset = static_cast<uint32_t>( + slot_address - main_jump_table_->instruction_start()); uint32_t slot_idx = JumpTableAssembler::SlotOffsetToIndex(slot_offset); DCHECK_LT(slot_idx, module_->num_declared_functions); return module_->num_imported_functions + slot_idx; @@ -1181,21 +1279,16 @@ WasmCodeManager::WasmCodeManager(WasmMemoryTracker* memory_tracker, size_t max_committed) : memory_tracker_(memory_tracker), max_committed_code_space_(max_committed), -#if defined(V8_OS_WIN_X64) - is_win64_unwind_info_disabled_for_testing_(false), -#endif - total_committed_code_space_(0), critical_committed_code_space_(max_committed / 2) { DCHECK_LE(max_committed, kMaxWasmCodeMemory); } -#if defined(V8_OS_WIN_X64) +#if defined(V8_OS_WIN64) bool WasmCodeManager::CanRegisterUnwindInfoForNonABICompliantCodeRange() const { return win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange() && - FLAG_win64_unwinding_info && - !is_win64_unwind_info_disabled_for_testing_; + FLAG_win64_unwinding_info; } -#endif +#endif // V8_OS_WIN64 bool WasmCodeManager::Commit(base::AddressRegion region) { // TODO(v8:8462): Remove eager commit once perf supports remapping. @@ -1241,8 +1334,8 @@ void WasmCodeManager::Decommit(base::AddressRegion region) { USE(old_committed); TRACE_HEAP("Discarding system pages 0x%" PRIxPTR ":0x%" PRIxPTR "\n", region.begin(), region.end()); - CHECK(allocator->DiscardSystemPages(reinterpret_cast<void*>(region.begin()), - region.size())); + CHECK(allocator->SetPermissions(reinterpret_cast<void*>(region.begin()), + region.size(), PageAllocator::kNoAccess)); } void WasmCodeManager::AssignRange(base::AddressRegion region, @@ -1363,12 +1456,13 @@ std::shared_ptr<NativeModule> WasmCodeManager::NewNativeModule( TRACE_HEAP("New NativeModule %p: Mem: %" PRIuPTR ",+%zu\n", ret.get(), start, size); -#if defined(V8_OS_WIN_X64) - if (CanRegisterUnwindInfoForNonABICompliantCodeRange()) { +#if defined(V8_OS_WIN64) + if (CanRegisterUnwindInfoForNonABICompliantCodeRange() && + !implicit_allocations_disabled_for_testing_) { win64_unwindinfo::RegisterNonABICompliantCodeRange( reinterpret_cast<void*>(start), size); } -#endif +#endif // V8_OS_WIN64 base::MutexGuard lock(&native_modules_mutex_); lookup_map_.insert(std::make_pair(start, std::make_pair(end, ret.get()))); @@ -1481,12 +1575,13 @@ void WasmCodeManager::FreeNativeModule(Vector<VirtualMemory> owned_code_space, TRACE_HEAP("VMem Release: 0x%" PRIxPTR ":0x%" PRIxPTR " (%zu)\n", code_space.address(), code_space.end(), code_space.size()); -#if defined(V8_OS_WIN_X64) - if (CanRegisterUnwindInfoForNonABICompliantCodeRange()) { +#if defined(V8_OS_WIN64) + if (CanRegisterUnwindInfoForNonABICompliantCodeRange() && + !implicit_allocations_disabled_for_testing_) { win64_unwindinfo::UnregisterNonABICompliantCodeRange( reinterpret_cast<void*>(code_space.address())); } -#endif +#endif // V8_OS_WIN64 lookup_map_.erase(code_space.address()); memory_tracker_->ReleaseReservation(code_space.size()); diff --git a/chromium/v8/src/wasm/wasm-code-manager.h b/chromium/v8/src/wasm/wasm-code-manager.h index db7b4f061d6..c2e5249e5ee 100644 --- a/chromium/v8/src/wasm/wasm-code-manager.h +++ b/chromium/v8/src/wasm/wasm-code-manager.h @@ -23,6 +23,7 @@ #include "src/wasm/compilation-environment.h" #include "src/wasm/wasm-features.h" #include "src/wasm/wasm-limits.h" +#include "src/wasm/wasm-module-sourcemap.h" #include "src/wasm/wasm-tier.h" namespace v8 { @@ -61,6 +62,10 @@ class V8_EXPORT_PRIVATE DisjointAllocationPool final { // failure. base::AddressRegion Allocate(size_t size); + // Allocate a contiguous region of size {size} within {region}. Return an + // empty pool on failure. + base::AddressRegion AllocateInRegion(size_t size, base::AddressRegion); + bool IsEmpty() const { return regions_.empty(); } const std::list<base::AddressRegion>& regions() const { return regions_; } @@ -295,6 +300,11 @@ class WasmCodeAllocator { // Allocate code space. Returns a valid buffer or fails with OOM (crash). Vector<byte> AllocateForCode(NativeModule*, size_t size); + // Allocate code space within a specific region. Returns a valid buffer or + // fails with OOM (crash). + Vector<byte> AllocateForCodeInRegion(NativeModule*, size_t size, + base::AddressRegion); + // Sets permissions of all owned code space to executable, or read-write (if // {executable} is false). Returns true on success. V8_EXPORT_PRIVATE bool SetExecutable(bool executable); @@ -302,6 +312,10 @@ class WasmCodeAllocator { // Free memory pages of all given code objects. Used for wasm code GC. void FreeCode(Vector<WasmCode* const>); + // Returns the region of the single code space managed by this code allocator. + // Will fail if more than one code space has been created. + base::AddressRegion GetSingleCodeRegion() const; + private: // The engine-wide wasm code manager. WasmCodeManager* const code_manager_; @@ -392,6 +406,9 @@ class V8_EXPORT_PRIVATE NativeModule final { WasmCode* GetCode(uint32_t index) const; bool HasCode(uint32_t index) const; + void SetWasmSourceMap(std::unique_ptr<WasmModuleSourceMap> source_map); + WasmModuleSourceMap* GetWasmSourceMap() const; + Address runtime_stub_entry(WasmCode::RuntimeStubId index) const { DCHECK_LT(index, WasmCode::kRuntimeStubCount); Address entry_address = runtime_stub_entries_[index]; @@ -400,17 +417,18 @@ class V8_EXPORT_PRIVATE NativeModule final { } Address jump_table_start() const { - return jump_table_ ? jump_table_->instruction_start() : kNullAddress; + return main_jump_table_ ? main_jump_table_->instruction_start() + : kNullAddress; } uint32_t GetJumpTableOffset(uint32_t func_index) const; bool is_jump_table_slot(Address address) const { - return jump_table_->contains(address); + return main_jump_table_->contains(address); } - // Returns the target to call for the given function (returns a jump table - // slot within {jump_table_}). + // Returns the canonical target to call for the given function (the slot in + // the first jump table). Address GetCallTargetForFunction(uint32_t func_index) const; // Reverse lookup from a given call target (i.e. a jump table slot as the @@ -485,9 +503,15 @@ class V8_EXPORT_PRIVATE NativeModule final { private: friend class WasmCode; + friend class WasmCodeAllocator; friend class WasmCodeManager; friend class NativeModuleModificationScope; + struct CodeSpaceData { + base::AddressRegion region; + WasmCode* jump_table; + }; + // Private constructor, called via {WasmCodeManager::NewNativeModule()}. NativeModule(WasmEngine* engine, const WasmFeatures& enabled_features, bool can_request_more, VirtualMemory code_space, @@ -507,7 +531,11 @@ class V8_EXPORT_PRIVATE NativeModule final { WasmCode* AddAndPublishAnonymousCode(Handle<Code>, WasmCode::Kind kind, const char* name = nullptr); - WasmCode* CreateEmptyJumpTable(uint32_t jump_table_size); + WasmCode* CreateEmptyJumpTableInRegion(uint32_t jump_table_size, + base::AddressRegion); + + // Called by the {WasmCodeAllocator} to register a new code space. + void AddCodeSpace(base::AddressRegion); // Hold the {allocation_mutex_} when calling this method. bool has_interpreter_redirection(uint32_t func_index) { @@ -546,6 +574,8 @@ class V8_EXPORT_PRIVATE NativeModule final { // tasks can keep this alive. std::shared_ptr<const WasmModule> module_; + std::unique_ptr<WasmModuleSourceMap> source_map_; + // Wire bytes, held in a shared_ptr so they can be kept alive by the // {WireBytesStorage}, held by background compile tasks. std::shared_ptr<OwnedVector<const uint8_t>> wire_bytes_; @@ -556,8 +586,9 @@ class V8_EXPORT_PRIVATE NativeModule final { // Jump table used for runtime stubs (i.e. trampolines to embedded builtins). WasmCode* runtime_stub_table_ = nullptr; - // Jump table used to easily redirect wasm function calls. - WasmCode* jump_table_ = nullptr; + // Jump table used by external calls (from JS). Wasm calls use one of the jump + // tables stored in {code_space_data_}. + WasmCode* main_jump_table_ = nullptr; // Lazy compile stub table, containing entries to jump to the // {WasmCompileLazy} builtin, passing the function index. @@ -587,6 +618,9 @@ class V8_EXPORT_PRIVATE NativeModule final { // this module marking those functions that have been redirected. std::unique_ptr<uint8_t[]> interpreter_redirections_; + // Data (especially jump table) per code space. + std::vector<CodeSpaceData> code_space_data_; + // End of fields protected by {allocation_mutex_}. ////////////////////////////////////////////////////////////////////////////// @@ -610,9 +644,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final { } #endif -#if defined(V8_OS_WIN_X64) +#if defined(V8_OS_WIN64) bool CanRegisterUnwindInfoForNonABICompliantCodeRange() const; -#endif +#endif // V8_OS_WIN64 NativeModule* LookupNativeModule(Address pc) const; WasmCode* LookupCode(Address pc) const; @@ -622,11 +656,13 @@ class V8_EXPORT_PRIVATE WasmCodeManager final { void SetMaxCommittedMemoryForTesting(size_t limit); -#if defined(V8_OS_WIN_X64) - void DisableWin64UnwindInfoForTesting() { - is_win64_unwind_info_disabled_for_testing_ = true; + void DisableImplicitAllocationsForTesting() { + implicit_allocations_disabled_for_testing_ = true; + } + + bool IsImplicitAllocationsDisabledForTesting() const { + return implicit_allocations_disabled_for_testing_; } -#endif static size_t EstimateNativeModuleCodeSize(const WasmModule* module); static size_t EstimateNativeModuleNonCodeSize(const WasmModule* module); @@ -654,11 +690,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final { size_t max_committed_code_space_; -#if defined(V8_OS_WIN_X64) - bool is_win64_unwind_info_disabled_for_testing_; -#endif + bool implicit_allocations_disabled_for_testing_ = false; - std::atomic<size_t> total_committed_code_space_; + std::atomic<size_t> total_committed_code_space_{0}; // If the committed code space exceeds {critical_committed_code_space_}, then // we trigger a GC before creating the next module. This value is set to the // currently committed space plus 50% of the available code space on creation diff --git a/chromium/v8/src/wasm/wasm-engine.cc b/chromium/v8/src/wasm/wasm-engine.cc index 7b91b16b807..97111f83497 100644 --- a/chromium/v8/src/wasm/wasm-engine.cc +++ b/chromium/v8/src/wasm/wasm-engine.cc @@ -278,13 +278,8 @@ Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs( asm_wasm_data->managed_native_module().get(); Handle<FixedArray> export_wrappers = handle(asm_wasm_data->export_wrappers(), isolate); - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize( - native_module->module()); - - Handle<WasmModuleObject> module_object = - WasmModuleObject::New(isolate, std::move(native_module), script, - export_wrappers, code_size_estimate); + Handle<WasmModuleObject> module_object = WasmModuleObject::New( + isolate, std::move(native_module), script, export_wrappers); module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table()); return module_object; } @@ -310,9 +305,6 @@ MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile( Handle<Script> script = CreateWasmScript(isolate, bytes, native_module->module()->source_map_url); - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize( - native_module->module()); // Create the module object. // TODO(clemensh): For the same module (same bytes / same hash), we should @@ -323,9 +315,8 @@ MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile( // and information needed at instantiation time. This object needs to be // serializable. Instantiation may occur off a deserialized version of this // object. - Handle<WasmModuleObject> module_object = - WasmModuleObject::New(isolate, std::move(native_module), script, - export_wrappers, code_size_estimate); + Handle<WasmModuleObject> module_object = WasmModuleObject::New( + isolate, std::move(native_module), script, export_wrappers); // Finish the Wasm script now and make it public to the debugger. isolate->debug()->OnAfterCompile(script); @@ -451,14 +442,13 @@ Handle<WasmModuleObject> WasmEngine::ImportNativeModule( Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module) { NativeModule* native_module = shared_native_module.get(); ModuleWireBytes wire_bytes(native_module->wire_bytes()); - const WasmModule* module = native_module->module(); - Handle<Script> script = - CreateWasmScript(isolate, wire_bytes, module->source_map_url); - size_t code_size = native_module->committed_code_space(); + Handle<Script> script = CreateWasmScript( + isolate, wire_bytes, native_module->module()->source_map_url); + Handle<FixedArray> export_wrappers; + CompileJsToWasmWrappers(isolate, native_module->module(), &export_wrappers); Handle<WasmModuleObject> module_object = WasmModuleObject::New( - isolate, std::move(shared_native_module), script, code_size); - CompileJsToWasmWrappers(isolate, native_module->module(), - handle(module_object->export_wrappers(), isolate)); + isolate, std::move(shared_native_module), script, export_wrappers, + native_module->committed_code_space()); { base::MutexGuard lock(&mutex_); DCHECK_EQ(1, isolates_.count(isolate)); @@ -681,6 +671,16 @@ void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) { } std::shared_ptr<NativeModule> WasmEngine::NewNativeModule( + Isolate* isolate, const WasmFeatures& enabled, + std::shared_ptr<const WasmModule> module) { + size_t code_size_estimate = + wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module.get()); + return NewNativeModule(isolate, enabled, code_size_estimate, + wasm::NativeModule::kCanAllocateMoreMemory, + std::move(module)); +} + +std::shared_ptr<NativeModule> WasmEngine::NewNativeModule( Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate, bool can_request_more, std::shared_ptr<const WasmModule> module) { std::shared_ptr<NativeModule> native_module = diff --git a/chromium/v8/src/wasm/wasm-engine.h b/chromium/v8/src/wasm/wasm-engine.h index 69e6cdae6e6..401cf2b8805 100644 --- a/chromium/v8/src/wasm/wasm-engine.h +++ b/chromium/v8/src/wasm/wasm-engine.h @@ -182,6 +182,9 @@ class V8_EXPORT_PRIVATE WasmEngine { // TODO(titzer): isolate is only required here for CompilationState. std::shared_ptr<NativeModule> NewNativeModule( Isolate* isolate, const WasmFeatures& enabled_features, + std::shared_ptr<const WasmModule> module); + std::shared_ptr<NativeModule> NewNativeModule( + Isolate* isolate, const WasmFeatures& enabled_features, size_t code_size_estimate, bool can_request_more, std::shared_ptr<const WasmModule> module); diff --git a/chromium/v8/src/wasm/wasm-external-refs.cc b/chromium/v8/src/wasm/wasm-external-refs.cc index 08e6139abe9..9ca45183ef6 100644 --- a/chromium/v8/src/wasm/wasm-external-refs.cc +++ b/chromium/v8/src/wasm/wasm-external-refs.cc @@ -80,37 +80,73 @@ void int64_to_float32_wrapper(Address data) { void uint64_to_float32_wrapper(Address data) { uint64_t input = ReadUnalignedValue<uint64_t>(data); - float result = static_cast<float>(input); - -#if V8_CC_MSVC - // With MSVC we use static_cast<float>(uint32_t) instead of - // static_cast<float>(uint64_t) to achieve round-to-nearest-ties-even - // semantics. The idea is to calculate - // static_cast<float>(high_word) * 2^32 + static_cast<float>(low_word). To - // achieve proper rounding in all cases we have to adjust the high_word - // with a "rounding bit" sometimes. The rounding bit is stored in the LSB of - // the high_word if the low_word may affect the rounding of the high_word. - uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF); - uint32_t high_word = static_cast<uint32_t>(input >> 32); - - float shift = static_cast<float>(1ull << 32); - // If the MSB of the high_word is set, then we make space for a rounding bit. - if (high_word < 0x80000000) { - high_word <<= 1; - shift = static_cast<float>(1ull << 31); +#if defined(V8_OS_WIN) + // On Windows, the FP stack registers calculate with less precision, which + // leads to a uint64_t to float32 conversion which does not satisfy the + // WebAssembly specification. Therefore we do a different approach here: + // + // / leading 0 \/ 24 float data bits \/ for rounding \/ trailing 0 \ + // 00000000000001XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX100000000000000 + // + // Float32 can only represent 24 data bit (1 implicit 1 bit + 23 mantissa + // bits). Starting from the most significant 1 bit, we can therefore extract + // 24 bits and do the conversion only on them. The other bits can affect the + // result only through rounding. Rounding works as follows: + // * If the most significant rounding bit is not set, then round down. + // * If the most significant rounding bit is set, and at least one of the + // other rounding bits is set, then round up. + // * If the most significant rounding bit is set, but all other rounding bits + // are not set, then round to even. + // We can aggregate 'all other rounding bits' in the second-most significant + // rounding bit. + // The resulting algorithm is therefore as follows: + // * Check if the distance between the most significant bit (MSB) and the + // least significant bit (LSB) is greater than 25 bits. If the distance is + // less or equal to 25 bits, the uint64 to float32 conversion is anyways + // exact, and we just use the C++ conversion. + // * Find the most significant bit (MSB). + // * Starting from the MSB, extract 25 bits (24 data bits + the first rounding + // bit). + // * The remaining rounding bits are guaranteed to contain at least one 1 bit, + // due to the check we did above. + // * Store the 25 bits + 1 aggregated bit in an uint32_t. + // * Convert this uint32_t to float. The conversion does the correct rounding + // now. + // * Shift the result back to the original magnitude. + uint32_t leading_zeros = base::bits::CountLeadingZeros(input); + uint32_t trailing_zeros = base::bits::CountTrailingZeros(input); + constexpr uint32_t num_extracted_bits = 25; + // Check if there are any rounding bits we have to aggregate. + if (leading_zeros + trailing_zeros + num_extracted_bits < 64) { + // Shift to extract the data bits. + uint32_t num_aggregation_bits = 64 - num_extracted_bits - leading_zeros; + // We extract the bits we want to convert. Note that we convert one bit more + // than necessary. This bit is a placeholder where we will store the + // aggregation bit. + int32_t extracted_bits = + static_cast<int32_t>(input >> (num_aggregation_bits - 1)); + // Set the aggregation bit. We don't have to clear the slot first, because + // the bit there is also part of the aggregation. + extracted_bits |= 1; + float result = static_cast<float>(extracted_bits); + // We have to shift the result back. The shift amount is + // (num_aggregation_bits - 1), which is the shift amount we did originally, + // and (-2), which is for the two additional bits we kept originally for + // rounding. + int32_t shift_back = static_cast<int32_t>(num_aggregation_bits) - 1 - 2; + // Calculate the multiplier to shift the extracted bits back to the original + // magnitude. This multiplier is a power of two, so in the float32 bit + // representation we just have to construct the correct exponent and put it + // at the correct bit offset. The exponent consists of 8 bits, starting at + // the second MSB (a.k.a '<< 23'). The encoded exponent itself is + // ('actual exponent' - 127). + int32_t multiplier_bits = ((shift_back - 127) & 0xff) << 23; + result *= bit_cast<float>(multiplier_bits); + WriteUnalignedValue<float>(data, result); + return; } - - if ((high_word & 0xFE000000) && low_word) { - // Set the rounding bit. - high_word |= 1; - } - - result = static_cast<float>(high_word); - result *= shift; - result += static_cast<float>(low_word); -#endif - - WriteUnalignedValue<float>(data, result); +#endif // defined(V8_OS_WIN) + WriteUnalignedValue<float>(data, static_cast<float>(input)); } void int64_to_float64_wrapper(Address data) { diff --git a/chromium/v8/src/wasm/wasm-feature-flags.h b/chromium/v8/src/wasm/wasm-feature-flags.h index 77d46fdc0d5..36f9ebd8a46 100644 --- a/chromium/v8/src/wasm/wasm-feature-flags.h +++ b/chromium/v8/src/wasm/wasm-feature-flags.h @@ -5,29 +5,27 @@ #ifndef V8_WASM_WASM_FEATURE_FLAGS_H_ #define V8_WASM_WASM_FEATURE_FLAGS_H_ -// The SEPARATOR argument allows generating proper comma-separated lists. -#define FOREACH_WASM_FEATURE_FLAG(V, SEPARATOR) \ - V(mv, "multi-value support", false) \ - SEPARATOR \ - V(eh, "exception handling opcodes", false) \ - SEPARATOR \ - V(se, "sign extension opcodes", true) \ - SEPARATOR \ - V(sat_f2i_conversions, "saturating float conversion opcodes", true) \ - SEPARATOR \ - V(threads, "thread opcodes", false) \ - SEPARATOR \ - V(simd, "SIMD opcodes", false) \ - SEPARATOR \ - V(anyref, "anyref opcodes", false) \ - SEPARATOR \ - V(bigint, "JS BigInt support", false) \ - SEPARATOR \ - V(bulk_memory, "bulk memory opcodes", true) \ - SEPARATOR \ - V(return_call, "return call opcodes", false) \ - SEPARATOR \ - V(type_reflection, "wasm type reflection in JS", false) \ - SEPARATOR \ +#define FOREACH_WASM_EXPERIMENTAL_FEATURE_FLAG(V) \ + V(mv, "multi-value support", false) \ + V(eh, "exception handling opcodes", false) \ + V(threads, "thread opcodes", false) \ + V(simd, "SIMD opcodes", false) \ + V(bigint, "JS BigInt support", false) \ + V(return_call, "return call opcodes", false) \ V(compilation_hints, "compilation hints section", false) + +#define FOREACH_WASM_STAGING_FEATURE_FLAG(V) \ + V(anyref, "anyref opcodes", false) \ + V(type_reflection, "wasm type reflection in JS", false) + +#define FOREACH_WASM_SHIPPED_FEATURE_FLAG(V) \ + V(bulk_memory, "bulk memory opcodes", true) \ + V(sat_f2i_conversions, "saturating float conversion opcodes", true) \ + V(se, "sign extension opcodes", true) + +#define FOREACH_WASM_FEATURE_FLAG(V) \ + FOREACH_WASM_EXPERIMENTAL_FEATURE_FLAG(V) \ + FOREACH_WASM_STAGING_FEATURE_FLAG(V) \ + FOREACH_WASM_SHIPPED_FEATURE_FLAG(V) + #endif // V8_WASM_WASM_FEATURE_FLAGS_H_ diff --git a/chromium/v8/src/wasm/wasm-features.cc b/chromium/v8/src/wasm/wasm-features.cc index fc0286655e7..d62db91750b 100644 --- a/chromium/v8/src/wasm/wasm-features.cc +++ b/chromium/v8/src/wasm/wasm-features.cc @@ -11,17 +11,17 @@ namespace v8 { namespace internal { namespace wasm { -#define COMMA , -#define SPACE -#define DO_UNION(feat, desc, val) dst->feat |= src.feat; -#define FLAG_REF(feat, desc, val) FLAG_experimental_wasm_##feat void UnionFeaturesInto(WasmFeatures* dst, const WasmFeatures& src) { - FOREACH_WASM_FEATURE(DO_UNION, SPACE); +#define DO_UNION(feat, desc, val) dst->feat |= src.feat; + FOREACH_WASM_FEATURE(DO_UNION); +#undef DO_UNION } WasmFeatures WasmFeaturesFromFlags() { - return WasmFeatures{FOREACH_WASM_FEATURE(FLAG_REF, COMMA)}; +#define FLAG_REF(feat, desc, val) FLAG_experimental_wasm_##feat, + return WasmFeatures(FOREACH_WASM_FEATURE(FLAG_REF){}); +#undef FLAG_REF } WasmFeatures WasmFeaturesFromIsolate(Isolate* isolate) { @@ -31,10 +31,6 @@ WasmFeatures WasmFeaturesFromIsolate(Isolate* isolate) { return features; } -#undef DO_UNION -#undef FLAG_REF -#undef SPACE -#undef COMMA } // namespace wasm } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/wasm/wasm-features.h b/chromium/v8/src/wasm/wasm-features.h index 2c6ab0f85a5..956982536da 100644 --- a/chromium/v8/src/wasm/wasm-features.h +++ b/chromium/v8/src/wasm/wasm-features.h @@ -17,37 +17,50 @@ namespace internal { class Isolate; namespace wasm { -#define COMMA , -#define SPACE -#define DECL_FIELD(feat, desc, val) bool feat = false; -#define JUST_TRUE(feat, desc, val) true -#define JUST_FALSE(feat, desc, val) false -#define DECL_PARAM(feat, desc, val) bool p##feat -#define DO_INIT(feat, desc, val) feat(p##feat) +// This is an empty type to indicate the end of the {WasmFeatures} struct. We +// use the {end_t} type there to avoid trailing commas that get generated by +// the macro generators. We considered the following alternatives: +// * Add "separators" to the {FOREACH_WASM_FEATURE_FLAGS} between entries. This +// does not work when we want to have different kinds of flags, e.g. for +// experimental, staging, and shipped features. +// * Use initialization lists, e.g. construct {WasmFeatures} with +// "WasmFeatures{true, true, ..., true,}". This solves the comma problem, +// because trailing commas are allowed here. However, we cannot +// default-initialize the fields of {WasmFeatures} anymore. This seems +// error-prone, because default-constructed {WasmFeatures} structs are already +// used in the code base. +// * Avoid the use of {constexpr}. With that we would be more flexible with how +// we generate {kAllWasmFeatures} and {kNoWasmFeatures}. These values may be +// used in performance-critical code, however, e.g. in the decoder or in the +// interpreter. +struct end_t {}; // Enabled or detected features. struct WasmFeatures { - FOREACH_WASM_FEATURE(DECL_FIELD, SPACE) +#define DECL_FIELD(feat, desc, val) bool feat = false; + FOREACH_WASM_FEATURE(DECL_FIELD) +#undef DECL_FIELD + // Marker for the end of the list, see the comment at {end_t}. + end_t end_; +#define DECL_PARAM(feat, desc, val) bool p##feat, +#define DO_INIT(feat, desc, val) feat(p##feat), + explicit constexpr WasmFeatures(FOREACH_WASM_FEATURE(DECL_PARAM) end_t) + : FOREACH_WASM_FEATURE(DO_INIT) end_() {} +#undef DECL_PARAM +#undef DO_INIT constexpr WasmFeatures() = default; - - explicit constexpr WasmFeatures(FOREACH_WASM_FEATURE(DECL_PARAM, COMMA)) - : FOREACH_WASM_FEATURE(DO_INIT, COMMA) {} }; -static constexpr WasmFeatures kAllWasmFeatures{ - FOREACH_WASM_FEATURE(JUST_TRUE, COMMA)}; - -static constexpr WasmFeatures kNoWasmFeatures{ - FOREACH_WASM_FEATURE(JUST_FALSE, COMMA)}; - +#define JUST_TRUE(feat, desc, val) true, +static constexpr WasmFeatures kAllWasmFeatures( + FOREACH_WASM_FEATURE(JUST_TRUE){}); #undef JUST_TRUE + +#define JUST_FALSE(feat, desc, val) false, +static constexpr WasmFeatures kNoWasmFeatures( + FOREACH_WASM_FEATURE(JUST_FALSE){}); #undef JUST_FALSE -#undef DECL_FIELD -#undef DECL_PARAM -#undef DO_INIT -#undef COMMA -#undef SPACE static constexpr WasmFeatures kAsmjsWasmFeatures = kNoWasmFeatures; diff --git a/chromium/v8/src/wasm/wasm-interpreter.cc b/chromium/v8/src/wasm/wasm-interpreter.cc index 44494398964..299128860da 100644 --- a/chromium/v8/src/wasm/wasm-interpreter.cc +++ b/chromium/v8/src/wasm/wasm-interpreter.cc @@ -1676,7 +1676,7 @@ class ThreadImpl { converter<ctype, mtype>{}(ReadLittleEndianValue<mtype>(addr))); Push(result); - *len = 1 + imm.length; + *len += imm.length; if (FLAG_trace_wasm_memory) { MemoryTracingInfo info(imm.offset + index, false, rep); @@ -1702,7 +1702,7 @@ class ThreadImpl { return false; } WriteLittleEndianValue<mtype>(addr, converter<mtype, ctype>{}(val)); - *len = 1 + imm.length; + *len += imm.length; if (FLAG_trace_wasm_memory) { MemoryTracingInfo info(imm.offset + index, true, rep); @@ -2241,14 +2241,27 @@ class ThreadImpl { Push(WasmValue(Simd128(res))); \ return true; \ } + BINOP_CASE(F64x2Add, f64x2, float2, 2, a + b) + BINOP_CASE(F64x2Sub, f64x2, float2, 2, a - b) + BINOP_CASE(F64x2Mul, f64x2, float2, 2, a * b) + BINOP_CASE(F64x2Div, f64x2, float2, 2, base::Divide(a, b)) + BINOP_CASE(F64x2Min, f64x2, float2, 2, JSMin(a, b)) + BINOP_CASE(F64x2Max, f64x2, float2, 2, JSMax(a, b)) BINOP_CASE(F32x4Add, f32x4, float4, 4, a + b) BINOP_CASE(F32x4Sub, f32x4, float4, 4, a - b) BINOP_CASE(F32x4Mul, f32x4, float4, 4, a * b) - BINOP_CASE(F32x4Min, f32x4, float4, 4, a < b ? a : b) - BINOP_CASE(F32x4Max, f32x4, float4, 4, a > b ? a : b) + BINOP_CASE(F32x4Div, f32x4, float4, 4, a / b) + BINOP_CASE(F32x4Min, f32x4, float4, 4, JSMin(a, b)) + BINOP_CASE(F32x4Max, f32x4, float4, 4, JSMax(a, b)) BINOP_CASE(I64x2Add, i64x2, int2, 2, base::AddWithWraparound(a, b)) BINOP_CASE(I64x2Sub, i64x2, int2, 2, base::SubWithWraparound(a, b)) BINOP_CASE(I64x2Mul, i64x2, int2, 2, base::MulWithWraparound(a, b)) + BINOP_CASE(I64x2MinS, i64x2, int2, 2, a < b ? a : b) + BINOP_CASE(I64x2MinU, i64x2, int2, 2, + static_cast<uint64_t>(a) < static_cast<uint64_t>(b) ? a : b) + BINOP_CASE(I64x2MaxS, i64x2, int2, 2, a > b ? a : b) + BINOP_CASE(I64x2MaxU, i64x2, int2, 2, + static_cast<uint64_t>(a) > static_cast<uint64_t>(b) ? a : b) BINOP_CASE(I32x4Add, i32x4, int4, 4, base::AddWithWraparound(a, b)) BINOP_CASE(I32x4Sub, i32x4, int4, 4, base::SubWithWraparound(a, b)) BINOP_CASE(I32x4Mul, i32x4, int4, 4, base::MulWithWraparound(a, b)) @@ -2422,40 +2435,32 @@ class ThreadImpl { case kExprS128StoreMem: return ExecuteStore<Simd128, Simd128>(decoder, code, pc, len, MachineRepresentation::kSimd128); -#define SHIFT_CASE(op, name, stype, count, expr) \ - case kExpr##op: { \ - SimdShiftImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc)); \ - *len += 1; \ - WasmValue v = Pop(); \ - stype s = v.to_s128().to_##name(); \ - stype res; \ - for (size_t i = 0; i < count; ++i) { \ - auto a = s.val[i]; \ - res.val[i] = expr; \ - } \ - Push(WasmValue(Simd128(res))); \ - return true; \ - } - SHIFT_CASE(I64x2Shl, i64x2, int2, 2, - static_cast<uint64_t>(a) << imm.shift) - SHIFT_CASE(I64x2ShrS, i64x2, int2, 2, a >> imm.shift) - SHIFT_CASE(I64x2ShrU, i64x2, int2, 2, - static_cast<uint64_t>(a) >> imm.shift) - SHIFT_CASE(I32x4Shl, i32x4, int4, 4, - static_cast<uint32_t>(a) << imm.shift) - SHIFT_CASE(I32x4ShrS, i32x4, int4, 4, a >> imm.shift) - SHIFT_CASE(I32x4ShrU, i32x4, int4, 4, - static_cast<uint32_t>(a) >> imm.shift) - SHIFT_CASE(I16x8Shl, i16x8, int8, 8, - static_cast<uint16_t>(a) << imm.shift) - SHIFT_CASE(I16x8ShrS, i16x8, int8, 8, a >> imm.shift) - SHIFT_CASE(I16x8ShrU, i16x8, int8, 8, - static_cast<uint16_t>(a) >> imm.shift) - SHIFT_CASE(I8x16Shl, i8x16, int16, 16, - static_cast<uint8_t>(a) << imm.shift) - SHIFT_CASE(I8x16ShrS, i8x16, int16, 16, a >> imm.shift) +#define SHIFT_CASE(op, name, stype, count, expr) \ + case kExpr##op: { \ + uint32_t shift = Pop().to<uint32_t>(); \ + WasmValue v = Pop(); \ + stype s = v.to_s128().to_##name(); \ + stype res; \ + for (size_t i = 0; i < count; ++i) { \ + auto a = s.val[i]; \ + res.val[i] = expr; \ + } \ + Push(WasmValue(Simd128(res))); \ + return true; \ + } + SHIFT_CASE(I64x2Shl, i64x2, int2, 2, static_cast<uint64_t>(a) << shift) + SHIFT_CASE(I64x2ShrS, i64x2, int2, 2, a >> shift) + SHIFT_CASE(I64x2ShrU, i64x2, int2, 2, static_cast<uint64_t>(a) >> shift) + SHIFT_CASE(I32x4Shl, i32x4, int4, 4, static_cast<uint32_t>(a) << shift) + SHIFT_CASE(I32x4ShrS, i32x4, int4, 4, a >> shift) + SHIFT_CASE(I32x4ShrU, i32x4, int4, 4, static_cast<uint32_t>(a) >> shift) + SHIFT_CASE(I16x8Shl, i16x8, int8, 8, static_cast<uint16_t>(a) << shift) + SHIFT_CASE(I16x8ShrS, i16x8, int8, 8, a >> shift) + SHIFT_CASE(I16x8ShrU, i16x8, int8, 8, static_cast<uint16_t>(a) >> shift) + SHIFT_CASE(I8x16Shl, i8x16, int16, 16, static_cast<uint8_t>(a) << shift) + SHIFT_CASE(I8x16ShrS, i8x16, int16, 16, a >> shift) SHIFT_CASE(I8x16ShrU, i8x16, int16, 16, - static_cast<uint8_t>(a) >> imm.shift) + static_cast<uint8_t>(a) >> shift) #undef SHIFT_CASE #define CONVERT_CASE(op, src_type, name, dst_type, count, start_index, ctype, \ expr) \ @@ -3042,8 +3047,8 @@ class ThreadImpl { code->at(pc)); HandleScope handle_scope(isolate_); // Avoid leaking handles. - Handle<WasmExportedFunction> function = - WasmInstanceObject::GetOrCreateWasmExportedFunction( + Handle<WasmExternalFunction> function = + WasmInstanceObject::GetOrCreateWasmExternalFunction( isolate_, instance_object_, imm.index); Push(WasmValue(function)); len = 1 + imm.length; @@ -3679,7 +3684,7 @@ class ThreadImpl { WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate); if (code->kind() == WasmCode::kWasmToJsWrapper && - !IsJSCompatibleSignature(sig, enabled_features.bigint)) { + !IsJSCompatibleSignature(sig, enabled_features)) { Drop(num_args); // Pop arguments before throwing. isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kWasmTrapTypeError)); diff --git a/chromium/v8/src/wasm/wasm-js.cc b/chromium/v8/src/wasm/wasm-js.cc index 1ee76fc11df..f10f5ff2bfe 100644 --- a/chromium/v8/src/wasm/wasm-js.cc +++ b/chromium/v8/src/wasm/wasm-js.cc @@ -1223,6 +1223,9 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe, } else if (enabled_features.anyref && string->StringEquals(v8_str(isolate, "anyfunc"))) { *type = i::wasm::kWasmFuncRef; + } else if (enabled_features.eh && + string->StringEquals(v8_str(isolate, "exnref"))) { + *type = i::wasm::kWasmExnRef; } else { // Unrecognized type. *type = i::wasm::kWasmStmt; @@ -1337,7 +1340,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { global_obj->SetF64(f64_value); break; } - case i::wasm::kWasmAnyRef: { + case i::wasm::kWasmAnyRef: + case i::wasm::kWasmExnRef: { 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'. @@ -1379,6 +1383,21 @@ void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) { thrower.TypeError("WebAssembly.Exception cannot be called"); } +namespace { + +uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context, + Local<Object> iterable) { + Local<String> length = Utils::ToLocal(isolate->factory()->length_string()); + MaybeLocal<Value> property = iterable->Get(context, length); + if (property.IsEmpty()) return i::kMaxUInt32; + MaybeLocal<Uint32> number = property.ToLocalChecked()->ToArrayIndex(context); + if (number.IsEmpty()) return i::kMaxUInt32; + DCHECK_NE(i::kMaxUInt32, number.ToLocalChecked()->Value()); + return number.ToLocalChecked()->Value(); +} + +} // namespace + // WebAssembly.Function void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); @@ -1403,13 +1422,16 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { function_type->Get(context, parameters_key); v8::Local<v8::Value> parameters_value; if (!parameters_maybe.ToLocal(¶meters_value)) return; - // TODO(7742): Allow any iterable, not just {Array} here. - if (!parameters_value->IsArray()) { + if (!parameters_value->IsObject()) { thrower.TypeError("Argument 0 must be a function type with 'parameters'"); return; } - Local<Array> parameters = parameters_value.As<Array>(); - uint32_t parameters_len = parameters->Length(); + Local<Object> parameters = parameters_value.As<Object>(); + uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters); + if (parameters_len == i::kMaxUInt32) { + thrower.TypeError("Argument 0 contains parameters without 'length'"); + return; + } if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) { thrower.TypeError("Argument 0 contains too many parameters"); return; @@ -1421,13 +1443,16 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { function_type->Get(context, results_key); v8::Local<v8::Value> results_value; if (!results_maybe.ToLocal(&results_value)) return; - // TODO(7742): Allow any iterable, not just {Array} here. - if (!results_value->IsArray()) { + if (!results_value->IsObject()) { thrower.TypeError("Argument 0 must be a function type with 'results'"); return; } - Local<Array> results = results_value.As<Array>(); - uint32_t results_len = results->Length(); + Local<Object> results = results_value.As<Object>(); + uint32_t results_len = GetIterableLength(i_isolate, context, results); + if (results_len == i::kMaxUInt32) { + thrower.TypeError("Argument 0 contains results without 'length'"); + return; + } if (results_len > (enabled_features.mv ? i::wasm::kV8MaxWasmFunctionMultiReturns : i::wasm::kV8MaxWasmFunctionReturns)) { @@ -1474,37 +1499,6 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(Utils::ToLocal(result)); } -// Converts the given {type} into a string representation that can be used in -// reflective functions. Should be kept in sync with the {GetValueType} helper. -Local<String> ToValueTypeString(Isolate* isolate, i::wasm::ValueType type) { - Local<String> string; - switch (type) { - case i::wasm::kWasmI32: { - string = v8_str(isolate, "i32"); - break; - } - case i::wasm::kWasmI64: { - string = v8_str(isolate, "i64"); - break; - } - case i::wasm::kWasmF32: { - string = v8_str(isolate, "f32"); - break; - } - case i::wasm::kWasmF64: { - string = v8_str(isolate, "f64"); - break; - } - case i::wasm::kWasmAnyRef: { - string = v8_str(isolate, "anyref"); - break; - } - default: - UNREACHABLE(); - } - return string; -} - // WebAssembly.Function.type(WebAssembly.Function) -> FunctionType void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); @@ -1524,36 +1518,8 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } - // Extract values for the {ValueType[]} arrays. - size_t param_index = 0; - i::ScopedVector<Local<Value>> param_values(sig->parameter_count()); - for (i::wasm::ValueType type : sig->parameters()) { - param_values[param_index++] = ToValueTypeString(isolate, type); - } - size_t result_index = 0; - i::ScopedVector<Local<Value>> result_values(sig->return_count()); - for (i::wasm::ValueType type : sig->returns()) { - result_values[result_index++] = ToValueTypeString(isolate, type); - } - - // Create the resulting {FunctionType} object. - Local<Object> ret = v8::Object::New(isolate); - Local<Context> context = isolate->GetCurrentContext(); - Local<Array> params = - v8::Array::New(isolate, param_values.begin(), param_values.size()); - if (!ret->CreateDataProperty(context, v8_str(isolate, "parameters"), params) - .IsJust()) { - return; - } - Local<Array> results = - v8::Array::New(isolate, result_values.begin(), result_values.size()); - if (!ret->CreateDataProperty(context, v8_str(isolate, "results"), results) - .IsJust()) { - return; - } - - v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); - return_value.Set(ret); + auto type = i::wasm::GetTypeForFunction(i_isolate, sig); + args.GetReturnValue().Set(Utils::ToLocal(type)); } constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global"; @@ -1681,48 +1647,15 @@ void WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value>& args) { auto maybe_table = GetFirstArgumentAsTable(args, &thrower); if (thrower.error()) return; i::Handle<i::WasmTableObject> table = maybe_table.ToHandleChecked(); - v8::Local<v8::Object> ret = v8::Object::New(isolate); - - Local<String> element; - auto enabled_features = i::wasm::WasmFeaturesFromFlags(); - if (table->type() == i::wasm::ValueType::kWasmFuncRef) { - element = v8_str(isolate, "anyfunc"); - } else if (enabled_features.anyref && - table->type() == i::wasm::ValueType::kWasmAnyRef) { - element = v8_str(isolate, "anyref"); - } else { - UNREACHABLE(); - } - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "element"), element) - .IsJust()) { - return; - } - - uint32_t curr_size = table->current_length(); - DCHECK_LE(curr_size, std::numeric_limits<uint32_t>::max()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "minimum"), - v8::Integer::NewFromUnsigned( - isolate, static_cast<uint32_t>(curr_size))) - .IsJust()) { - return; - } - + base::Optional<uint32_t> max_size; if (!table->maximum_length().IsUndefined()) { - uint64_t max_size = table->maximum_length().Number(); - DCHECK_LE(max_size, std::numeric_limits<uint32_t>::max()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "maximum"), - v8::Integer::NewFromUnsigned( - isolate, static_cast<uint32_t>(max_size))) - .IsJust()) { - return; - } + uint64_t max_size64 = table->maximum_length().Number(); + DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max()); + max_size.emplace(static_cast<uint32_t>(max_size64)); } - - v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); - return_value.Set(ret); + auto type = i::wasm::GetTypeForTable(i_isolate, table->type(), + table->current_length(), max_size); + args.GetReturnValue().Set(Utils::ToLocal(type)); } // WebAssembly.Memory.grow(num) -> num @@ -1802,33 +1735,18 @@ void WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value>& args) { auto maybe_memory = GetFirstArgumentAsMemory(args, &thrower); if (thrower.error()) return; i::Handle<i::WasmMemoryObject> memory = maybe_memory.ToHandleChecked(); - v8::Local<v8::Object> ret = v8::Object::New(isolate); i::Handle<i::JSArrayBuffer> buffer(memory->array_buffer(), i_isolate); - size_t curr_size = buffer->byte_length() / i::wasm::kWasmPageSize; DCHECK_LE(curr_size, std::numeric_limits<uint32_t>::max()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "minimum"), - v8::Integer::NewFromUnsigned( - isolate, static_cast<uint32_t>(curr_size))) - .IsJust()) { - return; - } - + uint32_t min_size = static_cast<uint32_t>(curr_size); + base::Optional<uint32_t> max_size; if (memory->has_maximum_pages()) { - uint64_t max_size = memory->maximum_pages(); - DCHECK_LE(max_size, std::numeric_limits<uint32_t>::max()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "maximum"), - v8::Integer::NewFromUnsigned( - isolate, static_cast<uint32_t>(max_size))) - .IsJust()) { - return; - } + uint64_t max_size64 = memory->maximum_pages(); + DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max()); + max_size.emplace(static_cast<uint32_t>(max_size64)); } - - v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); - return_value.Set(ret); + auto type = i::wasm::GetTypeForMemory(i_isolate, min_size, max_size); + args.GetReturnValue().Set(Utils::ToLocal(type)); } void WebAssemblyGlobalGetValueCommon( @@ -1960,24 +1878,9 @@ void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) { auto maybe_global = GetFirstArgumentAsGlobal(args, &thrower); if (thrower.error()) return; i::Handle<i::WasmGlobalObject> global = maybe_global.ToHandleChecked(); - v8::Local<v8::Object> ret = v8::Object::New(isolate); - - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "mutable"), - v8::Boolean::New(isolate, global->is_mutable())) - .IsJust()) { - return; - } - - Local<String> type = ToValueTypeString(isolate, global->type()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "value"), type) - .IsJust()) { - return; - } - - v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); - return_value.Set(ret); + auto type = i::wasm::GetTypeForGlobal(i_isolate, global->is_mutable(), + global->type()); + args.GetReturnValue().Set(Utils::ToLocal(type)); } } // namespace diff --git a/chromium/v8/src/wasm/wasm-module-builder.cc b/chromium/v8/src/wasm/wasm-module-builder.cc index 7dd6b1c7b24..d3874e1a344 100644 --- a/chromium/v8/src/wasm/wasm-module-builder.cc +++ b/chromium/v8/src/wasm/wasm-module-builder.cc @@ -233,6 +233,7 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone) global_imports_(zone), exports_(zone), functions_(zone), + tables_(zone), data_segments_(zone), indirect_functions_(zone), globals_(zone), @@ -269,15 +270,29 @@ uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) { } uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) { + DCHECK(allocating_indirect_functions_allowed_); uint32_t index = static_cast<uint32_t>(indirect_functions_.size()); DCHECK_GE(FLAG_wasm_max_table_size, index); if (count > FLAG_wasm_max_table_size - index) { return std::numeric_limits<uint32_t>::max(); } - DCHECK(max_table_size_ == 0 || - indirect_functions_.size() + count <= max_table_size_); - indirect_functions_.resize(indirect_functions_.size() + count, - WasmElemSegment::kNullIndex); + uint32_t new_size = static_cast<uint32_t>(indirect_functions_.size()) + count; + DCHECK(max_table_size_ == 0 || new_size <= max_table_size_); + indirect_functions_.resize(new_size, WasmElemSegment::kNullIndex); + uint32_t max = max_table_size_ > 0 ? max_table_size_ : new_size; + if (tables_.empty()) { + // This cannot use {AddTable} because that would flip the + // {allocating_indirect_functions_allowed_} flag. + tables_.push_back({kWasmFuncRef, new_size, max, true}); + } else { + // There can only be the indirect function table so far, otherwise the + // {allocating_indirect_functions_allowed_} flag would have been false. + DCHECK_EQ(1u, tables_.size()); + DCHECK_EQ(kWasmFuncRef, tables_[0].type); + DCHECK(tables_[0].has_maximum); + tables_[0].min_size = new_size; + tables_[0].max_size = max; + } return index; } @@ -290,6 +305,27 @@ void WasmModuleBuilder::SetMaxTableSize(uint32_t max) { DCHECK_GE(FLAG_wasm_max_table_size, max); DCHECK_GE(max, indirect_functions_.size()); max_table_size_ = max; + DCHECK(allocating_indirect_functions_allowed_); + if (!tables_.empty()) { + tables_[0].max_size = max; + } +} + +uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size) { +#if DEBUG + allocating_indirect_functions_allowed_ = false; +#endif + tables_.push_back({type, min_size, 0, false}); + return static_cast<uint32_t>(tables_.size() - 1); +} + +uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size, + uint32_t max_size) { +#if DEBUG + allocating_indirect_functions_allowed_ = false; +#endif + tables_.push_back({type, min_size, max_size, true}); + return static_cast<uint32_t>(tables_.size() - 1); } uint32_t WasmModuleBuilder::AddImport(Vector<const char> name, @@ -408,21 +444,20 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { FixupSection(buffer, start); } - // == emit function table ==================================================== - if (indirect_functions_.size() > 0) { + // == Emit tables ============================================================ + if (tables_.size() > 0) { size_t start = EmitSection(kTableSectionCode, buffer); - buffer->write_u8(1); // table count - buffer->write_u8(kLocalFuncRef); - buffer->write_u8(kHasMaximumFlag); - buffer->write_size(indirect_functions_.size()); - size_t max = - max_table_size_ > 0 ? max_table_size_ : indirect_functions_.size(); - DCHECK_GE(max, indirect_functions_.size()); - buffer->write_size(max); + buffer->write_size(tables_.size()); + for (const WasmTable& table : tables_) { + buffer->write_u8(ValueTypes::ValueTypeCodeFor(table.type)); + buffer->write_u8(table.has_maximum ? kHasMaximumFlag : kNoMaximumFlag); + buffer->write_size(table.min_size); + if (table.has_maximum) buffer->write_size(table.max_size); + } FixupSection(buffer, start); } - // == emit memory declaration ================================================ + // == Emit memory declaration ================================================ { size_t start = EmitSection(kMemorySectionCode, buffer); buffer->write_u8(1); // memory count @@ -473,7 +508,13 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { buffer->write_u8(kExprGetGlobal); buffer->write_u32v(global.init.val.global_index); break; - default: { + case WasmInitExpr::kRefNullConst: + buffer->write_u8(kExprRefNull); + break; + case WasmInitExpr::kRefFuncConst: + UNIMPLEMENTED(); + break; + case WasmInitExpr::kNone: { // No initializer, emit a default value. switch (global.type) { case kWasmI32: diff --git a/chromium/v8/src/wasm/wasm-module-builder.h b/chromium/v8/src/wasm/wasm-module-builder.h index 9e6a8933e2f..4c122b80623 100644 --- a/chromium/v8/src/wasm/wasm-module-builder.h +++ b/chromium/v8/src/wasm/wasm-module-builder.h @@ -243,6 +243,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { uint32_t AllocateIndirectFunctions(uint32_t count); void SetIndirectFunction(uint32_t indirect, uint32_t direct); void SetMaxTableSize(uint32_t max); + uint32_t AddTable(ValueType type, uint32_t min_size); + uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size); void MarkStartFunction(WasmFunctionBuilder* builder); void AddExport(Vector<const char> name, ImportExportKindCode kind, uint32_t index); @@ -288,6 +290,13 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { WasmInitExpr init; }; + struct WasmTable { + ValueType type; + uint32_t min_size; + uint32_t max_size; + bool has_maximum; + }; + struct WasmDataSegment { ZoneVector<byte> data; uint32_t dest; @@ -300,6 +309,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ZoneVector<WasmGlobalImport> global_imports_; ZoneVector<WasmExport> exports_; ZoneVector<WasmFunctionBuilder*> functions_; + ZoneVector<WasmTable> tables_; ZoneVector<WasmDataSegment> data_segments_; ZoneVector<uint32_t> indirect_functions_; ZoneVector<WasmGlobal> globals_; @@ -313,6 +323,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { #if DEBUG // Once AddExportedImport is called, no more imports can be added. bool adding_imports_allowed_ = true; + // Indirect functions must be allocated before adding extra tables. + bool allocating_indirect_functions_allowed_ = true; #endif }; diff --git a/chromium/v8/src/wasm/wasm-module-sourcemap.cc b/chromium/v8/src/wasm/wasm-module-sourcemap.cc new file mode 100644 index 00000000000..cfe54e7c375 --- /dev/null +++ b/chromium/v8/src/wasm/wasm-module-sourcemap.cc @@ -0,0 +1,161 @@ +// Copyright 2019 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. + +#include "src/wasm/wasm-module-sourcemap.h" + +#include <algorithm> + +#include "include/v8.h" +#include "src/api/api.h" +#include "src/base/vlq-base64.h" + +namespace v8 { +namespace internal { +namespace wasm { + +WasmModuleSourceMap::WasmModuleSourceMap(v8::Isolate* v8_isolate, + v8::Local<v8::String> src_map_str) { + v8::HandleScope scope(v8_isolate); + v8::Local<v8::Context> context = v8::Context::New(v8_isolate); + + v8::Local<v8::Value> src_map_value; + if (!v8::JSON::Parse(context, src_map_str).ToLocal(&src_map_value)) return; + v8::Local<v8::Object> src_map_obj = + v8::Local<v8::Object>::Cast(src_map_value); + + v8::Local<v8::Value> version_value, sources_value, mappings_value; + bool has_valid_version = + src_map_obj + ->Get(context, + v8::String::NewFromUtf8(v8_isolate, "version").ToLocalChecked()) + .ToLocal(&version_value) && + version_value->IsUint32(); + uint32_t version = 0; + if (!has_valid_version || !version_value->Uint32Value(context).To(&version) || + version != 3u) + return; + + bool has_valid_sources = + src_map_obj + ->Get(context, + v8::String::NewFromUtf8(v8_isolate, "sources").ToLocalChecked()) + .ToLocal(&sources_value) && + sources_value->IsArray(); + if (!has_valid_sources) return; + + v8::Local<v8::Object> sources_arr = + v8::Local<v8::Object>::Cast(sources_value); + v8::Local<v8::Value> sources_len_value; + if (!sources_arr + ->Get(context, + v8::String::NewFromUtf8(v8_isolate, "length").ToLocalChecked()) + .ToLocal(&sources_len_value)) + return; + uint32_t sources_len = 0; + if (!sources_len_value->Uint32Value(context).To(&sources_len)) return; + + for (uint32_t i = 0; i < sources_len; ++i) { + v8::Local<v8::Value> file_name_value; + if (!sources_arr->Get(context, i).ToLocal(&file_name_value) || + !file_name_value->IsString()) + return; + v8::Local<v8::String> file_name = + v8::Local<v8::String>::Cast(file_name_value); + auto file_name_sz = file_name->Utf8Length(v8_isolate); + std::unique_ptr<char[]> file_name_buf(new char[file_name_sz + 1]); + file_name->WriteUtf8(v8_isolate, file_name_buf.get()); + file_name_buf.get()[file_name_sz] = '\0'; + filenames.emplace_back(file_name_buf.get()); + } + + bool has_valid_mappings = + src_map_obj + ->Get( + context, + v8::String::NewFromUtf8(v8_isolate, "mappings").ToLocalChecked()) + .ToLocal(&mappings_value) && + mappings_value->IsString(); + if (!has_valid_mappings) return; + + v8::Local<v8::String> mappings = v8::Local<v8::String>::Cast(mappings_value); + int mappings_sz = mappings->Utf8Length(v8_isolate); + std::unique_ptr<char[]> mappings_buf(new char[mappings_sz + 1]); + mappings->WriteUtf8(v8_isolate, mappings_buf.get()); + mappings_buf.get()[mappings_sz] = '\0'; + + valid_ = DecodeMapping(mappings_buf.get()); +} + +size_t WasmModuleSourceMap::GetSourceLine(size_t wasm_offset) const { + std::vector<std::size_t>::const_iterator up = + std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); + CHECK_NE(offsets.begin(), up); + size_t source_idx = up - offsets.begin() - 1; + return source_row[source_idx]; +} + +std::string WasmModuleSourceMap::GetFilename(size_t wasm_offset) const { + std::vector<size_t>::const_iterator up = + std::upper_bound(offsets.begin(), offsets.end(), wasm_offset); + CHECK_NE(offsets.begin(), up); + size_t offset_idx = up - offsets.begin() - 1; + size_t filename_idx = file_idxs[offset_idx]; + return filenames[filename_idx]; +} + +bool WasmModuleSourceMap::HasSource(size_t start, size_t end) const { + return start <= *(offsets.end() - 1) && end > *offsets.begin(); +} + +bool WasmModuleSourceMap::HasValidEntry(size_t start, size_t addr) const { + std::vector<size_t>::const_iterator up = + std::upper_bound(offsets.begin(), offsets.end(), addr); + if (up == offsets.begin()) return false; + size_t offset_idx = up - offsets.begin() - 1; + size_t entry_offset = offsets[offset_idx]; + if (entry_offset < start) return false; + return true; +} + +bool WasmModuleSourceMap::DecodeMapping(const std::string& s) { + size_t pos = 0, gen_col = 0, file_idx = 0, ori_line = 0; + int32_t qnt = 0; + + while (pos < s.size()) { + // Skip redundant commas. + if (s[pos] == ',') { + ++pos; + continue; + } + if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == + std::numeric_limits<int32_t>::min()) + return false; + gen_col += qnt; + if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == + std::numeric_limits<int32_t>::min()) + return false; + file_idx += qnt; + if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == + std::numeric_limits<int32_t>::min()) + return false; + ori_line += qnt; + // Column number in source file is always 0 in source map generated by + // Emscripten. We just decode this value without further usage of it. + if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) == + std::numeric_limits<int32_t>::min()) + return false; + + if (pos < s.size() && s[pos] != ',') return false; + pos++; + + file_idxs.push_back(file_idx); + source_row.push_back(ori_line); + offsets.push_back(gen_col); + } + return true; +} + +} // namespace wasm +} // namespace internal +} // namespace v8 diff --git a/chromium/v8/src/wasm/wasm-module-sourcemap.h b/chromium/v8/src/wasm/wasm-module-sourcemap.h new file mode 100644 index 00000000000..83293ae2050 --- /dev/null +++ b/chromium/v8/src/wasm/wasm-module-sourcemap.h @@ -0,0 +1,83 @@ +// Copyright 2019 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. + +#ifndef V8_WASM_WASM_MODULE_SOURCEMAP_H_ +#define V8_WASM_WASM_MODULE_SOURCEMAP_H_ + +#include <string> +#include <vector> + +#include "include/v8.h" +#include "src/base/macros.h" + +namespace v8 { +namespace internal { +namespace wasm { +// The class is for decoding and managing source map generated by a WebAssembly +// toolchain (e.g. Emscripten). This implementation mostly complies with the +// specification (https://sourcemaps.info/spec.html), with the following +// accommodations: +// 1. "names" field is an empty array in current source maps of WASM, hence it +// is not handled; +// 2. The semicolons divides "mappings" field into groups, each of which +// represents a line in the generated code. As *.wasm is in binary format, there +// is one "line" of generated code, and ";" is treated as illegal symbol in +// "mappings". +// 3. Though each comma-separated section may contains 1, 4 or 5 fields, we only +// consider "mappings" with 4 fields, i.e. start line of generated code, index +// into "sources" fields, start line of source code and start column of source +// code. +class V8_EXPORT_PRIVATE WasmModuleSourceMap { + public: + WasmModuleSourceMap(v8::Isolate* v8_isolate, + v8::Local<v8::String> src_map_str); + + // Member valid_ is true only if the source map complies with specification + // and can be correctly decoded. + bool IsValid() const { return valid_; } + + // Given a function located at [start, end) in WASM Module, this function + // checks if this function has its corresponding source code. + bool HasSource(size_t start, size_t end) const; + + // Given a function's base address start and an address addr within, this + // function checks if the address can be mapped to an offset in this function. + // For example, we have the following memory layout for WASM functions, foo + // and bar, and O1, O2, O3 and O4 are the decoded offsets of source map: + // + // O1 --- O2 ----- O3 ----- O4 + // --->|<-foo->|<--bar->|<----- + // --------------A------------- + // + // Address A of function bar should be mapped to its nearest lower offset, O2. + // However, O2 is an address of function foo, thus, this mapping is treated as + // invalid. + bool HasValidEntry(size_t start, size_t addr) const; + + // This function is responsible for looking up an offset's corresponding line + // number in source file. It should only be called when current function is + // checked with IsValid, HasSource and HasValidEntry. + size_t GetSourceLine(size_t wasm_offset) const; + + // This function is responsible for looking up an offset's corresponding + // source file name. It should only be called when current function is checked + // with IsValid, HasSource and HasValidEntry. + std::string GetFilename(size_t wasm_offset) const; + + private: + std::vector<size_t> offsets; + std::vector<std::string> filenames; + std::vector<size_t> file_idxs; + std::vector<size_t> source_row; + // As column number in source file is always 0 in source map generated by + // WebAssembly toolchain, we will not store this value. + + bool valid_ = false; + + bool DecodeMapping(const std::string& s); +}; +} // namespace wasm +} // namespace internal +} // namespace v8 +#endif // V8_WASM_WASM_MODULE_SOURCEMAP_H_ diff --git a/chromium/v8/src/wasm/wasm-module.cc b/chromium/v8/src/wasm/wasm-module.cc index 05057301ed6..5a10368a8b6 100644 --- a/chromium/v8/src/wasm/wasm-module.cc +++ b/chromium/v8/src/wasm/wasm-module.cc @@ -113,13 +113,156 @@ bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) { v8::Utils::ToLocal(isolate->factory()->empty_string())); } +namespace { + +// Converts the given {type} into a string representation that can be used in +// reflective functions. Should be kept in sync with the {GetValueType} helper. +Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) { + Factory* factory = isolate->factory(); + Handle<String> string; + switch (type) { + case i::wasm::kWasmI32: { + string = factory->InternalizeUtf8String("i32"); + break; + } + case i::wasm::kWasmI64: { + string = factory->InternalizeUtf8String("i64"); + break; + } + case i::wasm::kWasmF32: { + string = factory->InternalizeUtf8String("f32"); + break; + } + case i::wasm::kWasmF64: { + string = factory->InternalizeUtf8String("f64"); + break; + } + case i::wasm::kWasmAnyRef: { + string = factory->InternalizeUtf8String("anyref"); + break; + } + case i::wasm::kWasmFuncRef: { + string = factory->InternalizeUtf8String("anyfunc"); + break; + } + case i::wasm::kWasmExnRef: { + string = factory->InternalizeUtf8String("exnref"); + break; + } + default: + UNREACHABLE(); + } + return string; +} + +} // namespace + +Handle<JSObject> GetTypeForFunction(Isolate* isolate, FunctionSig* sig) { + Factory* factory = isolate->factory(); + + // Extract values for the {ValueType[]} arrays. + int param_index = 0; + int param_count = static_cast<int>(sig->parameter_count()); + Handle<FixedArray> param_values = factory->NewFixedArray(param_count); + for (ValueType type : sig->parameters()) { + Handle<String> type_value = ToValueTypeString(isolate, type); + param_values->set(param_index++, *type_value); + } + int result_index = 0; + int result_count = static_cast<int>(sig->return_count()); + Handle<FixedArray> result_values = factory->NewFixedArray(result_count); + for (ValueType type : sig->returns()) { + Handle<String> type_value = ToValueTypeString(isolate, type); + result_values->set(result_index++, *type_value); + } + + // Create the resulting {FunctionType} object. + Handle<JSFunction> object_function = isolate->object_function(); + Handle<JSObject> object = factory->NewJSObject(object_function); + Handle<JSArray> params = factory->NewJSArrayWithElements(param_values); + Handle<JSArray> results = factory->NewJSArrayWithElements(result_values); + Handle<String> params_string = factory->InternalizeUtf8String("parameters"); + Handle<String> results_string = factory->InternalizeUtf8String("results"); + JSObject::AddProperty(isolate, object, params_string, params, NONE); + JSObject::AddProperty(isolate, object, results_string, results, NONE); + + return object; +} + +Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, + ValueType type) { + Factory* factory = isolate->factory(); + + Handle<JSFunction> object_function = isolate->object_function(); + Handle<JSObject> object = factory->NewJSObject(object_function); + Handle<String> mutable_string = factory->InternalizeUtf8String("mutable"); + Handle<String> value_string = factory->InternalizeUtf8String("value"); + JSObject::AddProperty(isolate, object, mutable_string, + factory->ToBoolean(is_mutable), NONE); + JSObject::AddProperty(isolate, object, value_string, + ToValueTypeString(isolate, type), NONE); + + return object; +} + +Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size, + base::Optional<uint32_t> max_size) { + Factory* factory = isolate->factory(); + + Handle<JSFunction> object_function = isolate->object_function(); + Handle<JSObject> object = factory->NewJSObject(object_function); + Handle<String> minimum_string = factory->InternalizeUtf8String("minimum"); + Handle<String> maximum_string = factory->InternalizeUtf8String("maximum"); + JSObject::AddProperty(isolate, object, minimum_string, + factory->NewNumberFromUint(min_size), NONE); + if (max_size.has_value()) { + JSObject::AddProperty(isolate, object, maximum_string, + factory->NewNumberFromUint(max_size.value()), NONE); + } + + return object; +} + +Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type, + uint32_t min_size, + base::Optional<uint32_t> max_size) { + Factory* factory = isolate->factory(); + + Handle<String> element; + if (type == ValueType::kWasmFuncRef) { + // TODO(wasm): We should define the "anyfunc" string in one central place + // and then use that constant everywhere. + element = factory->InternalizeUtf8String("anyfunc"); + } else { + DCHECK(WasmFeaturesFromFlags().anyref && type == ValueType::kWasmAnyRef); + element = factory->InternalizeUtf8String("anyref"); + } + + Handle<JSFunction> object_function = isolate->object_function(); + Handle<JSObject> object = factory->NewJSObject(object_function); + Handle<String> element_string = factory->InternalizeUtf8String("element"); + Handle<String> minimum_string = factory->InternalizeUtf8String("minimum"); + Handle<String> maximum_string = factory->InternalizeUtf8String("maximum"); + JSObject::AddProperty(isolate, object, element_string, element, NONE); + JSObject::AddProperty(isolate, object, minimum_string, + factory->NewNumberFromUint(min_size), NONE); + if (max_size.has_value()) { + JSObject::AddProperty(isolate, object, maximum_string, + factory->NewNumberFromUint(max_size.value()), NONE); + } + + return object; +} + Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module_object) { + auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate); Factory* factory = isolate->factory(); Handle<String> module_string = factory->InternalizeUtf8String("module"); Handle<String> name_string = factory->InternalizeUtf8String("name"); Handle<String> kind_string = factory->InternalizeUtf8String("kind"); + Handle<String> type_string = factory->InternalizeUtf8String("type"); Handle<String> function_string = factory->InternalizeUtf8String("function"); Handle<String> table_string = factory->InternalizeUtf8String("table"); @@ -145,17 +288,43 @@ Handle<JSArray> GetImports(Isolate* isolate, Handle<JSObject> entry = factory->NewJSObject(object_function); Handle<String> import_kind; + Handle<JSObject> type_value; switch (import.kind) { case kExternalFunction: + if (enabled_features.type_reflection) { + auto& func = module->functions[import.index]; + type_value = GetTypeForFunction(isolate, func.sig); + } import_kind = function_string; break; case kExternalTable: + if (enabled_features.type_reflection) { + auto& table = module->tables[import.index]; + base::Optional<uint32_t> maximum_size; + if (table.has_maximum_size) maximum_size.emplace(table.maximum_size); + type_value = GetTypeForTable(isolate, table.type, table.initial_size, + maximum_size); + } import_kind = table_string; break; case kExternalMemory: + if (enabled_features.type_reflection) { + DCHECK_EQ(0, import.index); // Only one memory supported. + base::Optional<uint32_t> maximum_size; + if (module->has_maximum_pages) { + maximum_size.emplace(module->maximum_pages); + } + type_value = + GetTypeForMemory(isolate, module->initial_pages, maximum_size); + } import_kind = memory_string; break; case kExternalGlobal: + if (enabled_features.type_reflection) { + auto& global = module->globals[import.index]; + type_value = + GetTypeForGlobal(isolate, global.mutability, global.type); + } import_kind = global_string; break; case kExternalException: @@ -178,6 +347,9 @@ Handle<JSArray> GetImports(Isolate* isolate, JSObject::AddProperty(isolate, entry, name_string, import_name.ToHandleChecked(), NONE); JSObject::AddProperty(isolate, entry, kind_string, import_kind, NONE); + if (!type_value.is_null()) { + JSObject::AddProperty(isolate, entry, type_string, type_value, NONE); + } storage->set(index, *entry); } @@ -187,10 +359,12 @@ Handle<JSArray> GetImports(Isolate* isolate, Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module_object) { + auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate); Factory* factory = isolate->factory(); Handle<String> name_string = factory->InternalizeUtf8String("name"); Handle<String> kind_string = factory->InternalizeUtf8String("kind"); + Handle<String> type_string = factory->InternalizeUtf8String("type"); Handle<String> function_string = factory->InternalizeUtf8String("function"); Handle<String> table_string = factory->InternalizeUtf8String("table"); @@ -214,17 +388,43 @@ Handle<JSArray> GetExports(Isolate* isolate, const WasmExport& exp = module->export_table[index]; Handle<String> export_kind; + Handle<JSObject> type_value; switch (exp.kind) { case kExternalFunction: + if (enabled_features.type_reflection) { + auto& func = module->functions[exp.index]; + type_value = GetTypeForFunction(isolate, func.sig); + } export_kind = function_string; break; case kExternalTable: + if (enabled_features.type_reflection) { + auto& table = module->tables[exp.index]; + base::Optional<uint32_t> maximum_size; + if (table.has_maximum_size) maximum_size.emplace(table.maximum_size); + type_value = GetTypeForTable(isolate, table.type, table.initial_size, + maximum_size); + } export_kind = table_string; break; case kExternalMemory: + if (enabled_features.type_reflection) { + DCHECK_EQ(0, exp.index); // Only one memory supported. + base::Optional<uint32_t> maximum_size; + if (module->has_maximum_pages) { + maximum_size.emplace(module->maximum_pages); + } + type_value = + GetTypeForMemory(isolate, module->initial_pages, maximum_size); + } export_kind = memory_string; break; case kExternalGlobal: + if (enabled_features.type_reflection) { + auto& global = module->globals[exp.index]; + type_value = + GetTypeForGlobal(isolate, global.mutability, global.type); + } export_kind = global_string; break; case kExternalException: @@ -243,6 +443,9 @@ Handle<JSArray> GetExports(Isolate* isolate, JSObject::AddProperty(isolate, entry, name_string, export_name.ToHandleChecked(), NONE); JSObject::AddProperty(isolate, entry, kind_string, export_kind, NONE); + if (!type_value.is_null()) { + JSObject::AddProperty(isolate, entry, type_string, type_value, NONE); + } storage->set(index, *entry); } diff --git a/chromium/v8/src/wasm/wasm-module.h b/chromium/v8/src/wasm/wasm-module.h index 7dea208d8e6..69c57725de3 100644 --- a/chromium/v8/src/wasm/wasm-module.h +++ b/chromium/v8/src/wasm/wasm-module.h @@ -7,6 +7,7 @@ #include <memory> +#include "src/base/optional.h" #include "src/common/globals.h" #include "src/handles/handles.h" #include "src/utils/vector.h" @@ -301,13 +302,19 @@ V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes( V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context); -V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate, - Handle<WasmModuleObject> module); -V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate, - Handle<WasmModuleObject> module); -V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections( - Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name, - ErrorThrower* thrower); +Handle<JSObject> GetTypeForFunction(Isolate* isolate, FunctionSig* sig); +Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, + ValueType type); +Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size, + base::Optional<uint32_t> max_size); +Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type, + uint32_t min_size, + base::Optional<uint32_t> max_size); +Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module); +Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module); +Handle<JSArray> GetCustomSections(Isolate* isolate, + Handle<WasmModuleObject> module, + Handle<String> name, ErrorThrower* thrower); // Decode local variable names from the names section. Return FixedArray of // FixedArray of <undefined|String>. The outer fixed array is indexed by the diff --git a/chromium/v8/src/wasm/wasm-objects-inl.h b/chromium/v8/src/wasm/wasm-objects-inl.h index 7a80b7ea2ba..66d3a2716e9 100644 --- a/chromium/v8/src/wasm/wasm-objects-inl.h +++ b/chromium/v8/src/wasm/wasm-objects-inl.h @@ -28,7 +28,7 @@ namespace v8 { namespace internal { OBJECT_CONSTRUCTORS_IMPL(WasmExceptionObject, JSObject) -OBJECT_CONSTRUCTORS_IMPL(WasmExceptionTag, Struct) +TQ_OBJECT_CONSTRUCTORS_IMPL(WasmExceptionTag) OBJECT_CONSTRUCTORS_IMPL(WasmExportedFunctionData, Struct) OBJECT_CONSTRUCTORS_IMPL(WasmDebugInfo, Struct) OBJECT_CONSTRUCTORS_IMPL(WasmGlobalObject, JSObject) @@ -42,7 +42,6 @@ NEVER_READ_ONLY_SPACE_IMPL(WasmDebugInfo) CAST_ACCESSOR(WasmDebugInfo) CAST_ACCESSOR(WasmExceptionObject) -CAST_ACCESSOR(WasmExceptionTag) CAST_ACCESSOR(WasmExportedFunctionData) CAST_ACCESSOR(WasmGlobalObject) CAST_ACCESSOR(WasmInstanceObject) @@ -261,9 +260,8 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, managed_native_allocations, Foreign, kManagedNativeAllocationsOffset) OPTIONAL_ACCESSORS(WasmInstanceObject, exceptions_table, FixedArray, kExceptionsTableOffset) -ACCESSORS(WasmInstanceObject, centry_stub, Code, kCEntryStubOffset) -OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_exported_functions, FixedArray, - kWasmExportedFunctionsOffset) +OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_external_functions, FixedArray, + kWasmExternalFunctionsOffset) void WasmInstanceObject::clear_padding() { if (FIELD_SIZE(kOptionalPaddingOffset) != 0) { @@ -325,7 +323,7 @@ SMI_ACCESSORS(WasmExportedFunctionData, jump_table_offset, kJumpTableOffsetOffset) SMI_ACCESSORS(WasmExportedFunctionData, function_index, kFunctionIndexOffset) ACCESSORS(WasmExportedFunctionData, c_wrapper_code, Object, kCWrapperCodeOffset) -ACCESSORS(WasmExportedFunctionData, wasm_call_target, Smi, +ACCESSORS(WasmExportedFunctionData, wasm_call_target, Object, kWasmCallTargetOffset) SMI_ACCESSORS(WasmExportedFunctionData, packed_args_size, kPackedArgsSizeOffset) @@ -358,12 +356,17 @@ OBJECT_CONSTRUCTORS_IMPL(WasmCapiFunctionData, Struct) CAST_ACCESSOR(WasmCapiFunctionData) PRIMITIVE_ACCESSORS(WasmCapiFunctionData, call_target, Address, kCallTargetOffset) -PRIMITIVE_ACCESSORS(WasmCapiFunctionData, embedder_data, void*, - kEmbedderDataOffset) +ACCESSORS(WasmCapiFunctionData, embedder_data, Foreign, kEmbedderDataOffset) ACCESSORS(WasmCapiFunctionData, wrapper_code, Code, kWrapperCodeOffset) ACCESSORS(WasmCapiFunctionData, serialized_signature, PodArray<wasm::ValueType>, kSerializedSignatureOffset) +// WasmExternalFunction +WasmExternalFunction::WasmExternalFunction(Address ptr) : JSFunction(ptr) { + SLOW_DCHECK(IsWasmExternalFunction(*this)); +} +CAST_ACCESSOR(WasmExternalFunction) + // WasmIndirectFunctionTable OBJECT_CONSTRUCTORS_IMPL(WasmIndirectFunctionTable, Struct) CAST_ACCESSOR(WasmIndirectFunctionTable) @@ -399,7 +402,7 @@ wasm::ValueType WasmTableObject::type() { bool WasmMemoryObject::has_maximum_pages() { return maximum_pages() >= 0; } // WasmExceptionTag -SMI_ACCESSORS(WasmExceptionTag, index, kIndexOffset) +TQ_SMI_ACCESSORS(WasmExceptionTag, index) // AsmWasmData ACCESSORS(AsmWasmData, managed_native_module, Managed<wasm::NativeModule>, diff --git a/chromium/v8/src/wasm/wasm-objects.cc b/chromium/v8/src/wasm/wasm-objects.cc index f44f8326ad6..d9417943a84 100644 --- a/chromium/v8/src/wasm/wasm-objects.cc +++ b/chromium/v8/src/wasm/wasm-objects.cc @@ -207,36 +207,19 @@ enum DispatchTableElements : int { // static Handle<WasmModuleObject> WasmModuleObject::New( - Isolate* isolate, const wasm::WasmFeatures& enabled, - std::shared_ptr<const wasm::WasmModule> shared_module, - OwnedVector<const uint8_t> wire_bytes, Handle<Script> script, - Handle<ByteArray> asm_js_offset_table) { - // Create a new {NativeModule} first. - size_t code_size_estimate = - wasm::WasmCodeManager::EstimateNativeModuleCodeSize(shared_module.get()); - auto native_module = isolate->wasm_engine()->NewNativeModule( - isolate, enabled, code_size_estimate, - wasm::NativeModule::kCanAllocateMoreMemory, std::move(shared_module)); - native_module->SetWireBytes(std::move(wire_bytes)); - native_module->SetRuntimeStubs(isolate); - - // Delegate to the shared {WasmModuleObject::New} allocator. - Handle<WasmModuleObject> module_object = - New(isolate, std::move(native_module), script, code_size_estimate); - if (!asm_js_offset_table.is_null()) { - module_object->set_asm_js_offset_table(*asm_js_offset_table); - } - return module_object; + Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, + Handle<Script> script) { + Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(0); + return New(isolate, std::move(native_module), script, export_wrappers); } // static Handle<WasmModuleObject> WasmModuleObject::New( Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, - Handle<Script> script, size_t code_size_estimate) { + Handle<Script> script, Handle<FixedArray> export_wrappers) { const WasmModule* module = native_module->module(); - int num_wrappers = MaxNumExportWrappers(module); - Handle<FixedArray> export_wrappers = - isolate->factory()->NewFixedArray(num_wrappers, AllocationType::kOld); + size_t code_size_estimate = + wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module); return New(isolate, std::move(native_module), script, export_wrappers, code_size_estimate); } @@ -964,6 +947,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate, // Now we handle the funcref case. if (WasmExportedFunction::IsWasmExportedFunction(*entry) || + WasmJSFunction::IsWasmJSFunction(*entry) || WasmCapiFunction::IsWasmCapiFunction(*entry)) { return entry; } @@ -980,7 +964,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate, // Check if we already compiled a wrapper for the function but did not store // it in the table slot yet. - entry = WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance, + entry = WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance, function_index); entries->set(entry_index, *entry); return entry; @@ -1726,9 +1710,6 @@ Handle<WasmInstanceObject> WasmInstanceObject::New( isolate->factory()->NewFixedArray(num_imported_functions); instance->set_imported_function_refs(*imported_function_refs); - Handle<Code> centry_stub = CodeFactory::CEntry(isolate); - instance->set_centry_stub(*centry_stub); - instance->SetRawMemory(nullptr, 0); instance->set_isolate_root(isolate->isolate_root()); instance->set_stack_limit_address( @@ -1878,27 +1859,27 @@ bool WasmInstanceObject::InitTableEntries(Isolate* isolate, dst, src, count); } -MaybeHandle<WasmExportedFunction> WasmInstanceObject::GetWasmExportedFunction( +MaybeHandle<WasmExternalFunction> WasmInstanceObject::GetWasmExternalFunction( Isolate* isolate, Handle<WasmInstanceObject> instance, int index) { - MaybeHandle<WasmExportedFunction> result; - if (instance->has_wasm_exported_functions()) { - Object val = instance->wasm_exported_functions().get(index); + MaybeHandle<WasmExternalFunction> result; + if (instance->has_wasm_external_functions()) { + Object val = instance->wasm_external_functions().get(index); if (!val.IsUndefined(isolate)) { - result = Handle<WasmExportedFunction>(WasmExportedFunction::cast(val), + result = Handle<WasmExternalFunction>(WasmExternalFunction::cast(val), isolate); } } return result; } -Handle<WasmExportedFunction> -WasmInstanceObject::GetOrCreateWasmExportedFunction( +Handle<WasmExternalFunction> +WasmInstanceObject::GetOrCreateWasmExternalFunction( Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index) { - MaybeHandle<WasmExportedFunction> maybe_result = - WasmInstanceObject::GetWasmExportedFunction(isolate, instance, + MaybeHandle<WasmExternalFunction> maybe_result = + WasmInstanceObject::GetWasmExternalFunction(isolate, instance, function_index); - Handle<WasmExportedFunction> result; + Handle<WasmExternalFunction> result; if (maybe_result.ToHandle(&result)) { return result; } @@ -1923,27 +1904,27 @@ WasmInstanceObject::GetOrCreateWasmExportedFunction( isolate, function.sig, function.imported); module_object->export_wrappers().set(wrapper_index, *wrapper); } - result = WasmExportedFunction::New( + result = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New( isolate, instance, function_index, - static_cast<int>(function.sig->parameter_count()), wrapper); + static_cast<int>(function.sig->parameter_count()), wrapper)); - WasmInstanceObject::SetWasmExportedFunction(isolate, instance, function_index, + WasmInstanceObject::SetWasmExternalFunction(isolate, instance, function_index, result); return result; } -void WasmInstanceObject::SetWasmExportedFunction( +void WasmInstanceObject::SetWasmExternalFunction( Isolate* isolate, Handle<WasmInstanceObject> instance, int index, - Handle<WasmExportedFunction> val) { + Handle<WasmExternalFunction> val) { Handle<FixedArray> functions; - if (!instance->has_wasm_exported_functions()) { - // lazily-allocate the wasm exported functions. + if (!instance->has_wasm_external_functions()) { + // Lazily allocate the wasm external functions array. functions = isolate->factory()->NewFixedArray( static_cast<int>(instance->module()->functions.size())); - instance->set_wasm_exported_functions(*functions); + instance->set_wasm_external_functions(*functions); } else { functions = - Handle<FixedArray>(instance->wasm_exported_functions(), isolate); + Handle<FixedArray>(instance->wasm_external_functions(), isolate); } functions->set(index, *val); } @@ -1968,8 +1949,7 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable( instance->module_object().native_module(); // TODO(mstarzinger): Cache and reuse wrapper code. const wasm::WasmFeatures enabled = native_module->enabled_features(); - auto resolved = - compiler::ResolveWasmImportCall(callable, sig, enabled.bigint); + auto resolved = compiler::ResolveWasmImportCall(callable, sig, enabled); compiler::WasmImportCallKind kind = resolved.first; callable = resolved.second; // Update to ultimate target. DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind); @@ -2183,13 +2163,13 @@ bool WasmCapiFunction::IsWasmCapiFunction(Object object) { } Handle<WasmCapiFunction> WasmCapiFunction::New( - Isolate* isolate, Address call_target, void* embedder_data, + Isolate* isolate, Address call_target, Handle<Foreign> embedder_data, Handle<PodArray<wasm::ValueType>> serialized_signature) { Handle<WasmCapiFunctionData> fun_data = Handle<WasmCapiFunctionData>::cast(isolate->factory()->NewStruct( WASM_CAPI_FUNCTION_DATA_TYPE, AllocationType::kOld)); fun_data->set_call_target(call_target); - fun_data->set_embedder_data(embedder_data); + fun_data->set_embedder_data(*embedder_data); fun_data->set_serialized_signature(*serialized_signature); // TODO(jkummerow): Install a JavaScript wrapper. For now, calling // these functions directly is unsupported; they can only be called @@ -2301,6 +2281,10 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate, if (sig_size > 0) { serialized_sig->copy_in(0, sig->all().begin(), sig_size); } + // TODO(mstarzinger): Think about caching and sharing the JS-to-JS wrappers + // per signature instead of compiling a new one for every instantiation. + Handle<Code> wrapper_code = + compiler::CompileJSToJSWrapper(isolate, sig).ToHandleChecked(); Handle<WasmJSFunctionData> function_data = Handle<WasmJSFunctionData>::cast(isolate->factory()->NewStruct( WASM_JS_FUNCTION_DATA_TYPE, AllocationType::kOld)); @@ -2308,9 +2292,7 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate, function_data->set_serialized_parameter_count(parameter_count); function_data->set_serialized_signature(*serialized_sig); function_data->set_callable(*callable); - // TODO(7742): Make this callable by using a proper wrapper code. - function_data->set_wrapper_code( - isolate->builtins()->builtin(Builtins::kIllegal)); + function_data->set_wrapper_code(*wrapper_code); Handle<String> name = isolate->factory()->Function_string(); if (callable->IsJSFunction()) { name = JSFunction::GetName(Handle<JSFunction>::cast(callable)); @@ -2319,6 +2301,7 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate, NewFunctionArgs args = NewFunctionArgs::ForWasm(name, function_data, function_map); Handle<JSFunction> js_function = isolate->factory()->NewFunction(args); + js_function->shared().set_internal_formal_parameter_count(parameter_count); return Handle<WasmJSFunction>::cast(js_function); } @@ -2361,6 +2344,11 @@ PodArray<wasm::ValueType> WasmCapiFunction::GetSerializedSignature() const { return shared().wasm_capi_function_data().serialized_signature(); } +bool WasmExternalFunction::IsWasmExternalFunction(Object object) { + return WasmExportedFunction::IsWasmExportedFunction(object) || + WasmJSFunction::IsWasmJSFunction(object); +} + Handle<WasmExceptionTag> WasmExceptionTag::New(Isolate* isolate, int index) { Handle<WasmExceptionTag> result = Handle<WasmExceptionTag>::cast(isolate->factory()->NewStruct( diff --git a/chromium/v8/src/wasm/wasm-objects.h b/chromium/v8/src/wasm/wasm-objects.h index 1200f7040aa..c198a9bc637 100644 --- a/chromium/v8/src/wasm/wasm-objects.h +++ b/chromium/v8/src/wasm/wasm-objects.h @@ -41,6 +41,7 @@ class WasmCapiFunction; class WasmDebugInfo; class WasmExceptionTag; class WasmExportedFunction; +class WasmExternalFunction; class WasmInstanceObject; class WasmJSFunction; class WasmModuleObject; @@ -139,18 +140,14 @@ class WasmModuleObject : public JSObject { DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, TORQUE_GENERATED_WASM_MODULE_OBJECT_FIELDS) - // Creates a new {WasmModuleObject} with a new {NativeModule} underneath. - V8_EXPORT_PRIVATE static Handle<WasmModuleObject> New( - Isolate* isolate, const wasm::WasmFeatures& enabled, - std::shared_ptr<const wasm::WasmModule> module, - OwnedVector<const uint8_t> wire_bytes, Handle<Script> script, - Handle<ByteArray> asm_js_offset_table); - // Creates a new {WasmModuleObject} for an existing {NativeModule} that is // reference counted and might be shared between multiple Isolates. V8_EXPORT_PRIVATE static Handle<WasmModuleObject> New( Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, - Handle<Script> script, size_t code_size_estimate); + Handle<Script> script); + V8_EXPORT_PRIVATE static Handle<WasmModuleObject> New( + Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, + Handle<Script> script, Handle<FixedArray> export_wrappers); V8_EXPORT_PRIVATE static Handle<WasmModuleObject> New( Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, Handle<Script> script, Handle<FixedArray> export_wrappers, @@ -444,8 +441,7 @@ class WasmInstanceObject : public JSObject { DECL_OPTIONAL_ACCESSORS(indirect_function_table_refs, FixedArray) DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign) DECL_OPTIONAL_ACCESSORS(exceptions_table, FixedArray) - DECL_ACCESSORS(centry_stub, Code) - DECL_OPTIONAL_ACCESSORS(wasm_exported_functions, FixedArray) + DECL_OPTIONAL_ACCESSORS(wasm_external_functions, FixedArray) DECL_PRIMITIVE_ACCESSORS(memory_start, byte*) DECL_PRIMITIVE_ACCESSORS(memory_size, size_t) DECL_PRIMITIVE_ACCESSORS(memory_mask, size_t) @@ -504,8 +500,7 @@ class WasmInstanceObject : public JSObject { V(kIndirectFunctionTablesOffset, kTaggedSize) \ V(kManagedNativeAllocationsOffset, kTaggedSize) \ V(kExceptionsTableOffset, kTaggedSize) \ - V(kCEntryStubOffset, kTaggedSize) \ - V(kWasmExportedFunctionsOffset, kTaggedSize) \ + V(kWasmExternalFunctionsOffset, kTaggedSize) \ V(kRealStackLimitAddressOffset, kSystemPointerSize) \ V(kDataSegmentStartsOffset, kSystemPointerSize) \ V(kDataSegmentSizesOffset, kSystemPointerSize) \ @@ -544,8 +539,7 @@ class WasmInstanceObject : public JSObject { kIndirectFunctionTablesOffset, kManagedNativeAllocationsOffset, kExceptionsTableOffset, - kCEntryStubOffset, - kWasmExportedFunctionsOffset}; + kWasmExternalFunctionsOffset}; V8_EXPORT_PRIVATE const wasm::WasmModule* module(); @@ -588,22 +582,22 @@ class WasmInstanceObject : public JSObject { // Iterates all fields in the object except the untagged fields. class BodyDescriptor; - static MaybeHandle<WasmExportedFunction> GetWasmExportedFunction( + static MaybeHandle<WasmExternalFunction> GetWasmExternalFunction( Isolate* isolate, Handle<WasmInstanceObject> instance, int index); - // Acquires the {WasmExportedFunction} for a given {function_index} from the + // Acquires the {WasmExternalFunction} for a given {function_index} from the // cache of the given {instance}, or creates a new {WasmExportedFunction} if // it does not exist yet. The new {WasmExportedFunction} is added to the // cache of the {instance} immediately. - V8_EXPORT_PRIVATE static Handle<WasmExportedFunction> - GetOrCreateWasmExportedFunction(Isolate* isolate, + V8_EXPORT_PRIVATE static Handle<WasmExternalFunction> + GetOrCreateWasmExternalFunction(Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index); - static void SetWasmExportedFunction(Isolate* isolate, + static void SetWasmExternalFunction(Isolate* isolate, Handle<WasmInstanceObject> instance, int index, - Handle<WasmExportedFunction> val); + Handle<WasmExternalFunction> val); // Imports a constructed {WasmJSFunction} into the indirect function table of // this instance. Note that this might trigger wrapper compilation, since a @@ -713,7 +707,7 @@ class WasmCapiFunction : public JSFunction { static bool IsWasmCapiFunction(Object object); static Handle<WasmCapiFunction> New( - Isolate* isolate, Address call_target, void* embedder_data, + Isolate* isolate, Address call_target, Handle<Foreign> embedder_data, Handle<PodArray<wasm::ValueType>> serialized_signature); Address GetHostCallTarget() const; @@ -726,6 +720,19 @@ class WasmCapiFunction : public JSFunction { OBJECT_CONSTRUCTORS(WasmCapiFunction, JSFunction); }; +// Any external function that can be imported/exported in modules. This abstract +// class just dispatches to the following concrete classes: +// - {WasmExportedFunction}: A proper Wasm function exported from a module. +// - {WasmJSFunction}: A function constructed via WebAssembly.Function in JS. +// // TODO(wasm): Potentially {WasmCapiFunction} will be added here as well. +class WasmExternalFunction : public JSFunction { + public: + static bool IsWasmExternalFunction(Object object); + + DECL_CAST(WasmExternalFunction) + OBJECT_CONSTRUCTORS(WasmExternalFunction, JSFunction); +}; + class WasmIndirectFunctionTable : public Struct { public: DECL_PRIMITIVE_ACCESSORS(size, uint32_t) @@ -757,7 +764,7 @@ class WasmIndirectFunctionTable : public Struct { class WasmCapiFunctionData : public Struct { public: DECL_PRIMITIVE_ACCESSORS(call_target, Address) - DECL_PRIMITIVE_ACCESSORS(embedder_data, void*) + DECL_ACCESSORS(embedder_data, Foreign) DECL_ACCESSORS(wrapper_code, Code) DECL_ACCESSORS(serialized_signature, PodArray<wasm::ValueType>) @@ -769,7 +776,7 @@ class WasmCapiFunctionData : public Struct { DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, TORQUE_GENERATED_WASM_CAPI_FUNCTION_DATA_FIELDS) - STATIC_ASSERT(kStartOfStrongFieldsOffset == kWrapperCodeOffset); + STATIC_ASSERT(kStartOfStrongFieldsOffset == kEmbedderDataOffset); using BodyDescriptor = FlexibleBodyDescriptor<kStartOfStrongFieldsOffset>; OBJECT_CONSTRUCTORS(WasmCapiFunctionData, Struct); @@ -785,7 +792,7 @@ class WasmExportedFunctionData : public Struct { DECL_INT_ACCESSORS(jump_table_offset) DECL_INT_ACCESSORS(function_index) DECL_ACCESSORS(c_wrapper_code, Object) - DECL_ACCESSORS(wasm_call_target, Smi) + DECL_ACCESSORS(wasm_call_target, Object) DECL_INT_ACCESSORS(packed_args_size) DECL_CAST(WasmExportedFunctionData) @@ -914,7 +921,8 @@ class WasmDebugInfo : public Struct { // header. They are referenced by the following fields: // - {WasmExceptionObject::exception_tag} : The tag of the exception object. // - {WasmInstanceObject::exceptions_table}: List of tags used by an instance. -class WasmExceptionTag : public Struct { +class WasmExceptionTag + : public TorqueGeneratedWasmExceptionTag<WasmExceptionTag, Struct> { public: V8_EXPORT_PRIVATE static Handle<WasmExceptionTag> New(Isolate* isolate, int index); @@ -924,14 +932,9 @@ class WasmExceptionTag : public Struct { // least one field, hence this also serves as a padding field for now. DECL_INT_ACCESSORS(index) - DECL_CAST(WasmExceptionTag) DECL_PRINTER(WasmExceptionTag) - DECL_VERIFIER(WasmExceptionTag) - - DEFINE_FIELD_OFFSET_CONSTANTS(Struct::kHeaderSize, - TORQUE_GENERATED_WASM_EXCEPTION_TAG_FIELDS) - OBJECT_CONSTRUCTORS(WasmExceptionTag, Struct); + TQ_OBJECT_CONSTRUCTORS(WasmExceptionTag) }; class AsmWasmData : public Struct { diff --git a/chromium/v8/src/wasm/wasm-opcodes.cc b/chromium/v8/src/wasm/wasm-opcodes.cc index d3fb4c42cf2..879da1445ba 100644 --- a/chromium/v8/src/wasm/wasm-opcodes.cc +++ b/chromium/v8/src/wasm/wasm-opcodes.cc @@ -10,6 +10,7 @@ #include "src/codegen/signature.h" #include "src/execution/messages.h" #include "src/runtime/runtime.h" +#include "src/wasm/wasm-features.h" namespace v8 { namespace internal { @@ -229,11 +230,16 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_F64x2_OP(Ne, "ne") CASE_I64x2_OP(Ne, "ne") CASE_SIMD_OP(Add, "add") + CASE_F64x2_OP(Add, "add") CASE_I64x2_OP(Add, "add") CASE_SIMD_OP(Sub, "sub") + CASE_F64x2_OP(Sub, "sub") CASE_I64x2_OP(Sub, "sub") CASE_SIMD_OP(Mul, "mul") + CASE_F64x2_OP(Mul, "mul") CASE_I64x2_OP(Mul, "mul") + CASE_F64x2_OP(Div, "div") + CASE_F32x4_OP(Div, "div") CASE_F64x2_OP(Splat, "splat") CASE_F64x2_OP(Lt, "lt") CASE_F64x2_OP(Le, "le") @@ -244,7 +250,9 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_F32x4_OP(AddHoriz, "add_horizontal") CASE_F32x4_OP(RecipApprox, "recip_approx") CASE_F32x4_OP(RecipSqrtApprox, "recip_sqrt_approx") + CASE_F64x2_OP(Min, "min") CASE_F32x4_OP(Min, "min") + CASE_F64x2_OP(Max, "max") CASE_F32x4_OP(Max, "max") CASE_F32x4_OP(Lt, "lt") CASE_F32x4_OP(Le, "le") @@ -267,7 +275,9 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_SIMDI_OP(ExtractLane, "extract_lane") CASE_SIMDI_OP(ReplaceLane, "replace_lane") CASE_SIGN_OP(SIMDI, Min, "min") + CASE_SIGN_OP(I64x2, Min, "min") CASE_SIGN_OP(SIMDI, Max, "max") + CASE_SIGN_OP(I64x2, Max, "max") CASE_SIGN_OP(SIMDI, Lt, "lt") CASE_SIGN_OP(I64x2, Lt, "lt") CASE_SIGN_OP(SIMDI, Le, "le") @@ -439,12 +449,13 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) { return os; } -bool IsJSCompatibleSignature(const FunctionSig* sig, bool has_bigint_feature) { - if (sig->return_count() > 1) { +bool IsJSCompatibleSignature(const FunctionSig* sig, + const WasmFeatures& enabled_features) { + if (!enabled_features.mv && sig->return_count() > 1) { return false; } for (auto type : sig->all()) { - if (!has_bigint_feature && type == kWasmI64) { + if (!enabled_features.bigint && type == kWasmI64) { return false; } diff --git a/chromium/v8/src/wasm/wasm-opcodes.h b/chromium/v8/src/wasm/wasm-opcodes.h index 22bd47d54b4..0b19d7452c3 100644 --- a/chromium/v8/src/wasm/wasm-opcodes.h +++ b/chromium/v8/src/wasm/wasm-opcodes.h @@ -15,8 +15,10 @@ namespace internal { namespace wasm { +struct WasmFeatures; + std::ostream& operator<<(std::ostream& os, const FunctionSig& function); -bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); +bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&); // Control expressions and blocks. #define FOREACH_CONTROL_OPCODE(V) \ @@ -335,6 +337,9 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(I8x16Neg, 0xfd51, s_s) \ V(S1x16AnyTrue, 0xfd52, i_s) \ V(S1x16AllTrue, 0xfd53, i_s) \ + V(I8x16Shl, 0xfd54, s_si) \ + V(I8x16ShrS, 0xfd55, s_si) \ + V(I8x16ShrU, 0xfd56, s_si) \ V(I8x16Add, 0xfd57, s_ss) \ V(I8x16AddSaturateS, 0xfd58, s_ss) \ V(I8x16AddSaturateU, 0xfd59, s_ss) \ @@ -349,6 +354,9 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(I16x8Neg, 0xfd62, s_s) \ V(S1x8AnyTrue, 0xfd63, i_s) \ V(S1x8AllTrue, 0xfd64, i_s) \ + V(I16x8Shl, 0xfd65, s_si) \ + V(I16x8ShrS, 0xfd66, s_si) \ + V(I16x8ShrU, 0xfd67, s_si) \ V(I16x8Add, 0xfd68, s_ss) \ V(I16x8AddSaturateS, 0xfd69, s_ss) \ V(I16x8AddSaturateU, 0xfd6a, s_ss) \ @@ -363,6 +371,9 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(I32x4Neg, 0xfd73, s_s) \ V(S1x4AnyTrue, 0xfd74, i_s) \ V(S1x4AllTrue, 0xfd75, i_s) \ + V(I32x4Shl, 0xfd76, s_si) \ + V(I32x4ShrS, 0xfd77, s_si) \ + V(I32x4ShrU, 0xfd78, s_si) \ V(I32x4Add, 0xfd79, s_ss) \ V(I32x4Sub, 0xfd7c, s_ss) \ V(I32x4Mul, 0xfd7f, s_ss) \ @@ -373,9 +384,16 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(I64x2Neg, 0xfd84, s_s) \ V(S1x2AnyTrue, 0xfd85, i_s) \ V(S1x2AllTrue, 0xfd86, i_s) \ + V(I64x2Shl, 0xfd87, s_si) \ + V(I64x2ShrS, 0xfd88, s_si) \ + V(I64x2ShrU, 0xfd89, s_si) \ V(I64x2Add, 0xfd8a, s_ss) \ V(I64x2Sub, 0xfd8d, s_ss) \ V(I64x2Mul, 0xfd8c, s_ss) \ + V(I64x2MinS, 0xfd8e, s_ss) \ + V(I64x2MinU, 0xfd8f, s_ss) \ + V(I64x2MaxS, 0xfd90, s_ss) \ + V(I64x2MaxU, 0xfd91, s_ss) \ V(F32x4Abs, 0xfd95, s_s) \ V(F32x4Neg, 0xfd96, s_s) \ V(F32x4RecipApprox, 0xfd98, s_s) \ @@ -383,26 +401,33 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(F32x4Add, 0xfd9a, s_ss) \ V(F32x4Sub, 0xfd9b, s_ss) \ V(F32x4Mul, 0xfd9c, s_ss) \ + V(F32x4Div, 0xfd9d, s_ss) \ V(F32x4Min, 0xfd9e, s_ss) \ V(F32x4Max, 0xfd9f, s_ss) \ V(F64x2Abs, 0xfda0, s_s) \ V(F64x2Neg, 0xfda1, s_s) \ + V(F64x2Add, 0xfda5, s_ss) \ + V(F64x2Sub, 0xfda6, s_ss) \ + V(F64x2Mul, 0xfda7, s_ss) \ + V(F64x2Div, 0xfda8, s_ss) \ + V(F64x2Min, 0xfda9, s_ss) \ + V(F64x2Max, 0xfdaa, s_ss) \ V(I32x4SConvertF32x4, 0xfdab, s_s) \ V(I32x4UConvertF32x4, 0xfdac, s_s) \ V(F32x4SConvertI32x4, 0xfdaf, s_s) \ V(F32x4UConvertI32x4, 0xfdb0, s_s) \ - V(I8x16SConvertI16x8, 0xfdb1, s_ss) \ - V(I8x16UConvertI16x8, 0xfdb2, s_ss) \ - V(I16x8SConvertI32x4, 0xfdb3, s_ss) \ - V(I16x8UConvertI32x4, 0xfdb4, s_ss) \ - V(I16x8SConvertI8x16Low, 0xfdb5, s_s) \ - V(I16x8SConvertI8x16High, 0xfdb6, s_s) \ - V(I16x8UConvertI8x16Low, 0xfdb7, s_s) \ - V(I16x8UConvertI8x16High, 0xfdb8, s_s) \ - V(I32x4SConvertI16x8Low, 0xfdb9, s_s) \ - V(I32x4SConvertI16x8High, 0xfdba, s_s) \ - V(I32x4UConvertI16x8Low, 0xfdbb, s_s) \ - V(I32x4UConvertI16x8High, 0xfdbc, s_s) \ + V(I8x16SConvertI16x8, 0xfdc6, s_ss) \ + V(I8x16UConvertI16x8, 0xfdc7, s_ss) \ + V(I16x8SConvertI32x4, 0xfdc8, s_ss) \ + V(I16x8UConvertI32x4, 0xfdc9, s_ss) \ + V(I16x8SConvertI8x16Low, 0xfdca, s_s) \ + V(I16x8SConvertI8x16High, 0xfdcb, s_s) \ + V(I16x8UConvertI8x16Low, 0xfdcc, s_s) \ + V(I16x8UConvertI8x16High, 0xfdcd, s_s) \ + V(I32x4SConvertI16x8Low, 0xfdce, s_s) \ + V(I32x4SConvertI16x8High, 0xfdcf, s_s) \ + V(I32x4UConvertI16x8Low, 0xfdd0, s_s) \ + V(I32x4UConvertI16x8High, 0xfdd1, s_s) \ V(I16x8AddHoriz, 0xfdbd, s_ss) \ V(I32x4AddHoriz, 0xfdbe, s_ss) \ V(F32x4AddHoriz, 0xfdbf, s_ss) @@ -413,19 +438,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); V(I32x4ExtractLane, 0xfd0d, _) \ V(I64x2ExtractLane, 0xfd10, _) \ V(F32x4ExtractLane, 0xfd13, _) \ - V(F64x2ExtractLane, 0xfd16, _) \ - V(I8x16Shl, 0xfd54, _) \ - V(I8x16ShrS, 0xfd55, _) \ - V(I8x16ShrU, 0xfd56, _) \ - V(I16x8Shl, 0xfd65, _) \ - V(I16x8ShrS, 0xfd66, _) \ - V(I16x8ShrU, 0xfd67, _) \ - V(I32x4Shl, 0xfd76, _) \ - V(I32x4ShrS, 0xfd77, _) \ - V(I32x4ShrU, 0xfd78, _) \ - V(I64x2Shl, 0xfd87, _) \ - V(I64x2ShrS, 0xfd88, _) \ - V(I64x2ShrU, 0xfd89, _) + V(F64x2ExtractLane, 0xfd16, _) #define FOREACH_SIMD_1_OPERAND_2_PARAM_OPCODE(V) \ V(I8x16ReplaceLane, 0xfd07, _) \ @@ -678,6 +691,8 @@ struct WasmInitExpr { val.global_index = index; } else if (kind == kRefFuncConst) { val.function_index = index; + } else if (kind == kRefNullConst) { + // Nothing to do. } else { // For the other types, the other initializers should be used. UNREACHABLE(); diff --git a/chromium/v8/src/wasm/wasm-serialization.cc b/chromium/v8/src/wasm/wasm-serialization.cc index a20b2f115a1..81460b9fe29 100644 --- a/chromium/v8/src/wasm/wasm-serialization.cc +++ b/chromium/v8/src/wasm/wasm-serialization.cc @@ -625,12 +625,17 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule( Handle<Script> script = CreateWasmScript(isolate, wire_bytes, module->source_map_url); - OwnedVector<uint8_t> wire_bytes_copy = - OwnedVector<uint8_t>::Of(wire_bytes_vec); + auto shared_native_module = isolate->wasm_engine()->NewNativeModule( + isolate, enabled_features, std::move(decode_result.value())); + shared_native_module->SetWireBytes(OwnedVector<uint8_t>::Of(wire_bytes_vec)); + shared_native_module->SetRuntimeStubs(isolate); + + Handle<FixedArray> export_wrappers; + CompileJsToWasmWrappers(isolate, shared_native_module->module(), + &export_wrappers); Handle<WasmModuleObject> module_object = WasmModuleObject::New( - isolate, enabled_features, std::move(decode_result).value(), - std::move(wire_bytes_copy), script, Handle<ByteArray>::null()); + isolate, std::move(shared_native_module), script, export_wrappers); NativeModule* native_module = module_object->native_module(); NativeModuleDeserializer deserializer(native_module); @@ -639,9 +644,6 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule( Reader reader(data + kVersionSize); if (!deserializer.Read(&reader)) return {}; - CompileJsToWasmWrappers(isolate, native_module->module(), - handle(module_object->export_wrappers(), isolate)); - // Log the code within the generated module for profiling. native_module->LogWasmCodes(isolate); diff --git a/chromium/v8/src/wasm/wasm-text.cc b/chromium/v8/src/wasm/wasm-text.cc index e17d34e36fc..44abd714459 100644 --- a/chromium/v8/src/wasm/wasm-text.cc +++ b/chromium/v8/src/wasm/wasm-text.cc @@ -321,23 +321,6 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, break; } - case kExprI8x16Shl: - case kExprI8x16ShrS: - case kExprI8x16ShrU: - case kExprI16x8Shl: - case kExprI16x8ShrS: - case kExprI16x8ShrU: - case kExprI32x4Shl: - case kExprI32x4ShrS: - case kExprI32x4ShrU: - case kExprI64x2Shl: - case kExprI64x2ShrS: - case kExprI64x2ShrU: { - SimdShiftImmediate<Decoder::kNoValidate> imm(&i, i.pc()); - os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.shift; - break; - } - FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE) { os << WasmOpcodes::OpcodeName(opcode); break; |