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,
wasm::ModuleOrigin,
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(
Isolate*, const wasm::WasmModule*, Address call_target, uint32_t index,
wasm::UseTrapHandler);
Isolate*, const wasm::NativeModule*, wasm::FunctionSig*, bool is_import,
wasm::UseTrapHandler use_trap_handler);
// Compiles a stub that redirects a call to a wasm function to the wasm
// interpreter. It's ABI compatible with the compiled wasm function.
......@@ -393,6 +394,8 @@ class WasmGraphBuilder {
UseRetpoline use_retpoline);
Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
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* BuildF64CopySign(Node* left, Node* right);
......
......@@ -171,46 +171,35 @@ namespace {
class JSToWasmWrapperCache {
public:
Handle<Code> CloneOrCompileJSToWasmWrapper(
Isolate* isolate, const wasm::WasmModule* module, Address call_target,
uint32_t index, wasm::UseTrapHandler use_trap_handler) {
const bool is_import = index < module->num_imported_functions;
DCHECK_EQ(is_import, call_target == kNullAddress);
const wasm::WasmFunction* func = &module->functions[index];
// We cannot cache js-to-wasm wrappers for imports, as they hard-code the
// function index.
if (!is_import) {
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> GetOrCompileJSToWasmWrapper(
Isolate* isolate, const wasm::NativeModule* native_module,
uint32_t func_index, wasm::UseTrapHandler use_trap_handler) {
const wasm::WasmModule* module = native_module->module();
const wasm::WasmFunction* func = &module->functions[func_index];
bool is_import = func_index < module->num_imported_functions;
auto& cache = cache_[is_import ? 1 : 0];
int cached_idx = cache.sig_map.Find(func->sig);
if (cached_idx >= 0) return cache.code_cache[cached_idx];
Handle<Code> code =
compiler::CompileJSToWasmWrapper(isolate, module, call_target, index,
use_trap_handler)
compiler::CompileJSToWasmWrapper(isolate, native_module, func->sig,
is_import, use_trap_handler)
.ToHandleChecked();
if (!is_import) {
uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig);
DCHECK_EQ(code_cache_.size(), new_cache_idx);
USE(new_cache_idx);
code_cache_.push_back(code);
}
uint32_t new_cache_idx = cache.sig_map.FindOrInsert(func->sig);
DCHECK_EQ(cache.code_cache.size(), new_cache_idx);
USE(new_cache_idx);
cache.code_cache.push_back(code);
return code;
}
private:
// sig_map_ maps signatures to an index in code_cache_.
wasm::SignatureMap sig_map_;
std::vector<Handle<Code>> code_cache_;
// We generate different code for calling imports than calling wasm functions
// in this module. Both are cached separately.
// [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.
......@@ -1254,13 +1243,9 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
if (module_->start_function_index >= 0) {
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;
Handle<Code> wrapper_code = js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper(
isolate_, module_, start_call_address, start_index, use_trap_handler());
Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
isolate_, native_module, start_index, use_trap_handler());
// TODO(clemensh): Don't generate an exported function for the start
// function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New(
......@@ -2135,9 +2120,8 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
// at module compile time and cached instead.
Handle<Code> wrapper_code =
js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper(
isolate_, module_, is_import ? kNullAddress : call_target,
func_index, use_trap_handler());
js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
isolate_, native_module, func_index, use_trap_handler());
MaybeHandle<String> func_name;
if (module_->origin == kAsmJsOrigin) {
// For modules arising from asm.js, honor the names section.
......@@ -3050,15 +3034,11 @@ void CompileJsToWasmWrappers(Isolate* isolate,
NativeModule* native_module = module_object->native_module();
wasm::UseTrapHandler use_trap_handler =
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) {
if (exp.kind != kExternalFunction) continue;
Address call_target =
exp.index < module->num_imported_functions
? kNullAddress
: native_module->GetCallTargetForFunction(exp.index);
Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(
isolate, module, call_target, exp.index, use_trap_handler);
Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
isolate, native_module, exp.index, use_trap_handler);
export_wrappers->set(wrapper_index, *wrapper_code);
RecordStats(*wrapper_code, isolate->counters());
++wrapper_index;
......
......@@ -279,6 +279,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
return code;
}
Address jump_table_start() const {
return jump_table_ ? jump_table_->instruction_start() : kNullAddress;
}
bool is_jump_table_slot(Address address) const {
return jump_table_->contains(address);
}
......
......@@ -501,10 +501,10 @@ class WasmExportedFunctionData : public Struct {
DECL_VERIFIER(WasmExportedFunctionData)
// Layout description.
#define WASM_EXPORTED_FUNCTION_DATA_FIELDS(V) \
V(kWrapperCodeOffset, kPointerSize) \
V(kInstanceOffset, kPointerSize) \
V(kFunctionIndexOffset, kPointerSize) \
#define WASM_EXPORTED_FUNCTION_DATA_FIELDS(V) \
V(kWrapperCodeOffset, kPointerSize) \
V(kInstanceOffset, kPointerSize) \
V(kFunctionIndexOffset, kPointerSize) /* Smi */ \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
......
......@@ -122,15 +122,14 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name,
Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
// Wrap the code so it can be called as a JS function.
Link();
Address target = native_module_->GetCallTargetForFunction(index);
FunctionSig* sig = test_module_->functions[index].sig;
MaybeHandle<Code> maybe_ret_code = compiler::CompileJSToWasmWrapper(
isolate_, test_module_ptr_, target, index,
isolate_, native_module_, sig, false,
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler);
Handle<Code> ret_code = maybe_ret_code.ToHandleChecked();
Handle<JSFunction> ret = WasmExportedFunction::New(
isolate_, instance_object(), MaybeHandle<String>(),
static_cast<int>(index),
static_cast<int>(test_module_->functions[index].sig->parameter_count()),
static_cast<int>(index), static_cast<int>(sig->parameter_count()),
ret_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