Commit 81548524 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Share code of js-to-wasm wrappers

Instead of creating a separate code object per exported function, we
can share the code per signature, and load the function index from the
{WasmExportedFunction} object which is being passed as an argument
anyway.
This greatly reduces instantiation time for modules with a lot of
exports.
As a next step, we could even share the code across instances, or (with
more work) across isolates.

R=mstarzinger@chromium.org

Bug: chromium:860491
Change-Id: I6438065b2de0df59dce980fb52408a0f475144b3
Reviewed-on: https://chromium-review.googlesource.com/1127660
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54299}
parent fa067fb9
This diff is collapsed.
...@@ -114,10 +114,11 @@ MaybeHandle<Code> CompileWasmToJSWrapper(Isolate*, Handle<JSReceiver> target, ...@@ -114,10 +114,11 @@ MaybeHandle<Code> CompileWasmToJSWrapper(Isolate*, Handle<JSReceiver> target,
wasm::ModuleOrigin, wasm::ModuleOrigin,
wasm::UseTrapHandler); wasm::UseTrapHandler);
// Wraps a given wasm code object, producing a code object. // Creates a code object calling a wasm function with the given signature,
// callable from JS.
V8_EXPORT_PRIVATE MaybeHandle<Code> CompileJSToWasmWrapper( V8_EXPORT_PRIVATE MaybeHandle<Code> CompileJSToWasmWrapper(
Isolate*, const wasm::WasmModule*, Address call_target, uint32_t index, Isolate*, const wasm::NativeModule*, wasm::FunctionSig*, bool is_import,
wasm::UseTrapHandler); wasm::UseTrapHandler use_trap_handler);
// Compiles a stub that redirects a call to a wasm function to the wasm // Compiles a stub that redirects a call to a wasm function to the wasm
// interpreter. It's ABI compatible with the compiled wasm function. // interpreter. It's ABI compatible with the compiled wasm function.
...@@ -393,6 +394,8 @@ class WasmGraphBuilder { ...@@ -393,6 +394,8 @@ class WasmGraphBuilder {
UseRetpoline use_retpoline); UseRetpoline use_retpoline);
Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets, Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, int func_index); wasm::WasmCodePosition position, int func_index);
Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, Node* func_index);
Node* BuildF32CopySign(Node* left, Node* right); Node* BuildF32CopySign(Node* left, Node* right);
Node* BuildF64CopySign(Node* left, Node* right); Node* BuildF64CopySign(Node* left, Node* right);
......
...@@ -171,46 +171,35 @@ namespace { ...@@ -171,46 +171,35 @@ namespace {
class JSToWasmWrapperCache { class JSToWasmWrapperCache {
public: public:
Handle<Code> CloneOrCompileJSToWasmWrapper( Handle<Code> GetOrCompileJSToWasmWrapper(
Isolate* isolate, const wasm::WasmModule* module, Address call_target, Isolate* isolate, const wasm::NativeModule* native_module,
uint32_t index, wasm::UseTrapHandler use_trap_handler) { uint32_t func_index, wasm::UseTrapHandler use_trap_handler) {
const bool is_import = index < module->num_imported_functions; const wasm::WasmModule* module = native_module->module();
DCHECK_EQ(is_import, call_target == kNullAddress); const wasm::WasmFunction* func = &module->functions[func_index];
const wasm::WasmFunction* func = &module->functions[index]; bool is_import = func_index < module->num_imported_functions;
// We cannot cache js-to-wasm wrappers for imports, as they hard-code the auto& cache = cache_[is_import ? 1 : 0];
// function index. int cached_idx = cache.sig_map.Find(func->sig);
if (!is_import) { if (cached_idx >= 0) return cache.code_cache[cached_idx];
int cached_idx = sig_map_.Find(func->sig);
if (cached_idx >= 0) {
Handle<Code> code =
isolate->factory()->CopyCode(code_cache_[cached_idx]);
// Now patch the call to wasm code.
RelocIterator it(*code,
RelocInfo::ModeMask(RelocInfo::JS_TO_WASM_CALL));
// If there is no reloc info, then it's an incompatible signature or
// calls an import.
if (!it.done()) it.rinfo()->set_js_to_wasm_address(call_target);
return code;
}
}
Handle<Code> code = Handle<Code> code =
compiler::CompileJSToWasmWrapper(isolate, module, call_target, index, compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig,
use_trap_handler) is_import, use_trap_handler)
.ToHandleChecked(); .ToHandleChecked();
if (!is_import) { uint32_t new_cache_idx = cache.sig_map.FindOrInsert(func->sig);
uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); DCHECK_EQ(cache.code_cache.size(), new_cache_idx);
DCHECK_EQ(code_cache_.size(), new_cache_idx); USE(new_cache_idx);
USE(new_cache_idx); cache.code_cache.push_back(code);
code_cache_.push_back(code);
}
return code; return code;
} }
private: private:
// sig_map_ maps signatures to an index in code_cache_. // We generate different code for calling imports than calling wasm functions
wasm::SignatureMap sig_map_; // in this module. Both are cached separately.
std::vector<Handle<Code>> code_cache_; // [0] for non-imports, [1] for imports.
struct Cache {
wasm::SignatureMap sig_map;
std::vector<Handle<Code>> code_cache;
} cache_[2];
}; };
// A helper class to simplify instantiating a module from a module object. // A helper class to simplify instantiating a module from a module object.
...@@ -1254,13 +1243,9 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -1254,13 +1243,9 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (module_->start_function_index >= 0) { if (module_->start_function_index >= 0) {
int start_index = module_->start_function_index; int start_index = module_->start_function_index;
Address start_call_address =
static_cast<uint32_t>(start_index) < module_->num_imported_functions
? kNullAddress
: native_module->GetCallTargetForFunction(start_index);
FunctionSig* sig = module_->functions[start_index].sig; FunctionSig* sig = module_->functions[start_index].sig;
Handle<Code> wrapper_code = js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
isolate_, module_, start_call_address, start_index, use_trap_handler()); isolate_, native_module, start_index, use_trap_handler());
// TODO(clemensh): Don't generate an exported function for the start // TODO(clemensh): Don't generate an exported function for the start
// function. Use CWasmEntry instead. // function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New( start_function_ = WasmExportedFunction::New(
...@@ -2135,9 +2120,8 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) { ...@@ -2135,9 +2120,8 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
// at module compile time and cached instead. // at module compile time and cached instead.
Handle<Code> wrapper_code = Handle<Code> wrapper_code =
js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
isolate_, module_, is_import ? kNullAddress : call_target, isolate_, native_module, func_index, use_trap_handler());
func_index, use_trap_handler());
MaybeHandle<String> func_name; MaybeHandle<String> func_name;
if (module_->origin == kAsmJsOrigin) { if (module_->origin == kAsmJsOrigin) {
// For modules arising from asm.js, honor the names section. // For modules arising from asm.js, honor the names section.
...@@ -3050,15 +3034,11 @@ void CompileJsToWasmWrappers(Isolate* isolate, ...@@ -3050,15 +3034,11 @@ void CompileJsToWasmWrappers(Isolate* isolate,
NativeModule* native_module = module_object->native_module(); NativeModule* native_module = module_object->native_module();
wasm::UseTrapHandler use_trap_handler = wasm::UseTrapHandler use_trap_handler =
native_module->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler; native_module->use_trap_handler() ? kUseTrapHandler : kNoTrapHandler;
const WasmModule* module = module_object->module(); const WasmModule* module = native_module->module();
for (auto exp : module->export_table) { for (auto exp : module->export_table) {
if (exp.kind != kExternalFunction) continue; if (exp.kind != kExternalFunction) continue;
Address call_target = Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
exp.index < module->num_imported_functions isolate, native_module, exp.index, use_trap_handler);
? kNullAddress
: native_module->GetCallTargetForFunction(exp.index);
Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(
isolate, module, call_target, exp.index, use_trap_handler);
export_wrappers->set(wrapper_index, *wrapper_code); export_wrappers->set(wrapper_index, *wrapper_code);
RecordStats(*wrapper_code, isolate->counters()); RecordStats(*wrapper_code, isolate->counters());
++wrapper_index; ++wrapper_index;
......
...@@ -279,6 +279,10 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -279,6 +279,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
return code; return code;
} }
Address jump_table_start() const {
return jump_table_ ? jump_table_->instruction_start() : kNullAddress;
}
bool is_jump_table_slot(Address address) const { bool is_jump_table_slot(Address address) const {
return jump_table_->contains(address); return jump_table_->contains(address);
} }
......
...@@ -501,10 +501,10 @@ class WasmExportedFunctionData : public Struct { ...@@ -501,10 +501,10 @@ class WasmExportedFunctionData : public Struct {
DECL_VERIFIER(WasmExportedFunctionData) DECL_VERIFIER(WasmExportedFunctionData)
// Layout description. // Layout description.
#define WASM_EXPORTED_FUNCTION_DATA_FIELDS(V) \ #define WASM_EXPORTED_FUNCTION_DATA_FIELDS(V) \
V(kWrapperCodeOffset, kPointerSize) \ V(kWrapperCodeOffset, kPointerSize) \
V(kInstanceOffset, kPointerSize) \ V(kInstanceOffset, kPointerSize) \
V(kFunctionIndexOffset, kPointerSize) \ V(kFunctionIndexOffset, kPointerSize) /* Smi */ \
V(kSize, 0) V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
......
...@@ -122,15 +122,14 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name, ...@@ -122,15 +122,14 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name,
Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) { Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
// Wrap the code so it can be called as a JS function. // Wrap the code so it can be called as a JS function.
Link(); Link();
Address target = native_module_->GetCallTargetForFunction(index); FunctionSig* sig = test_module_->functions[index].sig;
MaybeHandle<Code> maybe_ret_code = compiler::CompileJSToWasmWrapper( MaybeHandle<Code> maybe_ret_code = compiler::CompileJSToWasmWrapper(
isolate_, test_module_ptr_, target, index, isolate_, native_module_, sig, false,
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler); trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler);
Handle<Code> ret_code = maybe_ret_code.ToHandleChecked(); Handle<Code> ret_code = maybe_ret_code.ToHandleChecked();
Handle<JSFunction> ret = WasmExportedFunction::New( Handle<JSFunction> ret = WasmExportedFunction::New(
isolate_, instance_object(), MaybeHandle<String>(), isolate_, instance_object(), MaybeHandle<String>(),
static_cast<int>(index), static_cast<int>(index), static_cast<int>(sig->parameter_count()),
static_cast<int>(test_module_->functions[index].sig->parameter_count()),
ret_code); ret_code);
// Add reference to the exported wrapper code. // Add reference to the exported wrapper code.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment