[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: Andreas Haas <ahaas@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#62249}
This commit is contained in:
parent
b5fe1b4b4c
commit
79e8e3ec65
1
BUILD.gn
1
BUILD.gn
@ -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);
|
||||
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)) {
|
||||
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);
|
||||
auto debug_name = std::unique_ptr<char[]>(new char[kMaxNameLen]);
|
||||
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,
|
||||
AssemblerOptions::Default(isolate));
|
||||
Handle<Code> code;
|
||||
if (!maybe_code.ToHandle(&code)) {
|
||||
return maybe_code;
|
||||
// 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 {};
|
||||
}
|
||||
#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
|
||||
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
|
||||
|
@ -1,41 +0,0 @@
|
||||
// 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;
|
||||
|
||||
// Prepare compilation units in the main thread.
|
||||
for (auto exp : module->export_table) {
|
||||
if (exp.kind != kExternalFunction) continue;
|
||||
auto& function = module->functions[exp.index];
|
||||
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));
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
|
||||
export_wrappers->set(wrapper_index, *wrapper_code);
|
||||
RecordStats(*wrapper_code, isolate->counters());
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user