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") { ...@@ -2862,7 +2862,6 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/function-compiler.h", "src/wasm/function-compiler.h",
"src/wasm/graph-builder-interface.cc", "src/wasm/graph-builder-interface.cc",
"src/wasm/graph-builder-interface.h", "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.cc",
"src/wasm/jump-table-assembler.h", "src/wasm/jump-table-assembler.h",
"src/wasm/leb-helper.h", "src/wasm/leb-helper.h",
......
...@@ -1042,6 +1042,119 @@ void PipelineCompilationJob::RegisterWeakObjectsInOptimizedCode( ...@@ -1042,6 +1042,119 @@ void PipelineCompilationJob::RegisterWeakObjectsInOptimizedCode(
code->set_can_have_weak_objects(true); 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> template <typename Phase, typename... Args>
void PipelineImpl::Run(Args&&... args) { void PipelineImpl::Run(Args&&... args) {
PipelineRunScope scope(this->data_, Phase::phase_name()); PipelineRunScope scope(this->data_, Phase::phase_name());
...@@ -2360,58 +2473,6 @@ wasm::WasmCompilationResult Pipeline::GenerateCodeForWasmNativeStub( ...@@ -2360,58 +2473,6 @@ wasm::WasmCompilationResult Pipeline::GenerateCodeForWasmNativeStub(
return result; 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 // static
MaybeHandle<Code> Pipeline::GenerateCodeForTesting( MaybeHandle<Code> Pipeline::GenerateCodeForTesting(
OptimizedCompilationInfo* info, Isolate* isolate, OptimizedCompilationInfo* info, Isolate* isolate,
......
...@@ -59,11 +59,11 @@ class Pipeline : public AllStatic { ...@@ -59,11 +59,11 @@ class Pipeline : public AllStatic {
const char* debug_name, const AssemblerOptions& assembler_options, const char* debug_name, const AssemblerOptions& assembler_options,
SourcePositionTable* source_positions = nullptr); SourcePositionTable* source_positions = nullptr);
// Run the pipeline on a machine graph and generate code. // Returns a new compilation job for a wasm heap stub.
static MaybeHandle<Code> GenerateCodeForWasmHeapStub( static std::unique_ptr<OptimizedCompilationJob> NewWasmHeapStubCompilationJob(
Isolate* isolate, CallDescriptor* call_descriptor, Graph* graph, Isolate* isolate, CallDescriptor* call_descriptor,
Code::Kind kind, const char* debug_name, std::unique_ptr<Zone> zone, Graph* graph, Code::Kind kind,
const AssemblerOptions& assembler_options, std::unique_ptr<char[]> debug_name, const AssemblerOptions& options,
SourcePositionTable* source_positions = nullptr); SourcePositionTable* source_positions = nullptr);
// Run the pipeline on a machine graph and generate code. // Run the pipeline on a machine graph and generate code.
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "src/codegen/assembler-inl.h" #include "src/codegen/assembler-inl.h"
#include "src/codegen/assembler.h" #include "src/codegen/assembler.h"
#include "src/codegen/code-factory.h" #include "src/codegen/code-factory.h"
#include "src/codegen/compiler.h"
#include "src/codegen/interface-descriptors.h" #include "src/codegen/interface-descriptors.h"
#include "src/codegen/optimized-compilation-info.h" #include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/backend/code-generator.h" #include "src/compiler/backend/code-generator.h"
...@@ -4878,28 +4879,6 @@ void WasmGraphBuilder::RemoveBytecodePositionDecorator() { ...@@ -4878,28 +4879,6 @@ void WasmGraphBuilder::RemoveBytecodePositionDecorator() {
} }
namespace { 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 { class WasmWrapperGraphBuilder : public WasmGraphBuilder {
public: public:
WasmWrapperGraphBuilder(Zone* zone, JSGraph* jsgraph, wasm::FunctionSig* sig, WasmWrapperGraphBuilder(Zone* zone, JSGraph* jsgraph, wasm::FunctionSig* sig,
...@@ -5901,27 +5880,25 @@ void AppendSignature(char* buffer, size_t max_name_len, ...@@ -5901,27 +5880,25 @@ void AppendSignature(char* buffer, size_t max_name_len,
} // namespace } // namespace
MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate, std::unique_ptr<OptimizedCompilationJob> NewJSToWasmCompilationJob(
wasm::FunctionSig* sig, Isolate* isolate, wasm::FunctionSig* sig, bool is_import) {
bool is_import) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"CompileJSToWasmWrapper");
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Create the Graph. // Create the Graph.
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
Zone zone(isolate->allocator(), ZONE_NAME); std::unique_ptr<Zone> zone =
Graph graph(&zone); base::make_unique<Zone>(isolate->allocator(), ZONE_NAME);
CommonOperatorBuilder common(&zone); Graph* graph = new (zone.get()) Graph(zone.get());
CommonOperatorBuilder common(zone.get());
MachineOperatorBuilder machine( MachineOperatorBuilder machine(
&zone, MachineType::PointerRepresentation(), zone.get(), MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags(), InstructionSelector::SupportedMachineOperatorFlags(),
InstructionSelector::AlignmentRequirements()); InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); JSGraph jsgraph(isolate, graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr; Node* control = nullptr;
Node* effect = nullptr; Node* effect = nullptr;
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, nullptr, WasmWrapperGraphBuilder builder(zone.get(), &jsgraph, sig, nullptr,
StubCallMode::kCallCodeObject, StubCallMode::kCallCodeObject,
wasm::WasmFeaturesFromIsolate(isolate)); wasm::WasmFeaturesFromIsolate(isolate));
builder.set_control_ptr(&control); builder.set_control_ptr(&control);
...@@ -5929,38 +5906,20 @@ MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate, ...@@ -5929,38 +5906,20 @@ MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate,
builder.BuildJSToWasmWrapper(is_import); builder.BuildJSToWasmWrapper(is_import);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Run the compilation pipeline. // Create the compilation job.
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
static constexpr size_t kMaxNameLen = 128; static constexpr size_t kMaxNameLen = 128;
char debug_name[kMaxNameLen] = "js_to_wasm:"; auto debug_name = std::unique_ptr<char[]>(new char[kMaxNameLen]);
AppendSignature(debug_name, kMaxNameLen, sig); 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()); int params = static_cast<int>(sig->parameter_count());
CallDescriptor* incoming = Linkage::GetJSCallDescriptor( CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
&zone, false, params + 1, CallDescriptor::kNoFlags); zone.get(), 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
if (must_record_function_compilation(isolate)) { return Pipeline::NewWasmHeapStubCompilationJob(
RecordFunctionCompilation(CodeEventListener::STUB_TAG, isolate, code, "%s", isolate, incoming, std::move(zone), graph, Code::JS_TO_WASM_FUNCTION,
debug_name); std::move(debug_name), WasmAssemblerOptions());
}
return code;
} }
WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target, WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
...@@ -6328,19 +6287,20 @@ wasm::WasmCompilationResult CompileWasmInterpreterEntry( ...@@ -6328,19 +6287,20 @@ wasm::WasmCompilationResult CompileWasmInterpreterEntry(
} }
MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) { MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) {
Zone zone(isolate->allocator(), ZONE_NAME); std::unique_ptr<Zone> zone =
Graph graph(&zone); base::make_unique<Zone>(isolate->allocator(), ZONE_NAME);
CommonOperatorBuilder common(&zone); Graph* graph = new (zone.get()) Graph(zone.get());
CommonOperatorBuilder common(zone.get());
MachineOperatorBuilder machine( MachineOperatorBuilder machine(
&zone, MachineType::PointerRepresentation(), zone.get(), MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags(), InstructionSelector::SupportedMachineOperatorFlags(),
InstructionSelector::AlignmentRequirements()); InstructionSelector::AlignmentRequirements());
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); JSGraph jsgraph(isolate, graph, &common, nullptr, nullptr, &machine);
Node* control = nullptr; Node* control = nullptr;
Node* effect = nullptr; Node* effect = nullptr;
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, nullptr, WasmWrapperGraphBuilder builder(zone.get(), &jsgraph, sig, nullptr,
StubCallMode::kCallCodeObject, StubCallMode::kCallCodeObject,
wasm::WasmFeaturesFromIsolate(isolate)); wasm::WasmFeaturesFromIsolate(isolate));
builder.set_control_ptr(&control); builder.set_control_ptr(&control);
...@@ -6349,28 +6309,27 @@ MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) { ...@@ -6349,28 +6309,27 @@ MaybeHandle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig) {
// Schedule and compile to machine code. // Schedule and compile to machine code.
CallDescriptor* incoming = Linkage::GetJSCallDescriptor( CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
&zone, false, CWasmEntryParameters::kNumParameters + 1, zone.get(), false, CWasmEntryParameters::kNumParameters + 1,
CallDescriptor::kNoFlags); CallDescriptor::kNoFlags);
// Build a name in the form "c-wasm-entry:<params>:<returns>". // Build a name in the form "c-wasm-entry:<params>:<returns>".
static constexpr size_t kMaxNameLen = 128; static constexpr size_t kMaxNameLen = 128;
char debug_name[kMaxNameLen] = "c-wasm-entry:"; auto debug_name = std::unique_ptr<char[]>(new char[kMaxNameLen]);
AppendSignature(debug_name, kMaxNameLen, sig); memcpy(debug_name.get(), "c-wasm-entry:", 14);
AppendSignature(debug_name.get(), kMaxNameLen, sig);
MaybeHandle<Code> maybe_code = Pipeline::GenerateCodeForWasmHeapStub(
isolate, incoming, &graph, Code::C_WASM_ENTRY, debug_name, // Run the compilation job synchronously.
AssemblerOptions::Default(isolate)); std::unique_ptr<OptimizedCompilationJob> job(
Handle<Code> code; Pipeline::NewWasmHeapStubCompilationJob(
if (!maybe_code.ToHandle(&code)) { isolate, incoming, std::move(zone), graph, Code::C_WASM_ENTRY,
return maybe_code; std::move(debug_name), AssemblerOptions::Default(isolate)));
}
#ifdef ENABLE_DISASSEMBLER if (job->PrepareJob(isolate) == CompilationJob::FAILED ||
if (FLAG_print_opt_code) { job->ExecuteJob() == CompilationJob::FAILED ||
CodeTracer::Scope tracing_scope(isolate->GetCodeTracer()); job->FinalizeJob(isolate) == CompilationJob::FAILED) {
OFStream os(tracing_scope.file()); return {};
code->Disassemble(debug_name, os); }
} Handle<Code> code = job->compilation_info()->code();
#endif
return code; return code;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
struct AssemblerOptions; struct AssemblerOptions;
class OptimizedCompilationJob;
namespace compiler { namespace compiler {
// Forward declarations for some compiler data structures. // Forward declarations for some compiler data structures.
...@@ -123,11 +124,9 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine*, ...@@ -123,11 +124,9 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine*,
wasm::NativeModule*, wasm::NativeModule*,
wasm::FunctionSig*, Address address); wasm::FunctionSig*, Address address);
// Creates a code object calling a wasm function with the given signature, // Returns an OptimizedCompilationJob object for a JS to Wasm wrapper.
// callable from JS. std::unique_ptr<OptimizedCompilationJob> NewJSToWasmCompilationJob(
V8_EXPORT_PRIVATE MaybeHandle<Code> CompileJSToWasmWrapper(Isolate*, Isolate* isolate, wasm::FunctionSig* sig, bool is_import);
wasm::FunctionSig*,
bool is_import);
// 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.
......
...@@ -4,9 +4,14 @@ ...@@ -4,9 +4,14 @@
#include "src/wasm/function-compiler.h" #include "src/wasm/function-compiler.h"
#include "src/codegen/compiler.h"
#include "src/codegen/macro-assembler-inl.h" #include "src/codegen/macro-assembler-inl.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/wasm-compiler.h" #include "src/compiler/wasm-compiler.h"
#include "src/diagnostics/code-tracer.h"
#include "src/logging/counters.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/baseline/liftoff-compiler.h"
#include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-code-manager.h"
...@@ -206,6 +211,30 @@ WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation( ...@@ -206,6 +211,30 @@ WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation(
return result; 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 // static
void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate, void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
NativeModule* native_module, NativeModule* native_module,
...@@ -233,6 +262,46 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate, ...@@ -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 wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -18,6 +18,7 @@ namespace internal { ...@@ -18,6 +18,7 @@ namespace internal {
class AssemblerBuffer; class AssemblerBuffer;
class Counters; class Counters;
class OptimizedCompilationJob;
namespace wasm { namespace wasm {
...@@ -101,6 +102,24 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final { ...@@ -101,6 +102,24 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final {
ASSERT_TRIVIALLY_COPYABLE(WasmCompilationUnit); ASSERT_TRIVIALLY_COPYABLE(WasmCompilationUnit);
STATIC_ASSERT(sizeof(WasmCompilationUnit) <= 2 * kSystemPointerSize); 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 wasm
} // namespace internal } // namespace internal
} // namespace v8 } // 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 @@ ...@@ -23,7 +23,6 @@
#include "src/tracing/trace-event.h" #include "src/tracing/trace-event.h"
#include "src/trap-handler/trap-handler.h" #include "src/trap-handler/trap-handler.h"
#include "src/utils/identity-map.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/module-decoder.h"
#include "src/wasm/streaming-decoder.h" #include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-code-manager.h"
...@@ -2360,24 +2359,83 @@ void CompilationStateImpl::SetError() { ...@@ -2360,24 +2359,83 @@ void CompilationStateImpl::SetError() {
callbacks_.clear(); 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, void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
Handle<FixedArray> export_wrappers) { 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 // Prepare compilation units in the main thread.
// 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 exp : module->export_table) { for (auto exp : module->export_table) {
if (exp.kind != kExternalFunction) continue; if (exp.kind != kExternalFunction) continue;
auto& function = module->functions[exp.index]; auto& function = module->functions[exp.index];
Handle<Code> wrapper_code = js_to_wasm_cache.GetOrCompileJSToWasmWrapper( JSToWasmWrapperKey key(function.imported, *function.sig);
isolate, function.sig, function.imported); if (queue.insert(key)) {
int wrapper_index = auto unit = base::make_unique<JSToWasmWrapperCompilationUnit>(
GetExportWrapperIndex(module, function.sig, function.imported); 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); // Work in the main thread too.
RecordStats(*wrapper_code, isolate->counters()); 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 @@ ...@@ -9,6 +9,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "src/base/optional.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/tasks/cancelable-task.h" #include "src/tasks/cancelable-task.h"
#include "src/wasm/compilation-environment.h" #include "src/wasm/compilation-environment.h"
...@@ -67,6 +68,33 @@ bool CompileLazy(Isolate*, NativeModule*, int func_index); ...@@ -67,6 +68,33 @@ bool CompileLazy(Isolate*, NativeModule*, int func_index);
int GetMaxBackgroundTasks(); 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. // Encapsulates all the state and steps of an asynchronous compilation.
// An asynchronous compile job consists of a number of tasks that are executed // 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 // as foreground and background tasks. Any phase that touches the V8 heap or
......
...@@ -48,35 +48,8 @@ uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance, ...@@ -48,35 +48,8 @@ uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
} }
} }
// Queue of import wrapper keys to compile for an instance. using ImportWrapperQueue = WrapperQueue<WasmImportWrapperCache::CacheKey,
class ImportWrapperQueue { WasmImportWrapperCache::CacheKeyHash>;
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_;
};
class CompileImportWrapperTask final : public CancelableTask { class CompileImportWrapperTask final : public CancelableTask {
public: public:
...@@ -550,9 +523,9 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -550,9 +523,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;
auto& function = module_->functions[start_index]; auto& function = module_->functions[start_index];
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper( Handle<Code> wrapper_code =
isolate_, function.sig, function.imported) JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
.ToHandleChecked(); isolate_, function.sig, function.imported);
// 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(
......
...@@ -1876,9 +1876,8 @@ WasmInstanceObject::GetOrCreateWasmExportedFunction( ...@@ -1876,9 +1876,8 @@ WasmInstanceObject::GetOrCreateWasmExportedFunction(
// The wrapper may not exist yet if no function in the exports section has // 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 // this signature. We compile it and store the wrapper in the module for
// later use. // later use.
wrapper = compiler::CompileJSToWasmWrapper(isolate, function.sig, wrapper = wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
function.imported) isolate, function.sig, function.imported);
.ToHandleChecked();
module_object->export_wrappers().set(wrapper_index, *wrapper); module_object->export_wrappers().set(wrapper_index, *wrapper);
} }
result = WasmExportedFunction::New( 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