Commit 79e8e3ec authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Commit Bot

[wasm] Parallelize JS to WASM wrapper compilation

R=ahaas@chromium.org
CC=titzer@chromium.org

Bug: v8:9231
Change-Id: I209f7c89c99408a53a8db6a6af1ed795f6668a1d
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1655653
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62249}
parent b5fe1b4b
......@@ -2862,7 +2862,6 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/function-compiler.h",
"src/wasm/graph-builder-interface.cc",
"src/wasm/graph-builder-interface.h",
"src/wasm/js-to-wasm-wrapper-cache.h",
"src/wasm/jump-table-assembler.cc",
"src/wasm/jump-table-assembler.h",
"src/wasm/leb-helper.h",
......
......@@ -1042,6 +1042,119 @@ void PipelineCompilationJob::RegisterWeakObjectsInOptimizedCode(
code->set_can_have_weak_objects(true);
}
class WasmHeapStubCompilationJob final : public OptimizedCompilationJob {
public:
WasmHeapStubCompilationJob(Isolate* isolate, CallDescriptor* call_descriptor,
std::unique_ptr<Zone> zone, Graph* graph,
Code::Kind kind,
std::unique_ptr<char[]> debug_name,
const AssemblerOptions& options,
SourcePositionTable* source_positions)
// Note that the OptimizedCompilationInfo is not initialized at the time
// we pass it to the CompilationJob constructor, but it is not
// dereferenced there.
: OptimizedCompilationJob(isolate->stack_guard()->real_climit(), &info_,
"TurboFan"),
debug_name_(std::move(debug_name)),
info_(CStrVector(debug_name_.get()), graph->zone(), kind),
call_descriptor_(call_descriptor),
zone_stats_(isolate->allocator()),
zone_(std::move(zone)),
graph_(graph),
data_(&zone_stats_, &info_, isolate, graph_, nullptr, source_positions,
new (zone_.get()) NodeOriginTable(graph_), nullptr, options),
pipeline_(&data_) {}
~WasmHeapStubCompilationJob() = default;
protected:
Status PrepareJobImpl(Isolate* isolate) final;
Status ExecuteJobImpl() final;
Status FinalizeJobImpl(Isolate* isolate) final;
private:
std::unique_ptr<char[]> debug_name_;
OptimizedCompilationInfo info_;
CallDescriptor* call_descriptor_;
ZoneStats zone_stats_;
std::unique_ptr<Zone> zone_;
Graph* graph_;
PipelineData data_;
PipelineImpl pipeline_;
DISALLOW_COPY_AND_ASSIGN(WasmHeapStubCompilationJob);
};
// static
std::unique_ptr<OptimizedCompilationJob>
Pipeline::NewWasmHeapStubCompilationJob(Isolate* isolate,
CallDescriptor* call_descriptor,
std::unique_ptr<Zone> zone,
Graph* graph, Code::Kind kind,
std::unique_ptr<char[]> debug_name,
const AssemblerOptions& options,
SourcePositionTable* source_positions) {
return base::make_unique<WasmHeapStubCompilationJob>(
isolate, call_descriptor, std::move(zone), graph, kind,
std::move(debug_name), options, source_positions);
}
CompilationJob::Status WasmHeapStubCompilationJob::PrepareJobImpl(
Isolate* isolate) {
std::unique_ptr<PipelineStatistics> pipeline_statistics;
if (FLAG_turbo_stats || FLAG_turbo_stats_nvp) {
pipeline_statistics.reset(new PipelineStatistics(
&info_, isolate->GetTurboStatistics(), &zone_stats_));
pipeline_statistics->BeginPhaseKind("V8.WasmStubCodegen");
}
if (info_.trace_turbo_json_enabled() || info_.trace_turbo_graph_enabled()) {
CodeTracer::Scope tracing_scope(data_.GetCodeTracer());
OFStream os(tracing_scope.file());
os << "---------------------------------------------------\n"
<< "Begin compiling method " << info_.GetDebugName().get()
<< " using TurboFan" << std::endl;
}
if (info_.trace_turbo_graph_enabled()) { // Simple textual RPO.
StdoutStream{} << "-- wasm stub " << Code::Kind2String(info_.code_kind())
<< " graph -- " << std::endl
<< AsRPO(*data_.graph());
}
if (info_.trace_turbo_json_enabled()) {
TurboJsonFile json_of(&info_, std::ios_base::trunc);
json_of << "{\"function\":\"" << info_.GetDebugName().get()
<< "\", \"source\":\"\",\n\"phases\":[";
}
pipeline_.RunPrintAndVerify("V8.WasmMachineCode", true);
return CompilationJob::SUCCEEDED;
}
CompilationJob::Status WasmHeapStubCompilationJob::ExecuteJobImpl() {
pipeline_.ComputeScheduledGraph();
if (pipeline_.SelectInstructionsAndAssemble(call_descriptor_)) {
return CompilationJob::SUCCEEDED;
}
return CompilationJob::FAILED;
}
CompilationJob::Status WasmHeapStubCompilationJob::FinalizeJobImpl(
Isolate* isolate) {
Handle<Code> code;
if (pipeline_.FinalizeCode(call_descriptor_).ToHandle(&code) &&
pipeline_.CommitDependencies(code)) {
info_.SetCode(code);
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code) {
CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
OFStream os(tracing_scope.file());
code->Disassemble(compilation_info()->GetDebugName().get(), os);
}
#endif
return SUCCEEDED;
}
return FAILED;
}
template <typename Phase, typename... Args>
void PipelineImpl::Run(Args&&... args) {
PipelineRunScope scope(this->data_, Phase::phase_name());
......@@ -2360,58 +2473,6 @@ wasm::WasmCompilationResult Pipeline::GenerateCodeForWasmNativeStub(
return result;
}
// static
MaybeHandle<Code> Pipeline::GenerateCodeForWasmHeapStub(
Isolate* isolate, CallDescriptor* call_descriptor, Graph* graph,
Code::Kind kind, const char* debug_name, const AssemblerOptions& options,
SourcePositionTable* source_positions) {
OptimizedCompilationInfo info(CStrVector(debug_name), graph->zone(), kind);
// Construct a pipeline for scheduling and code generation.
ZoneStats zone_stats(isolate->allocator());
NodeOriginTable* node_positions = new (graph->zone()) NodeOriginTable(graph);
PipelineData data(&zone_stats, &info, isolate, graph, nullptr,
source_positions, node_positions, nullptr, options);
std::unique_ptr<PipelineStatistics> pipeline_statistics;
if (FLAG_turbo_stats || FLAG_turbo_stats_nvp) {
pipeline_statistics.reset(new PipelineStatistics(
&info, isolate->GetTurboStatistics(), &zone_stats));
pipeline_statistics->BeginPhaseKind("V8.WasmStubCodegen");
}
PipelineImpl pipeline(&data);
if (info.trace_turbo_json_enabled() ||
info.trace_turbo_graph_enabled()) {
CodeTracer::Scope tracing_scope(data.GetCodeTracer());
OFStream os(tracing_scope.file());
os << "---------------------------------------------------\n"
<< "Begin compiling method " << info.GetDebugName().get()
<< " using TurboFan" << std::endl;
}
if (info.trace_turbo_graph_enabled()) { // Simple textual RPO.
StdoutStream{} << "-- wasm stub " << Code::Kind2String(kind) << " graph -- "
<< std::endl
<< AsRPO(*graph);
}
if (info.trace_turbo_json_enabled()) {
TurboJsonFile json_of(&info, std::ios_base::trunc);
json_of << "{\"function\":\"" << info.GetDebugName().get()
<< "\", \"source\":\"\",\n\"phases\":[";
}
pipeline.RunPrintAndVerify("V8.WasmMachineCode", true);
pipeline.ComputeScheduledGraph();
Handle<Code> code;
if (pipeline.GenerateCode(call_descriptor).ToHandle(&code) &&
pipeline.CommitDependencies(code)) {
return code;
}
return MaybeHandle<Code>();
}
// static
MaybeHandle<Code> Pipeline::GenerateCodeForTesting(
OptimizedCompilationInfo* info, Isolate* isolate,
......
......@@ -59,11 +59,11 @@ class Pipeline : public AllStatic {
const char* debug_name, const AssemblerOptions& assembler_options,
SourcePositionTable* source_positions = nullptr);
// Run the pipeline on a machine graph and generate code.
static MaybeHandle<Code> GenerateCodeForWasmHeapStub(
Isolate* isolate, CallDescriptor* call_descriptor, Graph* graph,
Code::Kind kind, const char* debug_name,
const AssemblerOptions& assembler_options,
// Returns a new compilation job for a wasm heap stub.
static std::unique_ptr<OptimizedCompilationJob> NewWasmHeapStubCompilationJob(
Isolate* isolate, CallDescriptor* call_descriptor,
std::unique_ptr<Zone> zone, Graph* graph, Code::Kind kind,
std::unique_ptr<char[]> debug_name, const AssemblerOptions& options,
SourcePositionTable* source_positions = nullptr);
// Run the pipeline on a machine graph and generate code.
......
......@@ -14,6 +14,7 @@
#include "src/codegen/assembler-inl.h"
#include "src/codegen/assembler.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/compiler.h"
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/backend/code-generator.h"
......@@ -4878,28 +4879,6 @@ void WasmGraphBuilder::RemoveBytecodePositionDecorator() {
}
namespace {
bool must_record_function_compilation(Isolate* isolate) {
return isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling();
}
PRINTF_FORMAT(4, 5)
void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
Isolate* isolate, Handle<Code> code,
const char* format, ...) {
DCHECK(must_record_function_compilation(isolate));
ScopedVector<char> buffer(128);
va_list arguments;
va_start(arguments, format);
int len = VSNPrintF(buffer, format, arguments);
CHECK_LT(0, len);
va_end(arguments);
Handle<String> name_str =
isolate->factory()->NewStringFromAsciiChecked(buffer.begin());
PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *name_str));
}
class WasmWrapperGraphBuilder : public WasmGraphBuilder {
public:
WasmWrapperGraphBuilder(Zone* zone, JSGraph* jsgraph, wasm::FunctionSig* sig,
......@@ -5901,27 +5880,25 @@ void AppendSignature(char* buffer, size_t max_name_len,
} // namespace
MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate,
wasm::FunctionSig* sig,
bool is_import) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"CompileJSToWasmWrapper");
std::unique_ptr<OptimizedCompilationJob> NewJSToWasmCompilationJob(
Isolate* isolate, wasm::FunctionSig* sig, bool is_import) {
//----------------------------------------------------------------------------
// Create the Graph.
//----------------------------------------------------------------------------
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
std::unique_ptr<Zone> zone =
base::make_unique<Zone>(isolate->allocator(), ZONE_NAME);
Graph* graph = new (zone.get()) Graph(zone.get());
CommonOperatorBuilder common(zone.get());
MachineOperatorBuilder machine(
&zone, MachineType::PointerRepresentation(),
zone.get(), MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags(),
InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
JSGraph jsgraph(isolate, graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
Node* effect = nullptr;
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, nullptr,
WasmWrapperGraphBuilder builder(zone.get(), &jsgraph, sig, nullptr,
StubCallMode::kCallCodeObject,
wasm::WasmFeaturesFromIsolate(isolate));
builder.set_control_ptr(&control);
......@@ -5929,38 +5906,20 @@ MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate,
builder.BuildJSToWasmWrapper(is_import);
//----------------------------------------------------------------------------
// Run the compilation pipeline.
// Create the compilation job.
//----------------------------------------------------------------------------
static constexpr size_t kMaxNameLen = 128;
char debug_name[kMaxNameLen] = "js_to_wasm:";
AppendSignature(debug_name, kMaxNameLen, sig);
auto debug_name = std::unique_ptr<char[]>(new char[kMaxNameLen]);
memcpy(debug_name.get(), "js_to_wasm:", 12);
AppendSignature(debug_name.get(), kMaxNameLen, sig);
// Schedule and compile to machine code.
int params = static_cast<int>(sig->parameter_count());
CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
&zone, false, params + 1, CallDescriptor::kNoFlags);
MaybeHandle<Code> maybe_code = Pipeline::GenerateCodeForWasmHeapStub(
isolate, incoming, &graph, Code::JS_TO_WASM_FUNCTION, debug_name,
WasmAssemblerOptions());
Handle<Code> code;
if (!maybe_code.ToHandle(&code)) {
return maybe_code;
}
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code) {
CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
OFStream os(tracing_scope.file());
code->Disassemble(debug_name, os);
}
#endif
zone.get(), false, params + 1, CallDescriptor::kNoFlags);
if (must_record_function_compilation(isolate)) {
RecordFunctionCompilation(CodeEventListener::STUB_TAG, isolate, code, "%s",
debug_name);
}
return code;
return Pipeline::NewWasmHeapStubCompilationJob(
isolate, incoming, std::move(zone), graph, Code::JS_TO_WASM_FUNCTION,
std::move(debug_name), WasmAssemblerOptions());
}
WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
......@@ -6328,19 +6287,20 @@ wasm::WasmCompilationResult CompileWasmInterpreterEntry(
}
MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) {
Zone zone(isolate->allocator(), ZONE_NAME);
Graph graph(&zone);
CommonOperatorBuilder common(&zone);
std::unique_ptr<Zone> zone =
base::make_unique<Zone>(isolate->allocator(), ZONE_NAME);
Graph* graph = new (zone.get()) Graph(zone.get());
CommonOperatorBuilder common(zone.get());
MachineOperatorBuilder machine(
&zone, MachineType::PointerRepresentation(),
zone.get(), MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags(),
InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
JSGraph jsgraph(isolate, graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr;
Node* effect = nullptr;
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, nullptr,
WasmWrapperGraphBuilder builder(zone.get(), &jsgraph, sig, nullptr,
StubCallMode::kCallCodeObject,
wasm::WasmFeaturesFromIsolate(isolate));
builder.set_control_ptr(&control);
......@@ -6349,28 +6309,27 @@ MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) {
// Schedule and compile to machine code.
CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
&zone, false, CWasmEntryParameters::kNumParameters + 1,
zone.get(), false, CWasmEntryParameters::kNumParameters + 1,
CallDescriptor::kNoFlags);
// Build a name in the form "c-wasm-entry:<params>:<returns>".
static constexpr size_t kMaxNameLen = 128;
char debug_name[kMaxNameLen] = "c-wasm-entry:";
AppendSignature(debug_name, kMaxNameLen, sig);
MaybeHandle<Code> maybe_code = Pipeline::GenerateCodeForWasmHeapStub(
isolate, incoming, &graph, Code::C_WASM_ENTRY, debug_name,
AssemblerOptions::Default(isolate));
Handle<Code> code;
if (!maybe_code.ToHandle(&code)) {
return maybe_code;
}
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_opt_code) {
CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
OFStream os(tracing_scope.file());
code->Disassemble(debug_name, os);
}
#endif
auto debug_name = std::unique_ptr<char[]>(new char[kMaxNameLen]);
memcpy(debug_name.get(), "c-wasm-entry:", 14);
AppendSignature(debug_name.get(), kMaxNameLen, sig);
// Run the compilation job synchronously.
std::unique_ptr<OptimizedCompilationJob> job(
Pipeline::NewWasmHeapStubCompilationJob(
isolate, incoming, std::move(zone), graph, Code::C_WASM_ENTRY,
std::move(debug_name), AssemblerOptions::Default(isolate)));
if (job->PrepareJob(isolate) == CompilationJob::FAILED ||
job->ExecuteJob() == CompilationJob::FAILED ||
job->FinalizeJob(isolate) == CompilationJob::FAILED) {
return {};
}
Handle<Code> code = job->compilation_info()->code();
return code;
}
......
......@@ -20,6 +20,7 @@
namespace v8 {
namespace internal {
struct AssemblerOptions;
class OptimizedCompilationJob;
namespace compiler {
// Forward declarations for some compiler data structures.
......@@ -123,11 +124,9 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine*,
wasm::NativeModule*,
wasm::FunctionSig*, Address address);
// Creates a code object calling a wasm function with the given signature,
// callable from JS.
V8_EXPORT_PRIVATE MaybeHandle<Code> CompileJSToWasmWrapper(Isolate*,
wasm::FunctionSig*,
bool is_import);
// Returns an OptimizedCompilationJob object for a JS to Wasm wrapper.
std::unique_ptr<OptimizedCompilationJob> NewJSToWasmCompilationJob(
Isolate* isolate, wasm::FunctionSig* sig, bool is_import);
// Compiles a stub that redirects a call to a wasm function to the wasm
// interpreter. It's ABI compatible with the compiled wasm function.
......
......@@ -4,9 +4,14 @@
#include "src/wasm/function-compiler.h"
#include "src/codegen/compiler.h"
#include "src/codegen/macro-assembler-inl.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/wasm-compiler.h"
#include "src/diagnostics/code-tracer.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/utils/ostreams.h"
#include "src/wasm/baseline/liftoff-compiler.h"
#include "src/wasm/wasm-code-manager.h"
......@@ -206,6 +211,30 @@ WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation(
return result;
}
namespace {
bool must_record_function_compilation(Isolate* isolate) {
return isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling();
}
PRINTF_FORMAT(3, 4)
void RecordWasmHeapStubCompilation(Isolate* isolate, Handle<Code> code,
const char* format, ...) {
DCHECK(must_record_function_compilation(isolate));
ScopedVector<char> buffer(128);
va_list arguments;
va_start(arguments, format);
int len = VSNPrintF(buffer, format, arguments);
CHECK_LT(0, len);
va_end(arguments);
Handle<String> name_str =
isolate->factory()->NewStringFromAsciiChecked(buffer.begin());
PROFILE(isolate, CodeCreateEvent(CodeEventListener::STUB_TAG,
AbstractCode::cast(*code), *name_str));
}
} // namespace
// static
void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
NativeModule* native_module,
......@@ -233,6 +262,46 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
}
}
JSToWasmWrapperCompilationUnit::JSToWasmWrapperCompilationUnit(Isolate* isolate,
FunctionSig* sig,
bool is_import)
: job_(compiler::NewJSToWasmCompilationJob(isolate, sig, is_import)) {}
JSToWasmWrapperCompilationUnit::~JSToWasmWrapperCompilationUnit() = default;
void JSToWasmWrapperCompilationUnit::Prepare(Isolate* isolate) {
CompilationJob::Status status = job_->PrepareJob(isolate);
CHECK_EQ(status, CompilationJob::SUCCEEDED);
}
void JSToWasmWrapperCompilationUnit::Execute() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "CompileJSToWasmWrapper");
DCHECK_EQ(job_->state(), CompilationJob::State::kReadyToExecute);
CompilationJob::Status status = job_->ExecuteJob();
CHECK_EQ(status, CompilationJob::SUCCEEDED);
}
Handle<Code> JSToWasmWrapperCompilationUnit::Finalize(Isolate* isolate) {
CompilationJob::Status status = job_->FinalizeJob(isolate);
CHECK_EQ(status, CompilationJob::SUCCEEDED);
Handle<Code> code = job_->compilation_info()->code();
if (must_record_function_compilation(isolate)) {
RecordWasmHeapStubCompilation(
isolate, code, "%s", job_->compilation_info()->GetDebugName().get());
}
return code;
}
// static
Handle<Code> JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
Isolate* isolate, FunctionSig* sig, bool is_import) {
// Run the compilation unit synchronously.
JSToWasmWrapperCompilationUnit unit(isolate, sig, is_import);
unit.Prepare(isolate);
unit.Execute();
return unit.Finalize(isolate);
}
} // namespace wasm
} // namespace internal
} // namespace v8
......@@ -18,6 +18,7 @@ namespace internal {
class AssemblerBuffer;
class Counters;
class OptimizedCompilationJob;
namespace wasm {
......@@ -101,6 +102,24 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final {
ASSERT_TRIVIALLY_COPYABLE(WasmCompilationUnit);
STATIC_ASSERT(sizeof(WasmCompilationUnit) <= 2 * kSystemPointerSize);
class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final {
public:
JSToWasmWrapperCompilationUnit(Isolate* isolate, FunctionSig* sig,
bool is_import);
~JSToWasmWrapperCompilationUnit();
void Prepare(Isolate* isolate);
void Execute();
Handle<Code> Finalize(Isolate* isolate);
// Run a compilation unit synchronously.
static Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, FunctionSig* sig,
bool is_import);
private:
std::unique_ptr<OptimizedCompilationJob> job_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_WASM_JS_TO_WASM_WRAPPER_CACHE_H_
#define V8_WASM_JS_TO_WASM_WRAPPER_CACHE_H_
#include "src/compiler/wasm-compiler.h"
#include "src/logging/counters.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-code-manager.h"
namespace v8 {
namespace internal {
namespace wasm {
class JSToWasmWrapperCache {
public:
Handle<Code> GetOrCompileJSToWasmWrapper(Isolate* isolate, FunctionSig* sig,
bool is_import) {
std::pair<bool, FunctionSig> key(is_import, *sig);
Handle<Code>& cached = cache_[key];
if (cached.is_null()) {
cached = compiler::CompileJSToWasmWrapper(isolate, sig, is_import)
.ToHandleChecked();
}
return cached;
}
private:
// We generate different code for calling imports than calling wasm functions
// in this module. Both are cached separately.
using CacheKey = std::pair<bool, FunctionSig>;
std::unordered_map<CacheKey, Handle<Code>, base::hash<CacheKey>> cache_;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_JS_TO_WASM_WRAPPER_CACHE_H_
......@@ -23,7 +23,6 @@
#include "src/tracing/trace-event.h"
#include "src/trap-handler/trap-handler.h"
#include "src/utils/identity-map.h"
#include "src/wasm/js-to-wasm-wrapper-cache.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-code-manager.h"
......@@ -2360,24 +2359,83 @@ void CompilationStateImpl::SetError() {
callbacks_.clear();
}
namespace {
using JSToWasmWrapperKey = std::pair<bool, FunctionSig>;
using JSToWasmWrapperQueue =
WrapperQueue<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>>;
using JSToWasmWrapperUnitMap =
std::unordered_map<JSToWasmWrapperKey,
std::unique_ptr<JSToWasmWrapperCompilationUnit>,
base::hash<JSToWasmWrapperKey>>;
class CompileJSToWasmWrapperTask final : public CancelableTask {
public:
CompileJSToWasmWrapperTask(CancelableTaskManager* task_manager,
JSToWasmWrapperQueue* queue,
JSToWasmWrapperUnitMap* compilation_units)
: CancelableTask(task_manager),
queue_(queue),
compilation_units_(compilation_units) {}
void RunInternal() override {
while (base::Optional<JSToWasmWrapperKey> key = queue_->pop()) {
JSToWasmWrapperCompilationUnit* unit = (*compilation_units_)[*key].get();
unit->Execute();
}
}
private:
JSToWasmWrapperQueue* const queue_;
JSToWasmWrapperUnitMap* const compilation_units_;
};
} // namespace
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
Handle<FixedArray> export_wrappers) {
JSToWasmWrapperCache js_to_wasm_cache;
JSToWasmWrapperQueue queue;
JSToWasmWrapperUnitMap compilation_units;
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
// optimization we keep the code space unlocked to avoid repeated unlocking
// because many such wrapper are allocated in sequence below.
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
// Prepare compilation units in the main thread.
for (auto exp : module->export_table) {
if (exp.kind != kExternalFunction) continue;
auto& function = module->functions[exp.index];
Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper(
isolate, function.sig, function.imported);
int wrapper_index =
GetExportWrapperIndex(module, function.sig, function.imported);
JSToWasmWrapperKey key(function.imported, *function.sig);
if (queue.insert(key)) {
auto unit = base::make_unique<JSToWasmWrapperCompilationUnit>(
isolate, function.sig, function.imported);
unit->Prepare(isolate);
compilation_units.emplace(key, std::move(unit));
}
}
// Execute compilation jobs in the background.
CancelableTaskManager task_manager;
const int max_background_tasks = GetMaxBackgroundTasks();
for (int i = 0; i < max_background_tasks; ++i) {
auto task = base::make_unique<CompileJSToWasmWrapperTask>(
&task_manager, &queue, &compilation_units);
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
}
export_wrappers->set(wrapper_index, *wrapper_code);
RecordStats(*wrapper_code, isolate->counters());
// Work in the main thread too.
while (base::Optional<JSToWasmWrapperKey> key = queue.pop()) {
JSToWasmWrapperCompilationUnit* unit = compilation_units[*key].get();
unit->Execute();
}
task_manager.CancelAndWait();
// Finalize compilation jobs in the main thread.
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
// optimization we keep the code space unlocked to avoid repeated unlocking
// because many such wrapper are allocated in sequence below.
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
for (auto& pair : compilation_units) {
JSToWasmWrapperKey key = pair.first;
JSToWasmWrapperCompilationUnit* unit = pair.second.get();
Handle<Code> code = unit->Finalize(isolate);
int wrapper_index = GetExportWrapperIndex(module, &key.second, key.first);
export_wrappers->set(wrapper_index, *code);
RecordStats(*code, isolate->counters());
}
}
......
......@@ -9,6 +9,7 @@
#include <functional>
#include <memory>
#include "src/base/optional.h"
#include "src/common/globals.h"
#include "src/tasks/cancelable-task.h"
#include "src/wasm/compilation-environment.h"
......@@ -67,6 +68,33 @@ bool CompileLazy(Isolate*, NativeModule*, int func_index);
int GetMaxBackgroundTasks();
template <typename Key, typename Hash>
class WrapperQueue {
public:
// Removes an arbitrary key from the queue and returns it.
// If the queue is empty, returns nullopt.
// Thread-safe.
base::Optional<Key> pop() {
base::Optional<Key> key = base::nullopt;
base::LockGuard<base::Mutex> lock(&mutex_);
auto it = queue_.begin();
if (it != queue_.end()) {
key = *it;
queue_.erase(it);
}
return key;
}
// Add the given key to the queue and returns true iff the insert was
// successful.
// Not thread-safe.
bool insert(const Key& key) { return queue_.insert(key).second; }
private:
base::Mutex mutex_;
std::unordered_set<Key, Hash> queue_;
};
// Encapsulates all the state and steps of an asynchronous compilation.
// An asynchronous compile job consists of a number of tasks that are executed
// as foreground and background tasks. Any phase that touches the V8 heap or
......
......@@ -48,35 +48,8 @@ uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
}
}
// Queue of import wrapper keys to compile for an instance.
class ImportWrapperQueue {
public:
// Removes an arbitrary cache key from the queue and returns it.
// If the queue is empty, returns nullopt.
// Thread-safe.
base::Optional<WasmImportWrapperCache::CacheKey> pop() {
base::Optional<WasmImportWrapperCache::CacheKey> key = base::nullopt;
base::LockGuard<base::Mutex> lock(&mutex_);
auto it = queue_.begin();
if (it != queue_.end()) {
key = *it;
queue_.erase(it);
}
return key;
}
// Add the given key to the queue.
// Not thread-safe.
void insert(const WasmImportWrapperCache::CacheKey& key) {
queue_.insert(key);
}
private:
base::Mutex mutex_;
std::unordered_set<WasmImportWrapperCache::CacheKey,
WasmImportWrapperCache::CacheKeyHash>
queue_;
};
using ImportWrapperQueue = WrapperQueue<WasmImportWrapperCache::CacheKey,
WasmImportWrapperCache::CacheKeyHash>;
class CompileImportWrapperTask final : public CancelableTask {
public:
......@@ -550,9 +523,9 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (module_->start_function_index >= 0) {
int start_index = module_->start_function_index;
auto& function = module_->functions[start_index];
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
isolate_, function.sig, function.imported)
.ToHandleChecked();
Handle<Code> wrapper_code =
JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
isolate_, function.sig, function.imported);
// TODO(clemensh): Don't generate an exported function for the start
// function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New(
......
......@@ -1876,9 +1876,8 @@ WasmInstanceObject::GetOrCreateWasmExportedFunction(
// The wrapper may not exist yet if no function in the exports section has
// this signature. We compile it and store the wrapper in the module for
// later use.
wrapper = compiler::CompileJSToWasmWrapper(isolate, function.sig,
function.imported)
.ToHandleChecked();
wrapper = wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
isolate, function.sig, function.imported);
module_object->export_wrappers().set(wrapper_index, *wrapper);
}
result = WasmExportedFunction::New(
......
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