From 6d6f699921ed08071474688533fe5dff251d521c Mon Sep 17 00:00:00 2001 From: Ohad Rau Date: Wed, 10 Jul 2019 11:14:15 -0700 Subject: [PATCH] Add WIP V8 Preload changes --- deps/v8/BUILD.gn | 3 + deps/v8/include/v8-wasm.h | 87 ++++ deps/v8/src/api/api-wasm.cc | 311 +++++++++++++++ deps/v8/src/api/api-wasm.h | 127 ++++++ deps/v8/src/builtins/base.tq | 7 + deps/v8/src/codegen/code-stub-assembler.cc | 13 +- .../src/codegen/optimized-compilation-info.cc | 2 + .../backend/arm/code-generator-arm.cc | 9 +- .../backend/arm64/code-generator-arm64.cc | 6 +- .../v8/src/compiler/backend/code-generator.cc | 2 + .../backend/ia32/code-generator-ia32.cc | 9 +- .../compiler/backend/instruction-selector.cc | 2 + .../backend/mips/code-generator-mips.cc | 9 +- .../backend/mips64/code-generator-mips64.cc | 9 +- .../backend/ppc/code-generator-ppc.cc | 12 +- .../backend/s390/code-generator-s390.cc | 9 +- .../backend/x64/code-generator-x64.cc | 9 +- deps/v8/src/compiler/code-assembler.h | 1 + deps/v8/src/compiler/linkage.cc | 2 + deps/v8/src/compiler/linkage.h | 19 +- deps/v8/src/compiler/pipeline.cc | 1 + deps/v8/src/compiler/types.cc | 1 + deps/v8/src/compiler/wasm-compiler.cc | 214 +++++++++- deps/v8/src/compiler/wasm-compiler.h | 12 +- deps/v8/src/diagnostics/objects-debug.cc | 4 +- deps/v8/src/diagnostics/objects-printer.cc | 10 + deps/v8/src/execution/frames.cc | 7 +- deps/v8/src/execution/isolate.h | 1 + deps/v8/src/heap/factory.cc | 6 + deps/v8/src/heap/factory.h | 4 + deps/v8/src/heap/objects-visiting.h | 1 + deps/v8/src/logging/log.cc | 4 + deps/v8/src/objects/code.h | 23 +- deps/v8/src/objects/instance-type.h | 1 + deps/v8/src/objects/map.cc | 3 + deps/v8/src/objects/map.h | 1 + deps/v8/src/objects/objects-definitions.h | 3 + deps/v8/src/objects/objects.cc | 7 + .../v8/src/objects/shared-function-info-inl.h | 4 + deps/v8/src/objects/shared-function-info.h | 3 + deps/v8/src/wasm/module-instantiate.cc | 33 +- deps/v8/src/wasm/wasm-code-manager.cc | 2 + deps/v8/src/wasm/wasm-code-manager.h | 1 + deps/v8/src/wasm/wasm-feature-flags.h | 4 +- deps/v8/src/wasm/wasm-js.cc | 20 + deps/v8/src/wasm/wasm-js.h | 1 + deps/v8/src/wasm/wasm-objects-inl.h | 19 + deps/v8/src/wasm/wasm-objects.cc | 138 ++++++- deps/v8/src/wasm/wasm-objects.h | 46 +++ src/node.cc | 46 +++ src/node_native_module_env.cc | 7 + test_wasm/build.sh | 2 + test_wasm/chrome_gdb/gdb_chrome.py | 374 ++++++++++++++++++ test_wasm/chrome_gdb/gdbinit | 80 ++++ test_wasm/chrome_gdb/util/class_methods.py | 148 +++++++ test_wasm/chrome_gdb/viewg.gdb | 32 ++ test_wasm/import.txt | 1 + test_wasm/output.txt | 15 + test_wasm/test.c | 3 + test_wasm/test.js | 6 + test_wasm/test.o | Bin 0 -> 434 bytes test_wasm/test.wasm | Bin 0 -> 215 bytes test_wasm/test.wat | 53 +++ 63 files changed, 1931 insertions(+), 58 deletions(-) create mode 100644 deps/v8/include/v8-wasm.h create mode 100644 deps/v8/src/api/api-wasm.cc create mode 100644 deps/v8/src/api/api-wasm.h create mode 100755 test_wasm/build.sh create mode 100644 test_wasm/chrome_gdb/gdb_chrome.py create mode 100644 test_wasm/chrome_gdb/gdbinit create mode 100644 test_wasm/chrome_gdb/util/class_methods.py create mode 100644 test_wasm/chrome_gdb/viewg.gdb create mode 100644 test_wasm/import.txt create mode 100644 test_wasm/output.txt create mode 100644 test_wasm/test.c create mode 100644 test_wasm/test.js create mode 100644 test_wasm/test.o create mode 100755 test_wasm/test.wasm create mode 100644 test_wasm/test.wat diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 31b9d1776eb..220c6f2efad 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -1971,12 +1971,15 @@ v8_source_set("v8_base_without_compiler") { "include/v8-util.h", "include/v8-wasm-trap-handler-posix.h", "include/v8.h", + "include/v8-wasm.h" "include/v8config.h", "src/api/api-arguments-inl.h", "src/api/api-arguments.cc", "src/api/api-arguments.h", "src/api/api-natives.cc", "src/api/api-natives.h", + "src/api/api-wasm.cc", + "src/api/api-wasm.h", "src/api/api.cc", "src/api/api.h", "src/asmjs/asm-js.cc", diff --git a/deps/v8/include/v8-wasm.h b/deps/v8/include/v8-wasm.h new file mode 100644 index 00000000000..696a5d5ba22 --- /dev/null +++ b/deps/v8/include/v8-wasm.h @@ -0,0 +1,87 @@ +// 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 INCLUDE_V8_WASM_H_ +#define INCLUDE_V8_WASM_H_ + +#include +#include +#include "v8.h" + +namespace v8 { +namespace wasm { + +class V8_EXPORT Memory { + public: + Memory(size_t pages, uint8_t* data); + size_t size(); + size_t pages(); + uint8_t* data(); +}; + +class V8_EXPORT Context { + public: + explicit Context(Memory* memory); + Context(Memory* memory, Isolate* isolate); + Memory* memory; + Isolate* isolate; +}; + +enum V8_EXPORT ValKind : uint8_t { + I32, + I64, + F32, + F64, + ANYREF = 128, + FUNCREF +}; + +class V8_EXPORT Val { + public: + Val(); + Val(Val&&); + explicit Val(int32_t i); + explicit Val(int64_t i); + explicit Val(float i); + explicit Val(double i); + explicit Val(void* r); + + Val& operator=(Val&&); + + ValKind kind() const; + int32_t i32() const; + int64_t i64() const; + float f32() const; + double f64() const; + void* ref() const; +}; + +class V8_EXPORT FuncType { + public: + FuncType(std::vector params, std::vector results); + + std::vector params() const; + std::vector results() const; +}; + +class V8_EXPORT Func { + public: + // TODO(ohadrau): Specify a better return value (Trap) + using callbackType = void (*)(const Memory*, const Val[], Val[]); + + Func(const FuncType*, callbackType); + + const FuncType* type(); + callbackType callback(); +}; + +void PreloadNative(Isolate* isolate, + const char* module_name, + const char* name, + Func* import); + +} // namespace wasm +} // namespace v8 + +#endif // INCLUDE_V8_WASM_H_ diff --git a/deps/v8/src/api/api-wasm.cc b/deps/v8/src/api/api-wasm.cc new file mode 100644 index 00000000000..de8c529691a --- /dev/null +++ b/deps/v8/src/api/api-wasm.cc @@ -0,0 +1,311 @@ +// 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/base/memory.h" +#include "src/api/api-wasm.h" +#include "src/common/globals.h" +#include "src/api/api-inl.h" +#include "src/handles/handles.h" +#include "src/objects/contexts.h" +#include "src/objects/lookup.h" +#include "src/execution/isolate.h" +#include "src/wasm/value-type.h" +#include "src/wasm/wasm-objects.h" + +#include // [WASM-PL] + +#define IGNORE(X) ((void) (X)) + +namespace b = v8::base; +namespace i = v8::internal; + +namespace v8 { +namespace wasm { + +Memory::Memory(size_t pages, uint8_t* data) : pages_(pages), data_(data) {} +size_t Memory::size() { return pages_ * PAGE_SIZE; } +size_t Memory::pages() { return pages_; } +uint8_t* Memory::data() { return data_; } + +Context::Context(Memory* memory) { + this->isolate = Isolate::GetCurrent(); + this->memory = memory; +} + +Context::Context(Memory* memory, Isolate* isolate) { + this->isolate = isolate; + this->memory = memory; +} + +#define T_assert(X) \ + do { printf("[WASM-PL] [ASSERT] KIND: %d, VALUE: %d\n", kind_, value_.i32); assert(X); } while (0) + +Val::Val(ValKind kind, value value) : kind_(kind), value_(value) {} + +Val::Val() : kind_(ANYREF) { value_.ref = nullptr; } +Val::Val(Val&& val) : Val(val.kind_, val.value_) { + printf("[WASM-PL] [COPY] KIND: %d, VALUE: %d\n", val.kind_, val.value_.i32); +} + +Val::Val(int32_t i) : kind_(I32) { value_.i32 = i; } +Val::Val(int64_t i) : kind_(I64) { value_.i64 = i; } +Val::Val(float i) : kind_(F32) { value_.f32 = i; } +Val::Val(double i) : kind_(F64) { value_.f64 = i; } +Val::Val(void* r) : kind_(ANYREF) { value_.ref = r; } + +Val& Val::operator=(Val&& val) { + printf("[WASM-PL] [OPERATOR=] KIND: %d, VALUE: %d\n", val.kind_, val.value_.i32); + kind_ = val.kind_; + value_ = val.value_; + return *this; +} + +ValKind Val::kind() const { return kind_; } +int32_t Val::i32() const { T_assert(kind_ == I32); return value_.i32; } +int64_t Val::i64() const { T_assert(kind_ == I64); return value_.i64; } +float Val::f32() const { T_assert(kind_ == F32); return value_.f32; } +double Val::f64() const { T_assert(kind_ == F64); return value_.f64; } +void* Val::ref() const { + assert(kind_ == ANYREF || kind_ == FUNCREF); + return value_.ref; +} + +FuncType::FuncType( + std::vector params, std::vector results +) : params_(params), results_(results) {} + +std::vector FuncType::params() const { return params_; } +std::vector FuncType::results() const { return results_; } + +Func::Func(const FuncType* funcType, callbackType cb) : + type_(funcType), + callback_(cb) {} + +const FuncType* Func::type() { + return type_; +} + +Func::callbackType Func::callback() { + return callback_; +} + +i::wasm::ValueType wasm_valtype_to_v8(ValKind type) { + switch (type) { + case I32: + return i::wasm::kWasmI32; + case I64: + return i::wasm::kWasmI64; + case F32: + return i::wasm::kWasmF32; + case F64: + return i::wasm::kWasmF64; + default: + // TODO(wasm+): support new value types + UNREACHABLE(); + } +} + +i::Address FuncData::v8_callback( + void* data, i::Address argv, + size_t memoryPages, uint8_t* memoryBase +) { + FuncData* self = reinterpret_cast(data); + i::Isolate* isolate = self->isolate; + puts("[WASM-PL] Got self + isolate"); + + const std::vector& param_types = self->type->params(); + const std::vector& result_types = self->type->results(); + puts("[WASM-PL] Got param/result types"); + + int num_param_types = static_cast(param_types.size()); + int num_result_types = static_cast(result_types.size()); + printf("[WASM-PL] Signature arity: %d -> %d\n", num_param_types, num_result_types); + + Val v(5); + v = Val(3.0); + assert(v.kind() == F32); + + std::unique_ptr params(new Val[num_param_types]); + std::unique_ptr results(new Val[num_result_types]); + i::Address p = argv; + for (int i = 0; i < num_param_types; ++i) { + switch (param_types[i]) { + case I32: + params[i] = Val(b::ReadUnalignedValue(p)); + p += 4; + break; + case I64: + params[i] = Val(b::ReadUnalignedValue(p)); + p += 8; + break; + case F32: + params[i] = Val(b::ReadUnalignedValue(p)); + p += 4; + break; + case F64: + params[i] = Val(b::ReadUnalignedValue(p)); + p += 8; + break; + case ANYREF: + case FUNCREF: { + i::Address raw = b::ReadUnalignedValue(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 obj(raw_obj, raw_obj.GetIsolate()); + params[i] = Val(reinterpret_cast(obj->address())); + } + break; + } + } + } + puts("[WASM-PL] Got params"); + + for (int i = 0; i < num_param_types; i++) { + printf("[WASM-PL] [PARAMS] %d\n", params[i].kind()); + } + + const Memory* memory = new Memory(memoryPages, memoryBase); + puts("[WASM-PL] Created memory object"); + + self->callback(memory, params.get(), results.get()); + puts("[WASM-PL] Called callback"); + + if (isolate->has_scheduled_exception()) { + isolate->PromoteScheduledException(); + } + if (isolate->has_pending_exception()) { + i::Object ex = isolate->pending_exception(); + isolate->clear_pending_exception(); + return ex.ptr(); + } + puts("[WASM-PL] Checked pending exceptions"); + + p = argv; + for (int i = 0; i < num_result_types; ++i) { + switch (result_types[i]) { + case I32: + b::WriteUnalignedValue(p, results[i].i32()); + p += 4; + break; + case I64: + b::WriteUnalignedValue(p, results[i].i64()); + p += 8; + break; + case F32: + b::WriteUnalignedValue(p, results[i].f32()); + p += 4; + break; + case F64: + b::WriteUnalignedValue(p, results[i].f64()); + p += 8; + break; + case ANYREF: + case FUNCREF: { + if (results[i].ref() == nullptr) { + b::WriteUnalignedValue(p, i::kNullAddress); + } else { + b::WriteUnalignedValue(p, results[i].ref()); + } + p += sizeof(i::Address); + break; + } + } + } + puts("[WASM-PL] Got results"); + return i::kNullAddress; +} + +// TODO(ohadrau): Clean up so that we're not maintaining 2 copies of this +// Use an invalid type as a marker separating params and results. +static const i::wasm::ValueType kMarker = i::wasm::kWasmStmt; + +static i::Handle> Serialize( + i::Isolate* isolate, const FuncType* type) { + puts("[WASM-PL] Create PodArray"); + int sig_size = + static_cast(type->params().size() + type->results().size() + 1); + printf("[WASM-PL] Sig size: %d\n", sig_size); + i::Handle> sig = + i::PodArray::New(isolate, sig_size, + i::AllocationType::kOld); + puts("[WASM-PL] Copy v8 types"); + int index = 0; + // TODO(jkummerow): Consider making vec<> range-based for-iterable. + for (size_t i = 0; i < type->results().size(); i++) { + sig->set(index++, wasm_valtype_to_v8(type->results()[i])); + } + // {sig->set} needs to take the address of its second parameter, + // so we can't pass in the static const kMarker directly. + i::wasm::ValueType marker = kMarker; + sig->set(index++, marker); + for (size_t i = 0; i < type->params().size(); i++) { + sig->set(index++, wasm_valtype_to_v8(type->params()[i])); + } + puts("[WASM-PL] Serialized"); + return sig; +} + +void PreloadNative(Isolate* isolate, + const char* module_name, + const char* name, + Func* import) { + puts("[WASM-PL] Get isolate"); + i::Isolate* i_isolate = reinterpret_cast(isolate); + i::HandleScope handle_scope(i_isolate); + + puts("[WASM-PL] Select imports"); + Eternal imports = i_isolate->wasm_native_imports(); + if (imports.IsEmpty()) { + i::Handle handle = + i_isolate->factory()->NewJSObject(i_isolate->object_function()); + Local local = + Utils::Convert(handle); + imports.Set(isolate, local); + } + + Local imports_local = imports.Get(isolate); + i::Handle imports_handle = + i::Handle(reinterpret_cast(*imports_local)); + + i::Handle module_str = + i_isolate->factory()->NewStringFromAsciiChecked(module_name); + i::Handle name_str = + i_isolate->factory()->NewStringFromAsciiChecked(name); + + puts("[WASM-PL] Create object"); + i::Handle module_obj; + i::LookupIterator module_it(i_isolate, imports_handle, module_str, + i::LookupIterator::OWN_SKIP_INTERCEPTOR); + if (i::JSObject::HasProperty(&module_it).ToChecked()) { + module_obj = i::Handle::cast( + i::Object::GetProperty(&module_it).ToHandleChecked()); + } else { + module_obj = + i_isolate->factory()->NewJSObject(i_isolate->object_function()); + IGNORE(i::Object::SetProperty(i_isolate, imports_handle, + module_str, module_obj)); + } + puts("[WASM-PL] Create callback"); + // TODO(ohadrau): Is this the right embedder data to pass in? + FuncData* data = new FuncData(i_isolate, import->type()); + data->callback = import->callback(); + i::Handle callback = + i::WasmPreloadFunction::New( + i_isolate, reinterpret_cast(&FuncData::v8_callback), + data, Serialize(i_isolate, import->type())); + IGNORE(i::Object::SetProperty(i_isolate, module_obj, name_str, callback)); + + puts("[WASM-PL] Update native imports"); + i_isolate->set_wasm_native_imports(imports); + assert(imports.Get(isolate) == i_isolate->wasm_native_imports().Get(isolate)); + puts("[WASM-PL] Assertion success: imports_handle set."); +} + +} // namespace wasm +} // namespace v8 + +#undef IGNORE diff --git a/deps/v8/src/api/api-wasm.h b/deps/v8/src/api/api-wasm.h new file mode 100644 index 00000000000..d188e164c76 --- /dev/null +++ b/deps/v8/src/api/api-wasm.h @@ -0,0 +1,127 @@ +// 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_API_API_WASM_H_ +#define V8_API_API_WASM_H_ + +#include +#include +#include +#include "include/v8.h" + +namespace v8 { +namespace wasm { + +#define PAGE_SIZE 0x10000 + +class Memory { + private: + size_t pages_; + uint8_t* data_; + public: + Memory(size_t pages, uint8_t* data); + size_t size(); + size_t pages(); + uint8_t* data(); +}; + +class Context { + public: + explicit Context(Memory* memory); + Context(Memory* memory, v8::Isolate* isolate); + Memory* memory; + v8::Isolate* isolate; +}; + +enum ValKind : uint8_t { + I32, + I64, + F32, + F64, + ANYREF = 128, + FUNCREF +}; + +// TODO(ohadrau): Should we enforce Ref ownership like the C API? +class Val { + private: + ValKind kind_; + union value { + int32_t i32; + int64_t i64; + float f32; + double f64; + void* ref; + } value_; + + Val(ValKind kind, value value); + + public: + Val(); + Val(Val&& val); + explicit Val(int32_t i); + explicit Val(int64_t i); + explicit Val(float i); + explicit Val(double i); + explicit Val(void* r); + + Val& operator=(Val&&); + + ValKind kind() const; + int32_t i32() const; + int64_t i64() const; + float f32() const; + double f64() const; + void* ref() const; +}; + +class FuncType { + private: + std::vector params_, results_; + public: + FuncType(std::vector params, std::vector results); + + std::vector params() const; + std::vector results() const; +}; + +class Func { + friend struct FuncData; + // TODO(ohadrau): Specify a better return value (Trap) + using callbackType = void (*)(const Memory*, const Val[], Val[]); + private: + const FuncType* type_; + callbackType callback_; + public: + Func(const FuncType*, callbackType); + + const FuncType* type(); + callbackType callback(); +}; + +struct FuncData { + v8::internal::Isolate* isolate; + const FuncType* type; + Func::callbackType callback; + + FuncData(v8::internal::Isolate* isolate, const FuncType* type) + : isolate(isolate), + type(type) {} + + static v8::internal::Address v8_callback( + void* data, + v8::internal::Address argv, + size_t memoryPages, + uint8_t* memoryBase); +}; + +void PreloadNative(Isolate* isolate, + const char* module_name, + const char* name, + Func* import); + +} // namespace wasm +} // namespace v8 + +#endif // V8_API_API_WASM_H_ diff --git a/deps/v8/src/builtins/base.tq b/deps/v8/src/builtins/base.tq index c211a00bc30..254bf33a9ad 100644 --- a/deps/v8/src/builtins/base.tq +++ b/deps/v8/src/builtins/base.tq @@ -928,6 +928,13 @@ extern class WasmCapiFunctionData extends Struct { serialized_signature: ByteArray; // PodArray } +extern class WasmPreloadFunctionData extends Struct { + call_target: RawPtr; + embedder_data: RawPtr; + wrapper_code: Code; + serialized_signature: ByteArray; // PodArray +} + extern class WasmDebugInfo extends Struct { instance: WasmInstanceObject; interpreter_handle: Foreign | Undefined; diff --git a/deps/v8/src/codegen/code-stub-assembler.cc b/deps/v8/src/codegen/code-stub-assembler.cc index 5a5b244cfbd..0f913ba86c7 100644 --- a/deps/v8/src/codegen/code-stub-assembler.cc +++ b/deps/v8/src/codegen/code-stub-assembler.cc @@ -13600,7 +13600,8 @@ TNode CodeStubAssembler::GetSharedFunctionInfoCode( UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE, FUNCTION_TEMPLATE_INFO_TYPE, WASM_JS_FUNCTION_DATA_TYPE, - WASM_CAPI_FUNCTION_DATA_TYPE}; + WASM_CAPI_FUNCTION_DATA_TYPE, + WASM_PRELOAD_FUNCTION_DATA_TYPE}; Label check_is_bytecode_array(this); Label check_is_exported_function_data(this); Label check_is_asm_wasm_data(this); @@ -13610,6 +13611,7 @@ TNode CodeStubAssembler::GetSharedFunctionInfoCode( Label check_is_interpreter_data(this); Label check_is_wasm_js_function_data(this); Label check_is_wasm_capi_function_data(this); + Label check_is_wasm_preload_function_data(this); Label* case_labels[] = {&check_is_bytecode_array, &check_is_exported_function_data, &check_is_asm_wasm_data, @@ -13617,7 +13619,8 @@ TNode CodeStubAssembler::GetSharedFunctionInfoCode( &check_is_uncompiled_data_with_preparse_data, &check_is_function_template_info, &check_is_wasm_js_function_data, - &check_is_wasm_capi_function_data}; + &check_is_wasm_capi_function_data, + &check_is_wasm_preload_function_data}; STATIC_ASSERT(arraysize(case_values) == arraysize(case_labels)); Switch(data_type, &check_is_interpreter_data, case_values, case_labels, arraysize(case_labels)); @@ -13672,6 +13675,12 @@ TNode CodeStubAssembler::GetSharedFunctionInfoCode( WasmCapiFunctionData::kWrapperCodeOffset)); Goto(&done); + // IsWasmPreloadFunctionData: Use the wrapper code. + BIND(&check_is_wasm_preload_function_data); + sfi_code = CAST(LoadObjectField(CAST(sfi_data), + WasmPreloadFunctionData::kWrapperCodeOffset)); + Goto(&done); + BIND(&done); return sfi_code.value(); } diff --git a/deps/v8/src/codegen/optimized-compilation-info.cc b/deps/v8/src/codegen/optimized-compilation-info.cc index 35769e9ae98..1274eb034cf 100644 --- a/deps/v8/src/codegen/optimized-compilation-info.cc +++ b/deps/v8/src/codegen/optimized-compilation-info.cc @@ -90,6 +90,7 @@ void OptimizedCompilationInfo::ConfigureFlags() { break; case Code::WASM_FUNCTION: case Code::WASM_TO_CAPI_FUNCTION: + case Code::WASM_TO_PRELOAD_FUNCTION: SetFlag(kSwitchJumpTableEnabled); break; default: @@ -176,6 +177,7 @@ StackFrame::Type OptimizedCompilationInfo::GetOutputStackFrameType() const { case Code::WASM_FUNCTION: return StackFrame::WASM_COMPILED; case Code::WASM_TO_CAPI_FUNCTION: + case Code::WASM_TO_PRELOAD_FUNCTION: return StackFrame::WASM_EXIT; case Code::JS_TO_WASM_FUNCTION: return StackFrame::JS_TO_WASM; diff --git a/deps/v8/src/compiler/backend/arm/code-generator-arm.cc b/deps/v8/src/compiler/backend/arm/code-generator-arm.cc index 6ed4fbd8626..6f9ccf70077 100644 --- a/deps/v8/src/compiler/backend/arm/code-generator-arm.cc +++ b/deps/v8/src/compiler/backend/arm/code-generator-arm.cc @@ -834,7 +834,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( break; case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. __ str(pc, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset)); } @@ -3012,7 +3013,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ Push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -3022,7 +3024,8 @@ void CodeGenerator::AssembleConstructFrame() { __ ldr(kWasmInstanceRegister, FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ Push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ AllocateStackSpace(kSystemPointerSize); } diff --git a/deps/v8/src/compiler/backend/arm64/code-generator-arm64.cc b/deps/v8/src/compiler/backend/arm64/code-generator-arm64.cc index 894eb49eb3d..f954915f660 100644 --- a/deps/v8/src/compiler/backend/arm64/code-generator-arm64.cc +++ b/deps/v8/src/compiler/backend/arm64/code-generator-arm64.cc @@ -746,7 +746,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadiFunction()) { // Put the return address in a stack slot. Register scratch = x8; __ Adr(scratch, &return_location); @@ -2559,7 +2560,8 @@ void CodeGenerator::AssembleConstructFrame() { MemOperand(fp, WasmCompiledFrameConstants::kWasmInstanceOffset)); } break; case CallDescriptor::kCallWasmImportWrapper: - case CallDescriptor::kCallWasmCapiFunction: { + case CallDescriptor::kCallWasmCapiFunction: + case CallDescriptor::kCallWasmPreloadFunction: { UseScratchRegisterScope temps(tasm()); __ LoadTaggedPointerField( kJSFunctionRegister, diff --git a/deps/v8/src/compiler/backend/code-generator.cc b/deps/v8/src/compiler/backend/code-generator.cc index 9ce92dadaa9..290de2e5949 100644 --- a/deps/v8/src/compiler/backend/code-generator.cc +++ b/deps/v8/src/compiler/backend/code-generator.cc @@ -89,6 +89,7 @@ CodeGenerator::CodeGenerator( Code::Kind code_kind = info->code_kind(); if (code_kind == Code::WASM_FUNCTION || code_kind == Code::WASM_TO_CAPI_FUNCTION || + code_kind == Code::WASM_TO_PRELOAD_FUNCTION || code_kind == Code::WASM_TO_JS_FUNCTION || code_kind == Code::WASM_INTERPRETER_ENTRY || (Builtins::IsBuiltinId(builtin_index) && @@ -762,6 +763,7 @@ StubCallMode CodeGenerator::DetermineStubCallMode() const { Code::Kind code_kind = info()->code_kind(); return (code_kind == Code::WASM_FUNCTION || code_kind == Code::WASM_TO_CAPI_FUNCTION || + code_kind == Code::WASM_TO_PRELOAD_FUNCTION || code_kind == Code::WASM_TO_JS_FUNCTION) ? StubCallMode::kCallWasmRuntimeStub : StubCallMode::kCallCodeObject; diff --git a/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc b/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc index 4a3d590eadd..e1d669c5250 100644 --- a/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc +++ b/deps/v8/src/compiler/backend/ia32/code-generator-ia32.cc @@ -816,7 +816,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. Register scratch = eax; __ push(scratch); @@ -4241,7 +4242,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -4253,7 +4255,8 @@ void CodeGenerator::AssembleConstructFrame() { Operand(kWasmInstanceRegister, Tuple2::kValue1Offset - kHeapObjectTag)); __ push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ AllocateStackSpace(kSystemPointerSize); } diff --git a/deps/v8/src/compiler/backend/instruction-selector.cc b/deps/v8/src/compiler/backend/instruction-selector.cc index d7fa9268542..04089b9c951 100644 --- a/deps/v8/src/compiler/backend/instruction-selector.cc +++ b/deps/v8/src/compiler/backend/instruction-selector.cc @@ -908,6 +908,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, : g.UseRegister(callee)); break; case CallDescriptor::kCallWasmCapiFunction: + case CallDescriptor::kCallWasmPreloadFunction: case CallDescriptor::kCallWasmFunction: case CallDescriptor::kCallWasmImportWrapper: buffer->instruction_args.push_back( @@ -2636,6 +2637,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { opcode = kArchCallJSFunction | MiscField::encode(flags); break; case CallDescriptor::kCallWasmCapiFunction: + case CallDescriptor::kCallWasmPreloadFunction: case CallDescriptor::kCallWasmFunction: case CallDescriptor::kCallWasmImportWrapper: opcode = kArchCallWasmFunction | MiscField::encode(flags); diff --git a/deps/v8/src/compiler/backend/mips/code-generator-mips.cc b/deps/v8/src/compiler/backend/mips/code-generator-mips.cc index 1048a7067b5..8f3df0ee3d0 100644 --- a/deps/v8/src/compiler/backend/mips/code-generator-mips.cc +++ b/deps/v8/src/compiler/backend/mips/code-generator-mips.cc @@ -780,7 +780,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. __ LoadAddress(kScratchReg, &return_location); __ sw(kScratchReg, @@ -3398,7 +3399,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ Push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -3408,7 +3410,8 @@ void CodeGenerator::AssembleConstructFrame() { __ lw(kWasmInstanceRegister, FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ Push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ Subu(sp, sp, Operand(kSystemPointerSize)); } diff --git a/deps/v8/src/compiler/backend/mips64/code-generator-mips64.cc b/deps/v8/src/compiler/backend/mips64/code-generator-mips64.cc index b87f449b69f..5a8a7461a19 100644 --- a/deps/v8/src/compiler/backend/mips64/code-generator-mips64.cc +++ b/deps/v8/src/compiler/backend/mips64/code-generator-mips64.cc @@ -758,7 +758,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. __ LoadAddress(kScratchReg, &return_location); __ sd(kScratchReg, @@ -3557,7 +3558,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ Push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -3567,7 +3569,8 @@ void CodeGenerator::AssembleConstructFrame() { __ ld(kWasmInstanceRegister, FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ Push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ Dsubu(sp, sp, Operand(kSystemPointerSize)); } diff --git a/deps/v8/src/compiler/backend/ppc/code-generator-ppc.cc b/deps/v8/src/compiler/backend/ppc/code-generator-ppc.cc index 67a00111d21..af9c774d702 100644 --- a/deps/v8/src/compiler/backend/ppc/code-generator-ppc.cc +++ b/deps/v8/src/compiler/backend/ppc/code-generator-ppc.cc @@ -1023,8 +1023,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( Label start_call; bool isWasmCapiFunction = linkage()->GetIncomingDescriptor()->IsWasmCapiFunction(); + bool isWasmPreloadFunction = + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction(); constexpr int offset = 12; - if (isWasmCapiFunction) { + if (isWasmCapiFunction || isWasmPreloadFunction) { __ mflr(kScratchReg); __ bind(&start_call); __ LoadPC(r0); @@ -1044,7 +1046,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( // offset is set to 12 right now, which is counted from where we are // binding to the label and ends at this spot. If failed, replace it it // with the correct offset suggested. More info on f5ab7d3. - if (isWasmCapiFunction) + if (isWasmCapiFunction || isWasmPreloadFunction) CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call)); RecordSafepoint(instr->reference_map(), Safepoint::kNoLazyDeopt); @@ -2354,7 +2356,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ Push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -2364,7 +2367,8 @@ void CodeGenerator::AssembleConstructFrame() { __ LoadP(kWasmInstanceRegister, FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ Push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ addi(sp, sp, Operand(-kSystemPointerSize)); } diff --git a/deps/v8/src/compiler/backend/s390/code-generator-s390.cc b/deps/v8/src/compiler/backend/s390/code-generator-s390.cc index c5ee63e4c98..3ae91804abe 100644 --- a/deps/v8/src/compiler/backend/s390/code-generator-s390.cc +++ b/deps/v8/src/compiler/backend/s390/code-generator-s390.cc @@ -1512,7 +1512,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; // Put the return address in a stack slot. - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. __ larl(r0, &return_location); __ StoreP(r0, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset)); @@ -3045,7 +3046,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ Push(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -3055,7 +3057,8 @@ void CodeGenerator::AssembleConstructFrame() { __ LoadP(kWasmInstanceRegister, FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ Push(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ lay(sp, MemOperand(sp, -kSystemPointerSize)); } diff --git a/deps/v8/src/compiler/backend/x64/code-generator-x64.cc b/deps/v8/src/compiler/backend/x64/code-generator-x64.cc index 04ccf7d3919..e3a540c32bc 100644 --- a/deps/v8/src/compiler/backend/x64/code-generator-x64.cc +++ b/deps/v8/src/compiler/backend/x64/code-generator-x64.cc @@ -902,7 +902,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kArchCallCFunction: { int const num_parameters = MiscField::decode(instr->opcode()); Label return_location; - if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) { + if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction() || + linkage()->GetIncomingDescriptor()->IsWasmPreloadFunction()) { // Put the return address in a stack slot. __ leaq(kScratchRegister, Operand(&return_location, 0)); __ movq(MemOperand(rbp, WasmExitFrameConstants::kCallingPCOffset), @@ -3749,7 +3750,8 @@ void CodeGenerator::AssembleConstructFrame() { if (call_descriptor->IsWasmFunctionCall()) { __ pushq(kWasmInstanceRegister); } else if (call_descriptor->IsWasmImportWrapper() || - call_descriptor->IsWasmCapiFunction()) { + call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // WASM import wrappers are passed a tuple in the place of the instance. // Unpack the tuple into the instance and the target callable. // This must be done here in the codegen because it cannot be expressed @@ -3761,7 +3763,8 @@ void CodeGenerator::AssembleConstructFrame() { kWasmInstanceRegister, FieldOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); __ pushq(kWasmInstanceRegister); - if (call_descriptor->IsWasmCapiFunction()) { + if (call_descriptor->IsWasmCapiFunction() || + call_descriptor->IsWasmPreloadFunction()) { // Reserve space for saving the PC later. __ AllocateStackSpace(kSystemPointerSize); } diff --git a/deps/v8/src/compiler/code-assembler.h b/deps/v8/src/compiler/code-assembler.h index 8e5a9855931..853aedeb023 100644 --- a/deps/v8/src/compiler/code-assembler.h +++ b/deps/v8/src/compiler/code-assembler.h @@ -334,6 +334,7 @@ class SymbolWrapper; class Undetectable; class UniqueName; class WasmCapiFunctionData; +class WasmPreloadFunctionData; class WasmExceptionObject; class WasmExceptionTag; class WasmExportedFunctionData; diff --git a/deps/v8/src/compiler/linkage.cc b/deps/v8/src/compiler/linkage.cc index 1d88a27a5f7..8f7f104ab4f 100644 --- a/deps/v8/src/compiler/linkage.cc +++ b/deps/v8/src/compiler/linkage.cc @@ -38,6 +38,7 @@ std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) { os << "Addr"; break; case CallDescriptor::kCallWasmCapiFunction: + case CallDescriptor::kCallWasmPreloadFunction: os << "WasmExit"; break; case CallDescriptor::kCallWasmFunction: @@ -159,6 +160,7 @@ int CallDescriptor::CalculateFixedFrameSize(Code::Kind code_kind) const { case kCallWasmImportWrapper: return WasmCompiledFrameConstants::kFixedSlotCount; case kCallWasmCapiFunction: + case kCallWasmPreloadFunction: return WasmExitFrameConstants::kFixedSlotCount; } UNREACHABLE(); diff --git a/deps/v8/src/compiler/linkage.h b/deps/v8/src/compiler/linkage.h index 05eb0e7d117..1a528cff69e 100644 --- a/deps/v8/src/compiler/linkage.h +++ b/deps/v8/src/compiler/linkage.h @@ -172,13 +172,14 @@ class V8_EXPORT_PRIVATE CallDescriptor final public: // Describes the kind of this call, which determines the target. enum Kind { - kCallCodeObject, // target is a Code object - kCallJSFunction, // target is a JSFunction object - kCallAddress, // target is a machine pointer - kCallWasmCapiFunction, // target is a Wasm C API function - kCallWasmFunction, // target is a wasm function - kCallWasmImportWrapper, // target is a wasm import wrapper - kCallBuiltinPointer, // target is a builtin pointer + kCallCodeObject, // target is a Code object + kCallJSFunction, // target is a JSFunction object + kCallAddress, // target is a machine pointer + kCallWasmCapiFunction, // target is a Wasm C API function + kCallWasmPreloadFunction, // target is a Wasm preloaded function + kCallWasmFunction, // target is a wasm function + kCallWasmImportWrapper, // target is a wasm import wrapper + kCallBuiltinPointer, // target is a builtin pointer }; enum Flag { @@ -240,6 +241,10 @@ class V8_EXPORT_PRIVATE CallDescriptor final // Returns {true} if this descriptor is a call to a Wasm C API function. bool IsWasmCapiFunction() const { return kind_ == kCallWasmCapiFunction; } + // Returns {true} if this descriptor is a call to a Wasm preloaded function. + bool IsWasmPreloadFunction() const + { return kind_ == kCallWasmPreloadFunction; } + bool RequiresFrameAsIncoming() const { return IsCFunctionCall() || IsJSFunctionCall() || IsWasmFunctionCall(); } diff --git a/deps/v8/src/compiler/pipeline.cc b/deps/v8/src/compiler/pipeline.cc index 872fa75f529..a531941503a 100644 --- a/deps/v8/src/compiler/pipeline.cc +++ b/deps/v8/src/compiler/pipeline.cc @@ -2089,6 +2089,7 @@ struct VerifyGraphPhase { switch (data->info()->code_kind()) { case Code::WASM_FUNCTION: case Code::WASM_TO_CAPI_FUNCTION: + case Code::WASM_TO_PRELOAD_FUNCTION: case Code::WASM_TO_JS_FUNCTION: case Code::JS_TO_WASM_FUNCTION: case Code::WASM_INTERPRETER_ENTRY: diff --git a/deps/v8/src/compiler/types.cc b/deps/v8/src/compiler/types.cc index 38ead1810eb..747562505b7 100644 --- a/deps/v8/src/compiler/types.cc +++ b/deps/v8/src/compiler/types.cc @@ -350,6 +350,7 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) { case ENUM_CACHE_TYPE: case SOURCE_POSITION_TABLE_WITH_FRAME_CACHE_TYPE: case WASM_CAPI_FUNCTION_DATA_TYPE: + case WASM_PRELOAD_FUNCTION_DATA_TYPE: case WASM_DEBUG_INFO_TYPE: case WASM_EXCEPTION_TAG_TYPE: case WASM_EXPORTED_FUNCTION_DATA_TYPE: diff --git a/deps/v8/src/compiler/wasm-compiler.cc b/deps/v8/src/compiler/wasm-compiler.cc index 0b212c9fb11..305ee2be3f7 100644 --- a/deps/v8/src/compiler/wasm-compiler.cc +++ b/deps/v8/src/compiler/wasm-compiler.cc @@ -5719,6 +5719,137 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { if (ContainsInt64(sig_)) LowerInt64(); } +// TODO(ohadrau): Change call wrapper to pass in the correct params + void BuildPreloadCallWrapper( + Address address, + size_t memory_size, + i::byte *memory_start + ) { + // Store arguments on our stack, then align the stack for calling to C. + puts("[WASM-PL] Counting params/return"); + int param_bytes = 0; + for (wasm::ValueType type : sig_->parameters()) { + param_bytes += wasm::ValueTypes::MemSize(type); + } + int return_bytes = 0; + for (wasm::ValueType type : sig_->returns()) { + return_bytes += wasm::ValueTypes::MemSize(type); + } + + puts("[WASM-PL] Creating stack slots"); + int stack_slot_bytes = std::max(param_bytes, return_bytes); + Node* values = stack_slot_bytes == 0 + ? mcgraph()->IntPtrConstant(0) + : graph()->NewNode(mcgraph()->machine()->StackSlot( + stack_slot_bytes, kDoubleAlignment)); + + puts("[WASM-PL] Copying params"); + int offset = 0; + int param_count = static_cast(sig_->parameter_count()); + for (int i = 0; i < param_count; ++i) { + wasm::ValueType type = sig_->GetParam(i); + // Start from the parameter with index 1 to drop the instance_node. + // TODO(jkummerow): When a values is a reference type, we should pass it + // in a GC-safe way, not just as a raw pointer. + SetEffect(graph()->NewNode(GetSafeStoreOperator(offset, type), values, + Int32Constant(offset), Param(i + 1), Effect(), + Control())); + offset += wasm::ValueTypes::ElementSizeInBytes(type); + } + + puts("[WASM-PL] Acquiring memory offset"); + // TODO(ohadrau): How should we create the context object? + Node* memoryPages = Int64Constant(memory_size); + Node* memoryBase = Int64Constant((size_t) memory_start); + + puts("[WASM-PL] Passing function"); + // The function is passed as the last parameter, after WASM arguments. + Node* function_node = Param(param_count + 1); + Node* shared = LOAD_RAW( + function_node, + wasm::ObjectAccess::SharedFunctionInfoOffsetInTaggedJSFunction(), + MachineType::TypeCompressedTagged()); + Node* sfi_data = LOAD_RAW( + shared, SharedFunctionInfo::kFunctionDataOffset - kHeapObjectTag, + MachineType::TypeCompressedTagged()); + Node* host_data = LOAD_RAW( + sfi_data, WasmPreloadFunctionData::kEmbedderDataOffset - kHeapObjectTag, + MachineType::Pointer()); + + puts("[WASM-PL] Getting frame pointer"); + BuildModifyThreadInWasmFlag(false); + Node* isolate_root = + LOAD_INSTANCE_FIELD(IsolateRoot, MachineType::Pointer()); + Node* fp_value = graph()->NewNode(mcgraph()->machine()->LoadFramePointer()); + STORE_RAW(isolate_root, Isolate::c_entry_fp_offset(), fp_value, + MachineType::PointerRepresentation(), kNoWriteBarrier); + + puts("[WASM-PL] Creating external reference"); + // TODO(jkummerow): Load the address from the {host_data}, and cache + // wrappers per signature. + const ExternalReference ref = ExternalReference::Create(address); + Node* function = + graph()->NewNode(mcgraph()->common()->ExternalConstant(ref)); + + puts("[WASM-PL] Building signature"); + // TODO(ohadrau): Replace the memory base address w/ v8::wasm::Context *ctx + // Results: Address results. + // Parameters: void *data, Address args, size_t memorySize, void* memoryBase + MachineType host_sig_types[] = { + MachineType::Pointer(), MachineType::Pointer(), MachineType::Pointer(), + MachineType::Pointer(), MachineType::Pointer()}; + // size_t return_count, size_t parameter_count, const MachineType* reps + MachineSignature host_sig(1, 4, host_sig_types); + puts("[WASM-PL] Building C Call"); + Node* return_value = BuildCCall(&host_sig, function, host_data, values, + memoryPages, memoryBase); + + BuildModifyThreadInWasmFlag(true); + + puts("[WASM-PL] Exception branch"); + Node* exception_branch = + graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue), + graph()->NewNode(mcgraph()->machine()->WordEqual(), + return_value, IntPtrConstant(0)), + Control()); + SetControl( + graph()->NewNode(mcgraph()->common()->IfFalse(), exception_branch)); + WasmThrowDescriptor interface_descriptor; + auto call_descriptor = Linkage::GetStubCallDescriptor( + mcgraph()->zone(), interface_descriptor, + interface_descriptor.GetStackParameterCount(), CallDescriptor::kNoFlags, + Operator::kNoProperties, StubCallMode::kCallWasmRuntimeStub); + Node* call_target = mcgraph()->RelocatableIntPtrConstant( + wasm::WasmCode::kWasmRethrow, RelocInfo::WASM_STUB_CALL); + Node* throw_effect = + graph()->NewNode(mcgraph()->common()->Call(call_descriptor), + call_target, return_value, Effect(), Control()); + TerminateThrow(throw_effect, Control()); + + puts("[WASM-PL] Checking returns"); + SetControl( + graph()->NewNode(mcgraph()->common()->IfTrue(), exception_branch)); + DCHECK_LT(sig_->return_count(), wasm::kV8MaxWasmFunctionMultiReturns); + int return_count = static_cast(sig_->return_count()); + if (return_count == 0) { + Return(Int32Constant(0)); + } else { + Node** returns = Buffer(return_count); + offset = 0; + for (int i = 0; i < return_count; ++i) { + wasm::ValueType type = sig_->GetReturn(i); + Node* val = SetEffect( + graph()->NewNode(GetSafeLoadOperator(offset, type), values, + Int32Constant(offset), Effect(), Control())); + returns[i] = val; + offset += wasm::ValueTypes::ElementSizeInBytes(type); + } + Return(return_count, returns); + } + + if (ContainsInt64(sig_)) LowerInt64(); + } + void BuildWasmInterpreterEntry(int func_index) { int param_count = static_cast(sig_->parameter_count()); @@ -5968,6 +6099,13 @@ WasmImportCallKind GetWasmImportCallKind(Handle target, } return WasmImportCallKind::kWasmToCapi; } + if (WasmPreloadFunction::IsWasmPreloadFunction(*target)) { + WasmPreloadFunction preload_function = WasmPreloadFunction::cast(*target); + if (!preload_function.IsSignatureEqual(expected_sig)) { + return WasmImportCallKind::kLinkError; + } + return WasmImportCallKind::kWasmToPreload; + } // Assuming we are calling to JS, check whether this would be a runtime error. if (!wasm::IsJSCompatibleSignature(expected_sig, has_bigint_feature)) { return WasmImportCallKind::kRuntimeTypeError; @@ -6262,6 +6400,73 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine, return native_module->PublishCode(std::move(wasm_code)); } +// TODO(ohadrau): Change the semantics of a preload +wasm::WasmCode* CompileWasmPreloadCallWrapper(wasm::WasmEngine* wasm_engine, + wasm::NativeModule* native_module, + wasm::FunctionSig* sig, + Address address, + size_t memory_size, + i::byte *memory_start) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "CompilePreloadFunction"); + + puts("[WASM-PL] Creating zone"); + Zone zone(wasm_engine->allocator(), ZONE_NAME); + + puts("[WASM-PL] Building graph"); + // TODO(jkummerow): Extract common code into helper method. + SourcePositionTable* source_positions = nullptr; + MachineGraph* mcgraph = new (&zone) MachineGraph( + new (&zone) Graph(&zone), new (&zone) CommonOperatorBuilder(&zone), + new (&zone) MachineOperatorBuilder( + &zone, MachineType::PointerRepresentation(), + InstructionSelector::SupportedMachineOperatorFlags(), + InstructionSelector::AlignmentRequirements())); + JSGraph jsgraph(nullptr, mcgraph->graph(), mcgraph->common(), nullptr, + nullptr, mcgraph->machine()); + + puts("[WASM-PL] Creating graph builder"); + WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, source_positions, + StubCallMode::kCallWasmRuntimeStub, + native_module->enabled_features()); + + puts("[WASM-PL] Setting up graph start"); + // Set up the graph start. + int param_count = static_cast(sig->parameter_count()) + + 1 /* offset for first parameter index being -1 */ + + 1 /* Wasm instance */ + 1 /* kExtraCallableParam */; + Node* start = builder.Start(param_count); + Node* effect = start; + Node* control = start; + builder.set_effect_ptr(&effect); + builder.set_control_ptr(&control); + builder.set_instance_node(builder.Param(wasm::kWasmInstanceParameterIndex)); + puts("[WASM-PL] Building call wrapper"); + builder.BuildPreloadCallWrapper(address, memory_size, memory_start); + + puts("[WASM-PL] Creating call descriptor"); + // Run the compiler pipeline to generate machine code. + CallDescriptor* call_descriptor = + GetWasmCallDescriptor(&zone, sig, WasmGraphBuilder::kNoRetpoline, + WasmCallKind::kWasmPreloadFunction); + if (mcgraph->machine()->Is32()) { + call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor); + } + + puts("[WASM-PL] Generating code"); + const char* debug_name = "WasmPreloadCall"; + wasm::WasmCompilationResult result = Pipeline::GenerateCodeForWasmNativeStub( + wasm_engine, call_descriptor, mcgraph, Code::WASM_TO_PRELOAD_FUNCTION, + wasm::WasmCode::kWasmToPreloadWrapper, debug_name, + WasmStubAssemblerOptions(), source_positions); + std::unique_ptr wasm_code = native_module->AddCode( + wasm::kAnonymousFuncIndex, result.code_desc, result.frame_slot_count, + result.tagged_parameter_slots, std::move(result.protected_instructions), + std::move(result.source_positions), wasm::WasmCode::kWasmToPreloadWrapper, + wasm::ExecutionTier::kNone); + puts("[WASM-PL] Publishing code"); + return native_module->PublishCode(std::move(wasm_code)); +} + wasm::WasmCompilationResult CompileWasmInterpreterEntry( wasm::WasmEngine* wasm_engine, const wasm::WasmFeatures& enabled_features, uint32_t func_index, wasm::FunctionSig* sig) { @@ -6536,7 +6741,8 @@ CallDescriptor* GetWasmCallDescriptor( // The extra here is to accomodate the instance object as first parameter // and, when specified, the additional callable. bool extra_callable_param = - call_kind == kWasmImportWrapper || call_kind == kWasmCapiFunction; + call_kind == kWasmImportWrapper || call_kind == kWasmCapiFunction || + call_kind == kWasmPreloadFunction; int extra_params = extra_callable_param ? 2 : 1; LocationSignature::Builder locations(zone, fsig->return_count(), fsig->parameter_count() + extra_params); @@ -6606,9 +6812,11 @@ CallDescriptor* GetWasmCallDescriptor( descriptor_kind = CallDescriptor::kCallWasmFunction; } else if (call_kind == kWasmImportWrapper) { descriptor_kind = CallDescriptor::kCallWasmImportWrapper; - } else { - DCHECK_EQ(call_kind, kWasmCapiFunction); + } else if (call_kind == kWasmCapiFunction) { descriptor_kind = CallDescriptor::kCallWasmCapiFunction; + } else { + DCHECK_EQ(call_kind, kWasmPreloadFunction); + descriptor_kind = CallDescriptor::kCallWasmPreloadFunction; } CallDescriptor::Flags flags = diff --git a/deps/v8/src/compiler/wasm-compiler.h b/deps/v8/src/compiler/wasm-compiler.h index cf95596a021..e87ed92ecfa 100644 --- a/deps/v8/src/compiler/wasm-compiler.h +++ b/deps/v8/src/compiler/wasm-compiler.h @@ -69,6 +69,7 @@ enum class WasmImportCallKind : uint8_t { kLinkError, // static WASM->WASM type error kRuntimeTypeError, // runtime WASM->JS type error kWasmToCapi, // fast WASM->C-API call + kWasmToPreload, // fast WASM->V8 API call kWasmToWasm, // fast WASM->WASM call kJSFunctionArityMatch, // fast WASM->JS call kJSFunctionArityMatchSloppy, // fast WASM->JS call, sloppy receiver @@ -124,6 +125,14 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine*, wasm::NativeModule*, wasm::FunctionSig*, Address address); +// Compiles a host call wrapper, which allows WASM to call host functions. +wasm::WasmCode* CompileWasmPreloadCallWrapper(wasm::WasmEngine*, + wasm::NativeModule*, + wasm::FunctionSig*, + Address address, + size_t memory_size, + i::byte* memory_start); + // Returns an OptimizedCompilationJob object for a JS to Wasm wrapper. std::unique_ptr NewJSToWasmCompilationJob( Isolate* isolate, wasm::FunctionSig* sig, bool is_import); @@ -611,7 +620,8 @@ class WasmGraphBuilder { TrapId GetTrapIdForTrap(wasm::TrapReason reason); }; -enum WasmCallKind { kWasmFunction, kWasmImportWrapper, kWasmCapiFunction }; +enum WasmCallKind { kWasmFunction, kWasmImportWrapper, kWasmCapiFunction, + kWasmPreloadFunction }; V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor( Zone* zone, wasm::FunctionSig* signature, diff --git a/deps/v8/src/diagnostics/objects-debug.cc b/deps/v8/src/diagnostics/objects-debug.cc index 5ba4cadf0ef..18229188579 100644 --- a/deps/v8/src/diagnostics/objects-debug.cc +++ b/deps/v8/src/diagnostics/objects-debug.cc @@ -1045,7 +1045,7 @@ void SharedFunctionInfo::SharedFunctionInfoVerify(Isolate* isolate) { HasBytecodeArray() || HasAsmWasmData() || HasBuiltinId() || HasUncompiledDataWithPreparseData() || HasUncompiledDataWithoutPreparseData() || HasWasmJSFunctionData() || - HasWasmCapiFunctionData()); + HasWasmCapiFunctionData() || HasWasmPreloadFunctionData()); CHECK(script_or_debug_info().IsUndefined(isolate) || script_or_debug_info().IsScript() || HasDebugInfo()); @@ -1746,6 +1746,8 @@ USE_TORQUE_VERIFIER(FunctionTemplateRareData) USE_TORQUE_VERIFIER(WasmCapiFunctionData) +USE_TORQUE_VERIFIER(WasmPreloadFunctionData) + USE_TORQUE_VERIFIER(WasmJSFunctionData) USE_TORQUE_VERIFIER(ObjectTemplateInfo) diff --git a/deps/v8/src/diagnostics/objects-printer.cc b/deps/v8/src/diagnostics/objects-printer.cc index 8430b9b794e..a15a7ceac83 100644 --- a/deps/v8/src/diagnostics/objects-printer.cc +++ b/deps/v8/src/diagnostics/objects-printer.cc @@ -2071,6 +2071,16 @@ void WasmCapiFunctionData::WasmCapiFunctionDataPrint( os << "\n"; } +void WasmPreloadFunctionData::WasmPreloadFunctionDataPrint( + std::ostream& os) { // NOLINT + PrintHeader(os, "WasmPreloadFunctionData"); + os << "\n - call_target: " << call_target(); + os << "\n - embedder_data: " << embedder_data(); + os << "\n - wrapper_code: " << Brief(wrapper_code()); + os << "\n - serialized_signature: " << Brief(serialized_signature()); + os << "\n"; +} + void ObjectTemplateInfo::ObjectTemplateInfoPrint(std::ostream& os) { // NOLINT PrintHeader(os, "ObjectTemplateInfo"); os << "\n - tag: " << Brief(tag()); diff --git a/deps/v8/src/execution/frames.cc b/deps/v8/src/execution/frames.cc index e39026b21fc..ab07797ada4 100644 --- a/deps/v8/src/execution/frames.cc +++ b/deps/v8/src/execution/frames.cc @@ -536,6 +536,7 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator, case wasm::WasmCode::kFunction: return WASM_COMPILED; case wasm::WasmCode::kWasmToCapiWrapper: + case wasm::WasmCode::kWasmToPreloadWrapper: return WASM_EXIT; case wasm::WasmCode::kWasmToJsWrapper: return WASM_TO_JS; @@ -578,6 +579,7 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator, return C_WASM_ENTRY; case Code::WASM_FUNCTION: case Code::WASM_TO_CAPI_FUNCTION: + case Code::WASM_TO_PRELOAD_FUNCTION: case Code::WASM_TO_JS_FUNCTION: case Code::WASM_INTERPRETER_ENTRY: // Never appear as on-heap {Code} objects. @@ -915,7 +917,10 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const { safepoint_entry = table.FindEntry(inner_pointer); stack_slots = wasm_code->stack_slots(); has_tagged_params = wasm_code->kind() != wasm::WasmCode::kFunction && - wasm_code->kind() != wasm::WasmCode::kWasmToCapiWrapper; + wasm_code->kind() != + wasm::WasmCode::kWasmToCapiWrapper && + wasm_code->kind() != + wasm::WasmCode::kWasmToPreloadWrapper; tagged_parameter_slots = wasm_code->tagged_parameter_slots(); } else { InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry = diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index 92b82d0af3e..dcb834d010d 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -404,6 +404,7 @@ using DebugObjectCache = std::vector>; V(ExtensionCallback, wasm_instance_callback, &NoExtension) \ V(WasmStreamingCallback, wasm_streaming_callback, nullptr) \ V(WasmThreadsEnabledCallback, wasm_threads_enabled_callback, nullptr) \ + V(Eternal, wasm_native_imports, Eternal()) \ /* State for Relocatable. */ \ V(Relocatable*, relocatable_top, nullptr) \ V(DebugObjectCache*, string_stream_debug_object_cache, nullptr) \ diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 3602aef5f3d..988d710f5e1 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -3437,6 +3437,12 @@ Handle Factory::NewSharedFunctionInfoForWasmCapiFunction( Builtins::kNoBuiltinId, kConciseMethod); } +Handle Factory::NewSharedFunctionInfoForWasmPreloadFunction( + Handle data) { + return NewSharedFunctionInfo(MaybeHandle(), data, + Builtins::kNoBuiltinId, kConciseMethod); +} + Handle Factory::NewSharedFunctionInfoForBuiltin( MaybeHandle maybe_name, int builtin_index, FunctionKind kind) { Handle shared = NewSharedFunctionInfo( diff --git a/deps/v8/src/heap/factory.h b/deps/v8/src/heap/factory.h index 3ccbe6856f8..317946047a0 100644 --- a/deps/v8/src/heap/factory.h +++ b/deps/v8/src/heap/factory.h @@ -69,6 +69,7 @@ class TemplateObjectDescription; class UncompiledDataWithoutPreparseData; class UncompiledDataWithPreparseData; class WasmCapiFunctionData; +class WasmPreloadFunctionData; class WasmExportedFunctionData; class WasmJSFunctionData; class WeakCell; @@ -849,6 +850,9 @@ class V8_EXPORT_PRIVATE Factory { Handle NewSharedFunctionInfoForWasmCapiFunction( Handle data); + Handle NewSharedFunctionInfoForWasmPreloadFunction( + Handle data); + Handle NewSharedFunctionInfoForBuiltin( MaybeHandle name, int builtin_index, FunctionKind kind = kNormalFunction); diff --git a/deps/v8/src/heap/objects-visiting.h b/deps/v8/src/heap/objects-visiting.h index b343eafd457..386dbae33c4 100644 --- a/deps/v8/src/heap/objects-visiting.h +++ b/deps/v8/src/heap/objects-visiting.h @@ -62,6 +62,7 @@ namespace internal { V(UncompiledDataWithoutPreparseData, UncompiledDataWithoutPreparseData) \ V(UncompiledDataWithPreparseData, UncompiledDataWithPreparseData) \ V(WasmCapiFunctionData, WasmCapiFunctionData) \ + V(WasmPreloadFunctionData, WasmPreloadFunctionData) \ V(WasmInstanceObject, WasmInstanceObject) #define FORWARD_DECLARE(TypeName, Type) class Type; diff --git a/deps/v8/src/logging/log.cc b/deps/v8/src/logging/log.cc index 02bddd35ba0..78d72bca0db 100644 --- a/deps/v8/src/logging/log.cc +++ b/deps/v8/src/logging/log.cc @@ -1979,6 +1979,10 @@ void ExistingCodeLogger::LogCodeObject(Object object) { description = "A Wasm to C-API adapter"; tag = CodeEventListener::STUB_TAG; break; + case AbstractCode::WASM_TO_PRELOAD_FUNCTION: + description = "A Wasm to Preload adapter"; + tag = CodeEventListener::STUB_TAG; + break; case AbstractCode::WASM_TO_JS_FUNCTION: description = "A Wasm to JavaScript adapter"; tag = CodeEventListener::STUB_TAG; diff --git a/deps/v8/src/objects/code.h b/deps/v8/src/objects/code.h index 6e3c8fce6ca..5dcd0f9992f 100644 --- a/deps/v8/src/objects/code.h +++ b/deps/v8/src/objects/code.h @@ -35,17 +35,18 @@ class Code : public HeapObject { // cache state, and arguments count. using Flags = uint32_t; -#define CODE_KIND_LIST(V) \ - V(OPTIMIZED_FUNCTION) \ - V(BYTECODE_HANDLER) \ - V(STUB) \ - V(BUILTIN) \ - V(REGEXP) \ - V(WASM_FUNCTION) \ - V(WASM_TO_CAPI_FUNCTION) \ - V(WASM_TO_JS_FUNCTION) \ - V(JS_TO_WASM_FUNCTION) \ - V(WASM_INTERPRETER_ENTRY) \ +#define CODE_KIND_LIST(V) \ + V(OPTIMIZED_FUNCTION) \ + V(BYTECODE_HANDLER) \ + V(STUB) \ + V(BUILTIN) \ + V(REGEXP) \ + V(WASM_FUNCTION) \ + V(WASM_TO_CAPI_FUNCTION) \ + V(WASM_TO_PRELOAD_FUNCTION) \ + V(WASM_TO_JS_FUNCTION) \ + V(JS_TO_WASM_FUNCTION) \ + V(WASM_INTERPRETER_ENTRY) \ V(C_WASM_ENTRY) enum Kind { diff --git a/deps/v8/src/objects/instance-type.h b/deps/v8/src/objects/instance-type.h index e340b53e1da..29100866004 100644 --- a/deps/v8/src/objects/instance-type.h +++ b/deps/v8/src/objects/instance-type.h @@ -166,6 +166,7 @@ enum InstanceType : uint16_t { TUPLE3_TYPE, ARRAY_BOILERPLATE_DESCRIPTION_TYPE, WASM_CAPI_FUNCTION_DATA_TYPE, + WASM_PRELOAD_FUNCTION_DATA_TYPE, WASM_DEBUG_INFO_TYPE, WASM_EXCEPTION_TAG_TYPE, WASM_EXPORTED_FUNCTION_DATA_TYPE, diff --git a/deps/v8/src/objects/map.cc b/deps/v8/src/objects/map.cc index 4e3f271f47c..4ca22584eb3 100644 --- a/deps/v8/src/objects/map.cc +++ b/deps/v8/src/objects/map.cc @@ -352,6 +352,9 @@ VisitorId Map::GetVisitorId(Map map) { if (instance_type == WASM_CAPI_FUNCTION_DATA_TYPE) { return kVisitWasmCapiFunctionData; } + if (instance_type == WASM_PRELOAD_FUNCTION_DATA_TYPE) { + return kVisitWasmPreloadFunctionData; + } return kVisitStruct; case LOAD_HANDLER_TYPE: diff --git a/deps/v8/src/objects/map.h b/deps/v8/src/objects/map.h index 65a30168036..30501976196 100644 --- a/deps/v8/src/objects/map.h +++ b/deps/v8/src/objects/map.h @@ -74,6 +74,7 @@ enum InstanceType : uint16_t; V(UncompiledDataWithoutPreparseData) \ V(UncompiledDataWithPreparseData) \ V(WasmCapiFunctionData) \ + V(WasmPreloadFunctionData) \ V(WasmInstanceObject) \ V(WeakArray) \ V(WeakCell) diff --git a/deps/v8/src/objects/objects-definitions.h b/deps/v8/src/objects/objects-definitions.h index d356f11ce29..a534bd5b7e6 100644 --- a/deps/v8/src/objects/objects-definitions.h +++ b/deps/v8/src/objects/objects-definitions.h @@ -105,6 +105,7 @@ namespace internal { V(TUPLE3_TYPE) \ V(ARRAY_BOILERPLATE_DESCRIPTION_TYPE) \ V(WASM_CAPI_FUNCTION_DATA_TYPE) \ + V(WASM_PRELOAD_FUNCTION_DATA_TYPE) \ V(WASM_DEBUG_INFO_TYPE) \ V(WASM_EXCEPTION_TAG_TYPE) \ V(WASM_EXPORTED_FUNCTION_DATA_TYPE) \ @@ -333,6 +334,8 @@ namespace internal { array_boilerplate_description) \ V(_, WASM_CAPI_FUNCTION_DATA_TYPE, WasmCapiFunctionData, \ wasm_capi_function_data) \ + V(_, WASM_PRELOAD_FUNCTION_DATA_TYPE, WasmPreloadFunctionData, \ + wasm_preload_function_data) \ V(_, WASM_DEBUG_INFO_TYPE, WasmDebugInfo, wasm_debug_info) \ V(_, WASM_EXCEPTION_TAG_TYPE, WasmExceptionTag, wasm_exception_tag) \ V(_, WASM_EXPORTED_FUNCTION_DATA_TYPE, WasmExportedFunctionData, \ diff --git a/deps/v8/src/objects/objects.cc b/deps/v8/src/objects/objects.cc index 396c7700703..9754218a0df 100644 --- a/deps/v8/src/objects/objects.cc +++ b/deps/v8/src/objects/objects.cc @@ -4919,6 +4919,8 @@ Code SharedFunctionInfo::GetCode() const { return wasm_js_function_data().wrapper_code(); } else if (data.IsWasmCapiFunctionData()) { return wasm_capi_function_data().wrapper_code(); + } else if (data.IsWasmPreloadFunctionData()) { + return wasm_preload_function_data().wrapper_code(); } UNREACHABLE(); } @@ -4939,6 +4941,11 @@ WasmCapiFunctionData SharedFunctionInfo::wasm_capi_function_data() const { return WasmCapiFunctionData::cast(function_data()); } +WasmPreloadFunctionData SharedFunctionInfo::wasm_preload_function_data() const { + DCHECK(HasWasmPreloadFunctionData()); + return WasmPreloadFunctionData::cast(function_data()); +} + SharedFunctionInfo::ScriptIterator::ScriptIterator(Isolate* isolate, Script script) : ScriptIterator(handle(script.shared_function_infos(), isolate)) {} diff --git a/deps/v8/src/objects/shared-function-info-inl.h b/deps/v8/src/objects/shared-function-info-inl.h index 9778db5d908..8dda5c87303 100644 --- a/deps/v8/src/objects/shared-function-info-inl.h +++ b/deps/v8/src/objects/shared-function-info-inl.h @@ -667,6 +667,10 @@ bool SharedFunctionInfo::HasWasmCapiFunctionData() const { return function_data().IsWasmCapiFunctionData(); } +bool SharedFunctionInfo::HasWasmPreloadFunctionData() const { + return function_data().IsWasmPreloadFunctionData(); +} + Object SharedFunctionInfo::script() const { Object maybe_script = script_or_debug_info(); if (maybe_script.IsDebugInfo()) { diff --git a/deps/v8/src/objects/shared-function-info.h b/deps/v8/src/objects/shared-function-info.h index f7a82964b19..0897f674385 100644 --- a/deps/v8/src/objects/shared-function-info.h +++ b/deps/v8/src/objects/shared-function-info.h @@ -33,6 +33,7 @@ class CoverageInfo; class DebugInfo; class IsCompiledScope; class WasmCapiFunctionData; +class WasmPreloadFunctionData; class WasmExportedFunctionData; class WasmJSFunctionData; @@ -372,6 +373,8 @@ class SharedFunctionInfo : public HeapObject { WasmJSFunctionData wasm_js_function_data() const; inline bool HasWasmCapiFunctionData() const; WasmCapiFunctionData wasm_capi_function_data() const; + inline bool HasWasmPreloadFunctionData() const; + WasmPreloadFunctionData wasm_preload_function_data() const; // Clear out pre-parsed scope data from UncompiledDataWithPreparseData, // turning it into UncompiledDataWithoutPreparseData. diff --git a/deps/v8/src/wasm/module-instantiate.cc b/deps/v8/src/wasm/module-instantiate.cc index a28b8916024..a8a5f4149d2 100644 --- a/deps/v8/src/wasm/module-instantiate.cc +++ b/deps/v8/src/wasm/module-instantiate.cc @@ -810,6 +810,7 @@ bool InstanceBuilder::ProcessImportedFunction( Handle instance, int import_index, int func_index, Handle module_name, Handle import_name, Handle value) { + puts("[WASM-PL] Processing imported function"); // Function imports must be callable. if (!value->IsCallable()) { ReportLinkError("function import requires a callable", import_index, @@ -857,6 +858,33 @@ bool InstanceBuilder::ProcessImportedFunction( entry.SetWasmToJs(isolate_, js_receiver, wasm_code); break; } + case compiler::WasmImportCallKind::kWasmToPreload: { + puts("[WASM-PL] Acquiring native module"); + NativeModule* native_module = instance->module_object().native_module(); + puts("[WASM-PL] Getting call target"); + Address host_address = + WasmPreloadFunction::cast(*value).GetHostCallTarget(); + WasmCodeRefScope code_ref_scope; + puts("[WASM-PL] Compiling call wrapper"); + WasmCode* wasm_code = compiler::CompileWasmPreloadCallWrapper( + isolate_->wasm_engine(), native_module, expected_sig, host_address, + instance->memory_size(), instance->memory_start()); + wasm_code->Print("[WASM-PL]"); + puts("[WASM-PL] Incrementing counters"); + isolate_->counters()->wasm_generated_code_size()->Increment( + wasm_code->instructions().length()); + isolate_->counters()->wasm_reloc_size()->Increment( + wasm_code->reloc_info().length()); + + puts("[WASM-PL] Getting function entry"); + ImportedFunctionEntry entry(instance, func_index); + // We re-use the SetWasmToJs infrastructure because it passes the + // callable to the wrapper, which we need to get the function data. + puts("[WASM-PL] Setting WasmToJs"); + entry.SetWasmToJs(isolate_, js_receiver, wasm_code); + puts("[WASM-PL] Compiled preload"); + break; + } default: { // The imported function is a callable. NativeModule* native_module = instance->module_object().native_module(); @@ -1204,7 +1232,8 @@ void InstanceBuilder::CompileImportWrappers( compiler::GetWasmImportCallKind(js_receiver, sig, enabled_.bigint); if (kind == compiler::WasmImportCallKind::kWasmToWasm || kind == compiler::WasmImportCallKind::kLinkError || - kind == compiler::WasmImportCallKind::kWasmToCapi) { + kind == compiler::WasmImportCallKind::kWasmToCapi || + kind == compiler::WasmImportCallKind::kWasmToPreload) { continue; } WasmImportWrapperCache::CacheKey key(kind, sig); @@ -1238,6 +1267,7 @@ void InstanceBuilder::CompileImportWrappers( // order, loading them from the {ffi_} object. Returns the number of imported // functions. int InstanceBuilder::ProcessImports(Handle instance) { + puts("[WASM-PL] Processing imports"); int num_imported_functions = 0; int num_imported_tables = 0; @@ -1260,6 +1290,7 @@ int InstanceBuilder::ProcessImports(Handle instance) { import_name, value)) { return -1; } + puts("[WASM-PL] Processed imported function"); num_imported_functions++; break; } diff --git a/deps/v8/src/wasm/wasm-code-manager.cc b/deps/v8/src/wasm/wasm-code-manager.cc index 73d686b2224..3b2adb4da4c 100644 --- a/deps/v8/src/wasm/wasm-code-manager.cc +++ b/deps/v8/src/wasm/wasm-code-manager.cc @@ -354,6 +354,8 @@ const char* GetWasmCodeKindAsString(WasmCode::Kind kind) { return "wasm function"; case WasmCode::kWasmToCapiWrapper: return "wasm-to-capi"; + case WasmCode::kWasmToPreloadWrapper: + return "wasm-to-preload"; case WasmCode::kWasmToJsWrapper: return "wasm-to-js"; case WasmCode::kRuntimeStub: diff --git a/deps/v8/src/wasm/wasm-code-manager.h b/deps/v8/src/wasm/wasm-code-manager.h index 3ea2bf0936d..91d38175117 100644 --- a/deps/v8/src/wasm/wasm-code-manager.h +++ b/deps/v8/src/wasm/wasm-code-manager.h @@ -73,6 +73,7 @@ class V8_EXPORT_PRIVATE WasmCode final { enum Kind { kFunction, kWasmToCapiWrapper, + kWasmToPreloadWrapper, kWasmToJsWrapper, kRuntimeStub, kInterpreterEntry, diff --git a/deps/v8/src/wasm/wasm-feature-flags.h b/deps/v8/src/wasm/wasm-feature-flags.h index 77d46fdc0d5..9b97000c5b0 100644 --- a/deps/v8/src/wasm/wasm-feature-flags.h +++ b/deps/v8/src/wasm/wasm-feature-flags.h @@ -29,5 +29,7 @@ SEPARATOR \ V(type_reflection, "wasm type reflection in JS", false) \ SEPARATOR \ - V(compilation_hints, "compilation hints section", false) + V(compilation_hints, "compilation hints section", false) \ + SEPARATOR \ + V(preloads, "WASM function preloads", true) #endif // V8_WASM_WASM_FEATURE_FLAGS_H_ diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc index 7ab0ce558d2..a8d035f6e6c 100644 --- a/deps/v8/src/wasm/wasm-js.cc +++ b/deps/v8/src/wasm/wasm-js.cc @@ -663,6 +663,21 @@ void WebAssemblyValidate(const v8::FunctionCallbackInfo& args) { return_value.Set(Boolean::New(isolate, validated)); } +// WebAssembly.preloads() -> Object +void WebAssemblyPreloads(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast(isolate); + HandleScope scope(isolate); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.preloads()"); + + v8::ReturnValue return_value = args.GetReturnValue(); + Eternal imports = i_isolate->wasm_native_imports(); + Local result = imports.Get(isolate); + i::Handle real_result = + i::Handle(reinterpret_cast(*result)); + return_value.Set(Utils::ToLocal(real_result)); +} + // new WebAssembly.Module(bytes) -> WebAssembly.Module void WebAssemblyModule(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); @@ -2268,6 +2283,11 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) { context->set_wasm_exported_function_map(*function_map); } + // Setup preloads + if (enabled_features.preloads) { + InstallFunc(isolate, webassembly, "preloads", WebAssemblyPreloads, 1); + } + // Setup errors Handle compile_error( isolate->native_context()->wasm_compile_error_function(), isolate); diff --git a/deps/v8/src/wasm/wasm-js.h b/deps/v8/src/wasm/wasm-js.h index 4811288f4d8..a707f2ec3ee 100644 --- a/deps/v8/src/wasm/wasm-js.h +++ b/deps/v8/src/wasm/wasm-js.h @@ -5,6 +5,7 @@ #ifndef V8_WASM_WASM_JS_H_ #define V8_WASM_WASM_JS_H_ +#include "include/v8.h" #include "src/common/globals.h" namespace v8 { diff --git a/deps/v8/src/wasm/wasm-objects-inl.h b/deps/v8/src/wasm/wasm-objects-inl.h index d90123555b0..fe76cd3aa63 100644 --- a/deps/v8/src/wasm/wasm-objects-inl.h +++ b/deps/v8/src/wasm/wasm-objects-inl.h @@ -347,6 +347,25 @@ ACCESSORS(WasmCapiFunctionData, wrapper_code, Code, kWrapperCodeOffset) ACCESSORS(WasmCapiFunctionData, serialized_signature, PodArray, kSerializedSignatureOffset) +// WasmPreloadFunction +WasmPreloadFunction::WasmPreloadFunction(Address ptr) : JSFunction(ptr) { + SLOW_DCHECK(IsWasmPreloadFunction(*this)); +} +CAST_ACCESSOR(WasmPreloadFunction) + +// WasmCapiFunctionData +OBJECT_CONSTRUCTORS_IMPL(WasmPreloadFunctionData, Struct) +CAST_ACCESSOR(WasmPreloadFunctionData) +PRIMITIVE_ACCESSORS(WasmPreloadFunctionData, call_target, Address, + kCallTargetOffset) +PRIMITIVE_ACCESSORS(WasmPreloadFunctionData, embedder_data, void*, + kEmbedderDataOffset) +ACCESSORS(WasmPreloadFunctionData, wrapper_code, Code, kWrapperCodeOffset) +ACCESSORS(WasmPreloadFunctionData, serialized_signature, + PodArray, kSerializedSignatureOffset) + + + // WasmDebugInfo ACCESSORS(WasmDebugInfo, wasm_instance, WasmInstanceObject, kInstanceOffset) ACCESSORS(WasmDebugInfo, interpreter_handle, Object, kInterpreterHandleOffset) diff --git a/deps/v8/src/wasm/wasm-objects.cc b/deps/v8/src/wasm/wasm-objects.cc index cd117db5657..44e684ed8ae 100644 --- a/deps/v8/src/wasm/wasm-objects.cc +++ b/deps/v8/src/wasm/wasm-objects.cc @@ -898,11 +898,12 @@ bool WasmTableObject::IsValidElement(Isolate* isolate, // Anyref tables take everything. if (table->type() == wasm::kWasmAnyRef) return true; // Anyfunc tables can store {null}, {WasmExportedFunction}, {WasmJSFunction}, - // or {WasmCapiFunction} objects. + // {WasmCapiFunction}, or {WasmPreloadFunction} objects. if (entry->IsNull(isolate)) return true; return WasmExportedFunction::IsWasmExportedFunction(*entry) || WasmJSFunction::IsWasmJSFunction(*entry) || - WasmCapiFunction::IsWasmCapiFunction(*entry); + WasmCapiFunction::IsWasmCapiFunction(*entry) || + WasmPreloadFunction::IsWasmPreloadFunction(*entry); } void WasmTableObject::Set(Isolate* isolate, Handle table, @@ -938,10 +939,13 @@ void WasmTableObject::Set(Isolate* isolate, Handle table, } else if (WasmJSFunction::IsWasmJSFunction(*entry)) { UpdateDispatchTables(isolate, table, entry_index, Handle::cast(entry)); - } else { - DCHECK(WasmCapiFunction::IsWasmCapiFunction(*entry)); + } else if (WasmCapiFunction::IsWasmCapiFunction(*entry)) { UpdateDispatchTables(isolate, table, entry_index, Handle::cast(entry)); + } else { + DCHECK(WasmPreloadFunction::IsWasmPreloadFunction(*entry)); + UpdateDispatchTables(isolate, table, entry_index, + Handle::cast(entry)); } entries->set(entry_index, *entry); } @@ -963,7 +967,8 @@ Handle WasmTableObject::Get(Isolate* isolate, // Now we handle the anyfunc case. if (WasmExportedFunction::IsWasmExportedFunction(*entry) || - WasmCapiFunction::IsWasmCapiFunction(*entry)) { + WasmCapiFunction::IsWasmCapiFunction(*entry) || + WasmPreloadFunction::IsWasmPreloadFunction(*entry)) { return entry; } @@ -1115,6 +1120,67 @@ void WasmTableObject::UpdateDispatchTables( } } +void WasmTableObject::UpdateDispatchTables( + Isolate* isolate, Handle table, int entry_index, + Handle preload_function) { + // We simply need to update the IFTs for each instance that imports + // this table. + Handle dispatch_tables(table->dispatch_tables(), isolate); + DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); + + // Reconstruct signature. + // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc. + PodArray serialized_sig = + preload_function->GetSerializedSignature(); + int total_count = serialized_sig.length() - 1; + std::unique_ptr reps(new wasm::ValueType[total_count]); + int result_count; + static const wasm::ValueType kMarker = wasm::kWasmStmt; + for (int i = 0, j = 0; i <= total_count; i++) { + if (serialized_sig.get(i) == kMarker) { + result_count = i; + continue; + } + reps[j++] = serialized_sig.get(i); + } + int param_count = total_count - result_count; + wasm::FunctionSig sig(result_count, param_count, reps.get()); + + for (int i = 0; i < dispatch_tables->length(); + i += kDispatchTableNumElements) { + int table_index = + Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value(); + if (table_index > 0) { + // Only table 0 has a dispatch table in the instance at the moment. + // TODO(ahaas): Introduce dispatch tables for the other tables as well. + continue; + } + Handle instance( + WasmInstanceObject::cast( + dispatch_tables->get(i + kDispatchTableInstanceOffset)), + isolate); + // TODO(jkummerow): Find a way to avoid recompiling wrappers. + wasm::NativeModule* native_module = + instance->module_object().native_module(); + Address host_address = preload_function->GetHostCallTarget(); + wasm::WasmCodeRefScope code_ref_scope; + wasm::WasmCode* wasm_code = compiler::CompileWasmPreloadCallWrapper( + isolate->wasm_engine(), native_module, &sig, host_address, + instance->memory_size(), instance->memory_start()); + isolate->counters()->wasm_generated_code_size()->Increment( + wasm_code->instructions().length()); + isolate->counters()->wasm_reloc_size()->Increment( + wasm_code->reloc_info().length()); + Handle tuple = isolate->factory()->NewTuple2( + instance, preload_function, AllocationType::kOld); + // Note that {SignatureMap::Find} may return {-1} if the signature is + // not found; it will simply never match any check. + auto sig_id = instance->module()->signature_map.Find(sig); + IndirectFunctionTableEntry(instance, entry_index) + .Set(sig_id, wasm_code->instruction_start(), *tuple); + } +} + void WasmTableObject::ClearDispatchTables(Isolate* isolate, Handle table, int index) { @@ -1531,7 +1597,8 @@ void ImportedFunctionEntry::SetWasmToJs( instance_->ptr(), index_, callable->ptr(), wasm_to_js_wrapper->instructions().begin()); DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper || - wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper); + wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper || + wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToPreloadWrapper); Handle tuple = isolate->factory()->NewTuple2(instance_, callable, AllocationType::kOld); instance_->imported_function_refs().set(index_, *tuple); @@ -2008,6 +2075,27 @@ bool WasmCapiFunction::IsSignatureEqual(const wasm::FunctionSig* sig) const { return true; } +bool WasmPreloadFunction::IsSignatureEqual(const wasm::FunctionSig* sig) const { + // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc. + int param_count = static_cast(sig->parameter_count()); + int result_count = static_cast(sig->return_count()); + PodArray serialized_sig = + shared().wasm_preload_function_data().serialized_signature(); + if (param_count + result_count + 1 != serialized_sig.length()) return false; + int serialized_index = 0; + for (int i = 0; i < result_count; i++, serialized_index++) { + if (sig->GetReturn(i) != serialized_sig.get(serialized_index)) { + return false; + } + } + if (serialized_sig.get(serialized_index) != wasm::kWasmStmt) return false; + serialized_index++; + for (int i = 0; i < param_count; i++, serialized_index++) { + if (sig->GetParam(i) != serialized_sig.get(serialized_index)) return false; + } + return true; +} + // static Handle WasmExceptionPackage::New( Isolate* isolate, Handle exception_tag, int size) { @@ -2131,6 +2219,19 @@ bool WasmCapiFunction::IsWasmCapiFunction(Object object) { return js_function.shared().HasWasmCapiFunctionData(); } +bool WasmPreloadFunction::IsWasmPreloadFunction(Object object) { + if (!object.IsJSFunction()) return false; + JSFunction js_function = JSFunction::cast(object); + // TODO(jkummerow): Enable this when there is a JavaScript wrapper + // able to call this function. + // if (js_function->code()->kind() != Code::WASM_TO_PRELOAD_FUNCTION) { + // return false; + // } + // DCHECK(js_function->shared()->HasWasmPreloadFunctionData()); + // return true; + return js_function.shared().HasWasmPreloadFunctionData(); +} + Handle WasmCapiFunction::New( Isolate* isolate, Address call_target, void* embedder_data, Handle> serialized_signature) { @@ -2151,6 +2252,23 @@ Handle WasmCapiFunction::New( shared, isolate->native_context())); } +Handle WasmPreloadFunction::New( + Isolate* isolate, Address call_target, void* embedder_data, + Handle> serialized_signature) { + Handle fun_data = + Handle::cast(isolate->factory()->NewStruct( + WASM_PRELOAD_FUNCTION_DATA_TYPE, AllocationType::kOld)); + fun_data->set_call_target(call_target); + fun_data->set_embedder_data(embedder_data); + fun_data->set_serialized_signature(*serialized_signature); + fun_data->set_wrapper_code(isolate->builtins()->builtin(Builtins::kIllegal)); + Handle shared = + isolate->factory()->NewSharedFunctionInfoForWasmPreloadFunction(fun_data); + return Handle::cast( + isolate->factory()->NewFunctionFromSharedFunctionInfo( + shared, isolate->native_context())); +} + WasmInstanceObject WasmExportedFunction::instance() { return shared().wasm_exported_function_data().instance(); } @@ -2282,10 +2400,18 @@ Address WasmCapiFunction::GetHostCallTarget() const { return shared().wasm_capi_function_data().call_target(); } +Address WasmPreloadFunction::GetHostCallTarget() const { + return shared().wasm_preload_function_data().call_target(); +} + PodArray WasmCapiFunction::GetSerializedSignature() const { return shared().wasm_capi_function_data().serialized_signature(); } +PodArray WasmPreloadFunction::GetSerializedSignature() const { + return shared().wasm_preload_function_data().serialized_signature(); +} + Handle WasmExceptionTag::New(Isolate* isolate, int index) { Handle result = Handle::cast(isolate->factory()->NewStruct( diff --git a/deps/v8/src/wasm/wasm-objects.h b/deps/v8/src/wasm/wasm-objects.h index f2a27ece992..e68b0ef371c 100644 --- a/deps/v8/src/wasm/wasm-objects.h +++ b/deps/v8/src/wasm/wasm-objects.h @@ -38,6 +38,7 @@ class BreakPoint; class JSArrayBuffer; class SeqOneByteString; class WasmCapiFunction; +class WasmPreloadFunction; class WasmDebugInfo; class WasmExceptionTag; class WasmExportedFunction; @@ -307,6 +308,11 @@ class V8_EXPORT_PRIVATE WasmTableObject : public JSObject { Handle table, int entry_index, Handle capi_function); + static void UpdateDispatchTables(Isolate* isolate, + Handle table, + int entry_index, + Handle + preload_function); static void ClearDispatchTables(Isolate* isolate, Handle table, int index); @@ -721,6 +727,25 @@ class WasmCapiFunction : public JSFunction { OBJECT_CONSTRUCTORS(WasmCapiFunction, JSFunction); }; +// A native function exposed to Wasm via the V8 API +class WasmPreloadFunction : public JSFunction { + public: + static bool IsWasmPreloadFunction(Object object); + + static Handle New( + Isolate* isolate, Address call_target, void* embedder_data, + Handle> serialized_signature); + + Address GetHostCallTarget() const; + PodArray GetSerializedSignature() const; + // Checks whether the given {sig} has the same parameter types as the + // serialized signature stored within this preloaded function object. + bool IsSignatureEqual(const wasm::FunctionSig* sig) const; + + DECL_CAST(WasmPreloadFunction) + OBJECT_CONSTRUCTORS(WasmPreloadFunction, JSFunction); +}; + class WasmCapiFunctionData : public Struct { public: DECL_PRIMITIVE_ACCESSORS(call_target, Address) @@ -742,6 +767,27 @@ class WasmCapiFunctionData : public Struct { OBJECT_CONSTRUCTORS(WasmCapiFunctionData, Struct); }; +class WasmPreloadFunctionData : public Struct { + public: + DECL_PRIMITIVE_ACCESSORS(call_target, Address) + DECL_PRIMITIVE_ACCESSORS(embedder_data, void*) + DECL_ACCESSORS(wrapper_code, Code) + DECL_ACCESSORS(serialized_signature, PodArray) + + DECL_CAST(WasmPreloadFunctionData) + + DECL_PRINTER(WasmPreloadFunctionData) + DECL_VERIFIER(WasmPreloadFunctionData) + + DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, + TORQUE_GENERATED_WASM_PRELOAD_FUNCTION_DATA_FIELDS) + + STATIC_ASSERT(kStartOfStrongFieldsOffset == kWrapperCodeOffset); + using BodyDescriptor = FlexibleBodyDescriptor; + + OBJECT_CONSTRUCTORS(WasmPreloadFunctionData, Struct); +}; + // Information for a WasmExportedFunction which is referenced as the function // data of the SharedFunctionInfo underlying the function. For details please // see the {SharedFunctionInfo::HasWasmExportedFunctionData} predicate. diff --git a/src/node.cc b/src/node.cc index 9c788e64532..58b679040da 100644 --- a/src/node.cc +++ b/src/node.cc @@ -38,6 +38,12 @@ #include "node_v8_platform-inl.h" #include "node_version.h" +#define NODE_USE_V8_WASM_PRELOADS 1 + +#if NODE_USE_V8_WASM_PRELOADS +#include "v8-wasm.h" +#endif // NODE_USE_V8_WASM_PRELOADS + #if HAVE_OPENSSL #include "node_crypto.h" #endif @@ -388,6 +394,27 @@ void MarkBootstrapComplete(const FunctionCallbackInfo& args) { performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); } +#if NODE_USE_V8_WASM_PRELOADS +void hello_world( + const v8::wasm::Memory* memory, + const v8::wasm::Val args[], + v8::wasm::Val results[] +) { + puts("Hello, world!"); + int a = args[0].i32(), + b = args[1].i32(); + int32_t result = a + b; + printf("%d + %d = %d\n", a, b, result); + auto val = v8::wasm::Val(); + val = v8::wasm::Val(result); + assert(val.kind() == v8::wasm::ValKind::I32); + assert(val.i32() == result); + results[0] = v8::wasm::Val(result); + assert(results[0].kind() == v8::wasm::ValKind::I32); + assert(results[0].i32() == result); +} +#endif // NODE_USE_V8_WASM_PRELOADS + MaybeLocal StartExecution(Environment* env, const char* main_script_id) { EscapableHandleScope scope(env->isolate()); CHECK_NOT_NULL(main_script_id); @@ -408,6 +435,25 @@ MaybeLocal StartExecution(Environment* env, const char* main_script_id) { ->GetFunction(env->context()) .ToLocalChecked()}; +#if NODE_USE_V8_WASM_PRELOADS + puts("[WASM-PL] Get isolate"); + v8::Isolate* isolate = env->isolate(); + puts("[WASM-PL] Create function type"); + v8::wasm::FuncType* hello_world_type = + new v8::wasm::FuncType( + { v8::wasm::ValKind::I32, v8::wasm::ValKind::I32 }, + { v8::wasm::ValKind::I32 } + ); + puts("[WASM-PL] Create function"); + v8::wasm::Func* fn_hello_world = + new v8::wasm::Func(hello_world_type, &hello_world); + puts("[WASM-PL] Preload function"); + v8::wasm::PreloadNative(isolate, "node_test", "hello_world", fn_hello_world); + puts("[WASM-PL] Done"); + // TODO(ohadrau): delete hello_world_type, fn_hello_world + // Why does this cause a crash? +#endif // NODE_USE_V8_WASM_PRELOADS + Local result; if (!ExecuteBootstrapper(env, main_script_id, ¶meters, &arguments) .ToLocal(&result) || diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc index 31536000fc8..0745e65d8ad 100644 --- a/src/node_native_module_env.cc +++ b/src/node_native_module_env.cc @@ -1,6 +1,8 @@ #include "node_native_module_env.h" #include "env-inl.h" +#include // [WASM-PL] + namespace node { namespace native_module { @@ -113,6 +115,11 @@ void NativeModuleEnv::RecordResult(const char* id, NativeModuleLoader::Result result, Environment* env) { if (result == NativeModuleLoader::Result::kWithCache) { + printf("[WASM-PL] Inserting to native modules with cache: %s\n", id); + auto set = env->native_modules_with_cache; + for(auto sit = set.begin(); sit != set.end(); sit++) { + std::cout << *sit << std::endl; // [WASM-PL] + } env->native_modules_with_cache.insert(id); } else { env->native_modules_without_cache.insert(id); diff --git a/test_wasm/build.sh b/test_wasm/build.sh new file mode 100755 index 00000000000..4a7b70b8ab5 --- /dev/null +++ b/test_wasm/build.sh @@ -0,0 +1,2 @@ +clang --target=wasm32-unknown-wasi --sysroot=/usr/local/google/home/ohadrau/Projects/wasi-sysroot/sysroot -c test.c -o test.o +wasm-ld --entry=main -allow-undefined-file import.txt test.o -o test.wasm diff --git a/test_wasm/chrome_gdb/gdb_chrome.py b/test_wasm/chrome_gdb/gdb_chrome.py new file mode 100644 index 00000000000..b566d628ad3 --- /dev/null +++ b/test_wasm/chrome_gdb/gdb_chrome.py @@ -0,0 +1,374 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""GDB support for Chrome types. +Add this to your gdb by amending your ~/.gdbinit as follows: + python + import sys + sys.path.insert(1, "/path/to/tools/gdb/") + import gdb_chrome + end +Use + (gdb) p /r any_variable +to print |any_variable| without using any printers. +To interactively type Python for development of the printers: + (gdb) python foo = gdb.parse_and_eval('bar') +to put the C++ value 'bar' in the current scope into a Python variable 'foo'. +Then you can interact with that variable: + (gdb) python print foo['impl_'] +""" +import datetime +import gdb +import gdb.printing +import os +import re +import sys +sys.path.insert( + 1, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'util')) +import class_methods +sys.path.insert( + 1, + os.path.join( + os.path.dirname(os.path.abspath(__file__)), '..', '..', 'third_party', + 'blink', 'tools', 'gdb')) +try: + import blink +finally: + sys.path.pop(1) +# When debugging this module, set the below variable to True, and then use +# (gdb) python del sys.modules['gdb_chrome'] +# (gdb) python import gdb_chrome +# to reload. +_DEBUGGING = False +pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium") +def typed_ptr(ptr): + """Prints a pointer along with its exact type. + By default, gdb would print just the address, which takes more + steps to interpret. + """ + # Returning this as a cast expression surrounded by parentheses + # makes it easier to cut+paste inside of gdb. + return '((%s)%s)' % (ptr.dynamic_type, ptr) +def yield_fields(val): + """Use this in a printer's children() method to print an object's fields. + e.g. + def children(): + for result in yield_fields(self.val): + yield result + """ + try: + fields = val.type.target().fields() + except: + fields = val.type.fields() + for field in fields: + if field.is_base_class: + yield (field.name, val.cast(gdb.lookup_type(field.name))) + else: + yield (field.name, val[field.name]) +class Printer(object): + def __init__(self, val): + self.val = val +class StringPrinter(Printer): + def display_hint(self): + return 'string' +class String16Printer(StringPrinter): + def to_string(self): + return blink.ustring_to_string(self.val['_M_dataplus']['_M_p']) +pp_set.add_printer( + 'string16', '^string16|std::basic_string<(unsigned short|base::char16).*>$', + String16Printer) +class GURLPrinter(StringPrinter): + def to_string(self): + return self.val['spec_'] +pp_set.add_printer('GURL', '^GURL$', GURLPrinter) +class FilePathPrinter(StringPrinter): + def to_string(self): + return self.val['path_']['_M_dataplus']['_M_p'] +pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter) +class SmartPtrPrinter(Printer): + def to_string(self): + return '%s%s' % (self.typename, typed_ptr(self.ptr())) +class ScopedPtrPrinter(SmartPtrPrinter): + typename = 'scoped_ptr' + def ptr(self): + return self.val['impl_']['data_']['ptr'] +pp_set.add_printer('scoped_ptr', '^scoped_ptr<.*>$', ScopedPtrPrinter) +class ScopedRefPtrPrinter(SmartPtrPrinter): + typename = 'scoped_refptr' + def ptr(self): + return self.val['ptr_'] +pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter) +class LinkedPtrPrinter(SmartPtrPrinter): + typename = 'linked_ptr' + def ptr(self): + return self.val['value_'] +pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter) +class WeakPtrPrinter(SmartPtrPrinter): + typename = 'base::WeakPtr' + def ptr(self): + flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr() + if flag and flag['is_valid_']: + return self.val['ptr_'] + return gdb.Value(0).cast(self.val['ptr_'].type) +pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter) +class CallbackPrinter(Printer): + """Callbacks provide no usable information so reduce the space they take.""" + def to_string(self): + return '...' +pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter) +class LocationPrinter(Printer): + def to_string(self): + return '%s()@%s:%s' % (self.val['function_name_'].string(), + self.val['file_name_'].string(), + self.val['line_number_']) +pp_set.add_printer('base::Location', '^base::Location$', LocationPrinter) +class PendingTaskPrinter(Printer): + def to_string(self): + return 'From %s' % (self.val['posted_from'],) + def children(self): + for result in yield_fields(self.val): + if result[0] not in ('task', 'posted_from'): + yield result +pp_set.add_printer('base::PendingTask', '^base::PendingTask$', + PendingTaskPrinter) +class LockPrinter(Printer): + def to_string(self): + try: + if self.val['owned_by_thread_']: + return 'Locked by thread %s' % self.val['owning_thread_id_'] + else: + return 'Unlocked' + except gdb.error: + return 'Unknown state' +pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter) +class TimeDeltaPrinter(object): + def __init__(self, val): + self._timedelta = datetime.timedelta(microseconds=int(val['delta_'])) + def timedelta(self): + return self._timedelta + def to_string(self): + return str(self._timedelta) +pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter) +class TimeTicksPrinter(TimeDeltaPrinter): + def __init__(self, val): + self._timedelta = datetime.timedelta(microseconds=int(val['us_'])) +pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter) +class TimePrinter(object): + def __init__(self, val): + timet_offset = gdb.parse_and_eval('base::Time::kTimeTToMicrosecondsOffset') + self._datetime = ( + datetime.datetime.fromtimestamp(0) + + datetime.timedelta(microseconds=int(val['us_'] - timet_offset))) + def datetime(self): + return self._datetime + def to_string(self): + return str(self._datetime) +pp_set.add_printer('base::Time', '^base::Time$', TimePrinter) +class FlatTreePrinter(object): + def __init__(self, val): + self.val = val + def to_string(self): + # It would be nice to match the output of std::map which is a little + # nicer than printing the vector of pairs. But iterating over it in + # Python is much more complicated and this output is reasonable. + # (Without this printer, a flat_map will output 7 lines of internal + # template goop before the vector contents.) + return 'base::flat_tree with ' + str(self.val['impl_']['body_']) +pp_set.add_printer('base::flat_map', '^base::flat_map<.*>$', FlatTreePrinter) +pp_set.add_printer('base::flat_set', '^base::flat_set<.*>$', FlatTreePrinter) +pp_set.add_printer('base::flat_tree', '^base::internal::flat_tree<.*>$', + FlatTreePrinter) +class ValuePrinter(object): + def __init__(self, val): + self.val = val + def get_type(self): + return self.val['type_'] + def to_string(self): + typestr = str(self.get_type()) + # Trim prefix to just get the emum short name. + typestr = typestr[typestr.rfind(':') + 1:] + if typestr == 'NONE': + return 'base::Value of type NONE' + if typestr == 'BOOLEAN': + valuestr = self.val['bool_value_'] + if typestr == 'INTEGER': + valuestr = self.val['int_value_'] + if typestr == 'DOUBLE': + valuestr = self.val['double_value_'] + if typestr == 'STRING': + valuestr = self.val['string_value_'] + if typestr == 'BINARY': + valuestr = self.val['binary_value_'] + if typestr == 'DICTIONARY': + valuestr = self.val['dict_'] + if typestr == 'LIST': + valuestr = self.val['list_'] + return "base::Value of type %s = %s" % (typestr, str(valuestr)) +pp_set.add_printer('base::Value', '^base::Value$', ValuePrinter) +pp_set.add_printer('base::ListValue', '^base::ListValue$', ValuePrinter) +pp_set.add_printer('base::DictionaryValue', '^base::DictionaryValue$', + ValuePrinter) +class IpcMessagePrinter(Printer): + def header(self): + return self.val['header_'].cast( + gdb.lookup_type('IPC::Message::Header').pointer()) + def to_string(self): + message_type = self.header()['type'] + return '%s of kind %s line %s' % (self.val.dynamic_type, + (message_type >> 16).cast( + gdb.lookup_type('IPCMessageStart')), + message_type & 0xffff) + def children(self): + yield ('header_', self.header().dereference()) + yield ('capacity_after_header_', self.val['capacity_after_header_']) + for field in self.val.type.fields(): + if field.is_base_class: + continue + yield (field.name, self.val[field.name]) +pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter) +class NotificationRegistrarPrinter(Printer): + def to_string(self): + try: + registrations = self.val['registered_'] + vector_finish = registrations['_M_impl']['_M_finish'] + vector_start = registrations['_M_impl']['_M_start'] + if vector_start == vector_finish: + return 'Not watching notifications' + if vector_start.dereference().type.sizeof == 0: + # Incomplete type: b/8242773 + return 'Watching some notifications' + return ('Watching %s notifications; ' + 'print %s->registered_ for details') % (int( + vector_finish - vector_start), typed_ptr(self.val.address)) + except gdb.error: + return 'NotificationRegistrar' +pp_set.add_printer('content::NotificationRegistrar', + '^content::NotificationRegistrar$', + NotificationRegistrarPrinter) +class SiteInstanceImplPrinter(object): + def __init__(self, val): + self.val = val.cast(val.dynamic_type) + def to_string(self): + return 'SiteInstanceImpl@%s for %s' % (self.val.address, self.val['site_']) + def children(self): + yield ('id_', self.val['id_']) + yield ('has_site_', self.val['has_site_']) + if self.val['browsing_instance_']['ptr_']: + yield ('browsing_instance_', self.val['browsing_instance_']['ptr_']) + if self.val['process_']: + yield ('process_', typed_ptr(self.val['process_'])) +pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$', + SiteInstanceImplPrinter) +class RenderProcessHostImplPrinter(object): + def __init__(self, val): + self.val = val.cast(val.dynamic_type) + def to_string(self): + pid = '' + try: + child_process_launcher_ptr = ( + self.val['child_process_launcher_']['impl_']['data_']['ptr']) + if child_process_launcher_ptr: + context = (child_process_launcher_ptr['context_']['ptr_']) + if context: + pid = ' PID %s' % str(context['process_']['process_']) + except gdb.error: + # The definition of the Context type may not be available. + # b/8242773 + pass + return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid) + def children(self): + yield ('id_', self.val['id_']) + yield ('listeners_', self.val['listeners_']['data_']) + yield ('worker_ref_count_', self.val['worker_ref_count_']) + yield ('fast_shutdown_started_', self.val['fast_shutdown_started_']) + yield ('deleting_soon_', self.val['deleting_soon_']) + yield ('pending_views_', self.val['pending_views_']) + yield ('visible_widgets_', self.val['visible_widgets_']) + yield ('backgrounded_', self.val['backgrounded_']) + yield ('widget_helper_', self.val['widget_helper_']) + yield ('is_initialized_', self.val['is_initialized_']) + yield ('browser_context_', typed_ptr(self.val['browser_context_'])) + yield ('sudden_termination_allowed_', + self.val['sudden_termination_allowed_']) + yield ('ignore_input_events_', self.val['ignore_input_events_']) + yield ('is_guest_', self.val['is_guest_']) +pp_set.add_printer('content::RenderProcessHostImpl', + '^content::RenderProcessHostImpl$', + RenderProcessHostImplPrinter) +gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING) +"""Implementations of inlined libc++ std container functions.""" +def gdb_running_under_rr(): + try: + # rr defines the when command to return the current event number. + gdb.execute('when') + # If there was no error executing the command, we are running under rr. + return True + except gdb.error: + return False +def find_nearest_frame_matching(frame, predicate): + while frame and not predicate(frame): + frame = frame.older() + return frame +class ReverseCallback(gdb.Command): + """Find when the currently running callback was created.""" + def __init__(self): + super(ReverseCallback, self).__init__("reverse-callback", gdb.COMMAND_USER) + def invoke(self, arg, from_tty): + if not gdb_running_under_rr(): + raise gdb.error('reverse-callback requires debugging under rr: ' + + 'https://rr-project.org/') + # Find the stack frame which extracts the bind state from the task. + bind_state_frame = find_nearest_frame_matching( + gdb.selected_frame(), + lambda frame : frame.function() and + re.match('^base::internal::Invoker' + + '::RunOnce\(base::internal::BindStateBase\*\)$', + frame.function().name)) + if bind_state_frame is None: + raise Exception( + 'base::internal::Invoker frame not found; are you in a callback?') + bind_state_frame.select() + # Disable all existing breakpoints. + was_enabled = [] + for breakpoint in gdb.breakpoints(): + was_enabled.append(breakpoint.enabled) + breakpoint.enabled = False + # Break on the initialization of the BindState. + storage_address = gdb.parse_and_eval('storage') + watchpoint = gdb.Breakpoint('*' + str(storage_address), gdb.BP_WATCHPOINT) + # Find the construction. + gdb.execute('reverse-continue') + # Restore breakpoints + watchpoint.delete() + for breakpoint, enabled in zip(gdb.breakpoints(), was_enabled): + breakpoint.enabled = enabled + # Find the stack frame which created the BindState. + def in_bindstate(frame): + return frame.function() and frame.function().name.startswith( + 'base::internal::BindState<') + creation_frame = find_nearest_frame_matching( + find_nearest_frame_matching(gdb.selected_frame(), in_bindstate), + lambda frame: not in_bindstate(frame)) + # The callback creates the bindstate, step up once more to get the creator + # of the callback. + creation_frame.older().select() +ReverseCallback() +@class_methods.Class('std::__1::vector', template_types=['T']) +class LibcppVector(object): + @class_methods.member_function('T&', 'operator[]', ['int']) + def element(obj, i): + return obj['__begin_'][i] + @class_methods.member_function('size_t', 'size', []) + def size(obj): + return obj['__end_'] - obj['__begin_'] +@class_methods.Class('std::__1::unique_ptr', template_types=['T']) +class LibcppUniquePtr(object): + @class_methods.member_function('T*', 'get', []) + def get(obj): + return obj['__ptr_']['__value_'] + @class_methods.member_function('T*', 'operator->', []) + def arrow(obj): + return obj['__ptr_']['__value_'] + @class_methods.member_function('T&', 'operator*', []) + def dereference(obj): + return obj['__ptr_']['__value_'].dereference() diff --git a/test_wasm/chrome_gdb/gdbinit b/test_wasm/chrome_gdb/gdbinit new file mode 100644 index 00000000000..8729285721d --- /dev/null +++ b/test_wasm/chrome_gdb/gdbinit @@ -0,0 +1,80 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +python +import os +import subprocess +import sys +compile_dirs = set() +def get_current_debug_file_directories(): + dir = gdb.execute("show debug-file-directory", to_string=True) + dir = dir[ + len('The directory where separate debug symbols are searched for is "' + ):-len('".') - 1] + return set(dir.split(":")) +def add_debug_file_directory(dir): + # gdb has no function to add debug-file-directory, simulates that by using + # `show debug-file-directory` and `set debug-file-directory `. + current_dirs = get_current_debug_file_directories() + current_dirs.add(dir) + gdb.execute( + "set debug-file-directory %s" % ":".join(current_dirs), to_string=True) +def load_libcxx_pretty_printers(src_dir): + libcxx_pretty_printers = os.path.join(src_dir, 'third_party', + 'libcxx-pretty-printers') + if not os.path.isdir(libcxx_pretty_printers): + return + sys.path.insert(1, libcxx_pretty_printers) + from printers import register_libcxx_printers + register_libcxx_printers(None) +def load_gdb_chrome(src_dir): + tools_gdb = os.path.join(src_dir, 'tools', 'gdb') + sys.path.insert(1, tools_gdb) + import gdb_chrome + gdb.execute('source %s' % os.path.join(tools_gdb, 'viewg.gdb')) +def newobj_handler(event): + global compile_dirs + compile_dir = os.path.dirname(event.new_objfile.filename) + if not compile_dir: + return + if compile_dir in compile_dirs: + return + compile_dirs.add(compile_dir) + # Add source path + gdb.execute("dir %s" % compile_dir) + # Need to tell the location of .dwo files. + # https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html + # https://crbug.com/603286#c35 + add_debug_file_directory(compile_dir) + git = subprocess.Popen( + ['git', '-C', compile_dir, 'rev-parse', '--show-toplevel'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + src_dir, _ = git.communicate() + if git.returncode: + return + if isinstance(src_dir, str): + src_dir = src_dir.rstrip() + else: + src_dir = src_dir.decode('utf-8').rstrip() + load_libcxx_pretty_printers(src_dir) + load_gdb_chrome(src_dir) +lsb_release = subprocess.Popen(['lsb_release', '-sc'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) +codename = lsb_release.communicate()[0] +if codename.rstrip() == b'rodete' and 'gg' not in gdb.VERSION: + sys.stderr.write( + '\033[93mDetected gLinux without using google-gdb. google-gdb is ' + 'recommended because it is more up-to-date than the system gdb, which ' + 'has known issues (https://crbug.com/957374). To switch to google-gdb, ' + 'run "sudo apt install google-gdb" which will *uninstall* gdb and ' + '*install* google-gdb. This is a drop-in replacement with the same ' + 'command line usage, so you can still run eg. ' + '"gdb out/Debug/chrome".\033[0m\n' + ) +# Event hook for newly loaded objfiles. +# https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html +gdb.events.new_objfile.connect(newobj_handler) +gdb.execute("set environment CHROMIUM_GDBINIT_SOURCED=1") +end \ No newline at end of file diff --git a/test_wasm/chrome_gdb/util/class_methods.py b/test_wasm/chrome_gdb/util/class_methods.py new file mode 100644 index 00000000000..1ffa5b19f9b --- /dev/null +++ b/test_wasm/chrome_gdb/util/class_methods.py @@ -0,0 +1,148 @@ + +# Copyright (c) 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""Helper library for defining XMethod implementations on C++ classes. +Include this library and then define python implementations of C++ methods +using the Class and member_function decorator functions. +""" +import gdb +import gdb.xmethod +import operator +import re +class MemberFunction(object): + def __init__(self, return_type, name, arguments, wrapped_function): + self.return_type = return_type + self.name = name + self.arguments = arguments + self.function_ = wrapped_function + def __call__(self, *args): + self.function_(*args) +def member_function(return_type, name, arguments): + """Decorate a member function. + See Class decorator for example usage within a class. + Args: + return_type: The return type of the function (e.g. 'int') + name: The function name (e.g. 'sum') + arguments: The argument types for this function (e.g. ['int', 'int']) + Each type can be a string (e.g. 'int', 'std::string', 'T*') or a + function which constructs the return type. See CreateTypeResolver + for details about type resolution. + """ + def DefineMember(fn): + return MemberFunction(return_type, name, arguments, fn) + return DefineMember +def Class(class_name, template_types): + """Decorate a python class with its corresponding C++ type. + Args: + class_name: The canonical string identifier for the class (e.g. base::Foo) + template_types: An array of names for each templated type (e.g. ['K', + 'V']) + Example: + As an example, the following is an implementation of size() and operator[] + on std::__1::vector, functions which are normally inlined and not + normally callable from gdb. + @class_methods.Class('std::__1::vector', template_types=['T']) + class LibcppVector(object): + @class_methods.member_function('T&', 'operator[]', ['int']) + def element(obj, i): + return obj['__begin_'][i] + @class_methods.member_function('size_t', 'size', []) + def size(obj): + return obj['__end_'] - obj['__begin_'] + Note: + Note that functions are looked up by the function name, which means that + functions cannot currently have overloaded implementations for different + arguments. + """ + class MethodWorkerWrapper(gdb.xmethod.XMethod): + """Wrapper of an XMethodWorker class as an XMethod.""" + def __init__(self, name, worker_class): + super(MethodWorkerWrapper, self).__init__(name) + self.name = name + self.worker_class = worker_class + class ClassMatcher(gdb.xmethod.XMethodMatcher): + """Matches member functions of one class template.""" + def __init__(self, obj): + super(ClassMatcher, self).__init__(class_name) + # Constructs a regular expression to match this type. + self._class_regex = re.compile( + '^' + re.escape(class_name) + + ('<.*>' if len(template_types) > 0 else '') + '$') + # Construct a dictionary and array of methods + self.dict = {} + self.methods = [] + for name in dir(obj): + attr = getattr(obj, name) + if not isinstance(attr, MemberFunction): + continue + name = attr.name + return_type = CreateTypeResolver(attr.return_type) + arguments = [CreateTypeResolver(arg) for arg in + attr.arguments] + method = MethodWorkerWrapper( + attr.name, + CreateTemplatedMethodWorker(return_type, + arguments, attr.function_)) + self.methods.append(method) + def match(self, class_type, method_name): + if not re.match(self._class_regex, class_type.tag): + return None + templates = [class_type.template_argument(i) for i in + range(len(template_types))] + return [method.worker_class(templates) for method in self.methods + if method.name == method_name and method.enabled] + def CreateTypeResolver(type_desc): + """Creates a callback which resolves to the appropriate type when + invoked. + This is a helper to allow specifying simple types as strings when + writing function descriptions. For complex cases, a callback can be + passed which will be invoked when template instantiation is known. + Args: + type_desc: A callback generating the type or a string description of + the type to lookup. Supported types are classes in the + template_classes array (e.g. T) which will be looked up when those + templated classes are known, or globally visible type names (e.g. + int, base::Foo). + Types can be modified by appending a '*' or '&' to denote a + pointer or reference. + If a callback is used, the callback will be passed an array of the + instantiated template types. + Note: + This does not parse complex types such as std::vector::iterator, + to refer to types like these you must currently write a callback + which constructs the appropriate type. + """ + if callable(type_desc): + return type_desc + if type_desc == 'void': + return lambda T: None + if type_desc[-1] == '&': + inner_resolver = CreateTypeResolver(type_desc[:-1]) + return lambda template_types: inner_resolver(template_types).reference() + if type_desc[-1] == '*': + inner_resolver = CreateTypeResolver(type_desc[:-1]) + return lambda template_types: inner_resolver(template_types).pointer() + try: + template_index = template_types.index(type_desc) + return operator.itemgetter(template_index) + except ValueError: + return lambda template_types: gdb.lookup_type(type_desc) + def CreateTemplatedMethodWorker(return_callback, args_callbacks, + method_callback): + class TemplatedMethodWorker(gdb.xmethod.XMethodWorker): + def __init__(self, templates): + super(TemplatedMethodWorker, self).__init__() + self._templates = templates + def get_arg_types(self): + return [cb(self._templates) for cb in args_callbacks] + def get_result_type(self, obj): + return return_callback(self._templates) + def __call__(self, *args): + return method_callback(*args) + return TemplatedMethodWorker + def DefineClass(obj): + matcher = ClassMatcher(obj) + gdb.xmethod.register_xmethod_matcher(None, matcher) + return matcher + return DefineClass diff --git a/test_wasm/chrome_gdb/viewg.gdb b/test_wasm/chrome_gdb/viewg.gdb new file mode 100644 index 00000000000..924abba0747 --- /dev/null +++ b/test_wasm/chrome_gdb/viewg.gdb @@ -0,0 +1,32 @@ + +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# Creates a SVG graph of the view hierarchy, when stopped in a +# method of an object that inherits from views::View. Requires +# graphviz. +# +# For more info see +# chromium/src/+/HEAD/docs/graphical_debugging_aid_chromium_views.md +# +# To make this command available, add the following to your ~/.gdbinit: +# source {Path to SRC Root}/tools/gdbviewg.gdb +# +# Usage: type `viewg` at the GDB prompt, given the conditions above. +define viewg + if $argc != 0 + echo Usage: viewg + else + set pagination off + set print elements 0 + set logging off + set logging file ~/state.dot + set logging overwrite on + set logging redirect on + set logging on + printf "%s\n", views::PrintViewGraph(this).c_str() + set logging off + shell dot -Tsvg -o ~/state.svg ~/state.dot + set pagination on + end +end \ No newline at end of file diff --git a/test_wasm/import.txt b/test_wasm/import.txt new file mode 100644 index 00000000000..242c034c120 --- /dev/null +++ b/test_wasm/import.txt @@ -0,0 +1 @@ +hello_world diff --git a/test_wasm/output.txt b/test_wasm/output.txt new file mode 100644 index 00000000000..1fe17ecc3b8 --- /dev/null +++ b/test_wasm/output.txt @@ -0,0 +1,15 @@ +[WASM-PL] Get isolate +[WASM-PL] Create function type +[WASM-PL] Create function +[WASM-PL] Preload function +[WASM-PL] Get isolate +[WASM-PL] Select imports +[WASM-PL] Create object +[WASM-PL] Create callback +[WASM-PL] Create PodArray +[WASM-PL] Sig size: 2 +[WASM-PL] Copy v8 types +[WASM-PL] Serialized +[WASM-PL] Update native imports +[WASM-PL] Assertion success: imports_handle set. +[WASM-PL] Done diff --git a/test_wasm/test.c b/test_wasm/test.c new file mode 100644 index 00000000000..f7c8a518a09 --- /dev/null +++ b/test_wasm/test.c @@ -0,0 +1,3 @@ +int main(int argc, char **argv) { + return hello_world(1, 2); +} diff --git a/test_wasm/test.js b/test_wasm/test.js new file mode 100644 index 00000000000..a7f4fe7387d --- /dev/null +++ b/test_wasm/test.js @@ -0,0 +1,6 @@ +const fs = require('fs') +const buf = fs.readFileSync('./test.wasm') +const mod = new WebAssembly.Module(buf) +const imports = WebAssembly.preloads() +const inst = new WebAssembly.Instance(mod, imports) +console.log('Result: ' + inst.exports.main()) diff --git a/test_wasm/test.o b/test_wasm/test.o new file mode 100644 index 0000000000000000000000000000000000000000..787c05dc599d0d42ba9d6e835846761b7943d42b GIT binary patch literal 434 zcmXAlJx{|h5QfiA+9YkIc10|XV1S`%2&JWL6(1`zVlcL2+O&>SC23KoR4k0lEc`k6 z6KpuGWZirB-aWR?z={+B0Q%VNc7R@RRiO(1)P-$Hz3Eb_5-nLyQ<-M@76a7w5v7Ta zlU$0DzOJ<>lT6c+aV62}Fk!%=WMWBIS)xmsTNSFo_FO8J(YGvDu^D{dzY4A2y>jGL zCnnp0kEcD~BA8f9-zN6y-kc)8F~xp!3cf>Z;*4;|2#m061dXsw8l>4f@UG!KV|xp~ zMOp`*xZO5<*Vj8{r)8pN*!`?a3v6VkEYY|JV9r-YRCKa+MWZdUl)DVj4+0!HlgOHrf%W_rR3$Hm{_J?l^uP-*g=GK{ zp}>_>LgEvuaq6O9=f%Ee2s+qf-|c0L6+UYDq&F%t3@~DU^B-ycVr9EQnb9= cpq9%Gw5%nQ{6MzczG_R=x2>gaNXor=0sa>zpa1{> literal 0 HcmV?d00001 diff --git a/test_wasm/test.wat b/test_wasm/test.wat new file mode 100644 index 00000000000..24f53a315d9 --- /dev/null +++ b/test_wasm/test.wat @@ -0,0 +1,53 @@ +(module + (type (;0;) (func (param i32 i32) (result i32))) + (import "node_test" "hello_world" (func $hello_world (type 0))) + (func $main (type 0) (param i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32 i32) + global.get 0 + local.set 2 + i32.const 16 + local.set 3 + local.get 2 + local.get 3 + i32.sub + local.set 4 + local.get 4 + global.set 0 + i32.const 1 + local.set 5 + i32.const 2 + local.set 6 + i32.const 0 + local.set 7 + local.get 4 + local.get 7 + i32.store offset=12 + local.get 4 + local.get 0 + i32.store offset=8 + local.get 4 + local.get 1 + i32.store offset=4 + local.get 5 + local.get 6 + call $hello_world + local.set 8 + i32.const 16 + local.set 9 + local.get 4 + local.get 9 + i32.add + local.set 10 + local.get 10 + global.set 0 + local.get 8 + return) + (table (;0;) 1 1 funcref) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 66560)) + (global (;1;) i32 (i32.const 66560)) + (global (;2;) i32 (i32.const 1024)) + (export "memory" (memory 0)) + (export "__heap_base" (global 1)) + (export "__data_end" (global 2)) + (export "main" (func $main)))